Macros & Keybinds
LiquidBounce features a sophisticated keybind system that allows you to bind keyboard keys and mouse buttons to modules and commands. The system supports multiple bind actions, modifiers, and complex key combinations.
Overview
The keybind system provides:
Flexible Bindings Bind keyboard keys, mouse buttons, and scancodes
Bind Actions Toggle, Hold, or Smart activation modes
Modifiers Support for Shift, Ctrl, Alt, and Super modifiers
Module Integration Every module has a configurable keybind
The core of the keybind system is the InputBind class:
data class InputBind (
val boundKey: InputConstants .Key,
val action: BindAction ,
val modifiers: Set < Modifier >,
)
Components
boundKey : The key or button to bind (keyboard key, mouse button, or scancode)
action : How the bind behaves (Toggle, Hold, or Smart)
modifiers : Required modifier keys (Shift, Ctrl, Alt, Super)
Bind Actions
Three bind action modes are available:
Flips the state when pressed:
Press once: Enable
Press again: Disable
Most common for modules
Enabled only while key is held:
Press: Enable
Release: Disable immediately
Useful for temporary actions
Adaptive behavior based on usage:
Quick tap: Toggle
Hold down: Acts like Hold mode
Best of both worlds
Implementation
From InputBind.kt:181:
fun getNewState (event: KeyboardKeyEvent , currentState: Boolean ): Boolean {
if ( ! matchesKey (event.keyCode, event.scanCode)) {
return currentState
}
val eventAction = event.action
return when (eventAction) {
GLFW.GLFW_PRESS if mc.screen == null -> when (action) {
BindAction.TOGGLE -> ! currentState
BindAction.HOLD, BindAction.SMART -> true
}
GLFW.GLFW_RELEASE -> when (action) {
BindAction.HOLD -> false
BindAction.TOGGLE, BindAction.SMART -> currentState
}
else -> currentState
}
}
Binds only activate when no screen is open (prevents conflict with menus).
Modifiers
Supported modifier keys:
enum class Modifier (
override val tag: String ,
val bitMask: Int ,
vararg val keyCodes: Int
): Tagged {
SHIFT ( "Shift" , GLFW.GLFW_MOD_SHIFT, KEY_LSHIFT, KEY_RSHIFT),
CONTROL ( "Control" , GLFW.GLFW_MOD_CONTROL, KEY_LCONTROL, KEY_RCONTROL),
ALT ( "Alt" , GLFW.GLFW_MOD_ALT, KEY_LALT, KEY_RALT),
SUPER ( "Super" , GLFW.GLFW_MOD_SUPER, KEY_LSUPER, KEY_RSUPER)
}
Modifiers show platform-appropriate symbols:
val platformRenderName: String get () = when (Util. getPlatform ()) {
Util.OS.WINDOWS -> when ( this ) {
CONTROL -> "Ctrl"
SUPER -> "⊞" // Windows key
else -> tag
}
Util.OS.OSX -> when ( this ) {
SHIFT -> "⇧"
CONTROL -> "^"
ALT -> "⌥"
SUPER -> "⌘" // Command key
}
else -> tag
}
Checking Modifiers
Check if required modifiers are active:
fun matchesModifiers (mods: Int ): Boolean {
return this .modifiers. all { it. isActive (mods) }
}
fun Modifier . isActive (modifiers: Int ) =
modifiers and this .bitMask != 0
Creating Bindings
Basic Bind
// Bind to a specific key
val bind = InputBind (
InputConstants.Type.KEYSYM,
GLFW.GLFW_KEY_R,
BindAction.TOGGLE
)
// Bind by name
val bind = InputBind ( "key.keyboard.r" )
Bind with Modifiers
// Ctrl+R
val bind = InputBind (
boundKey = InputConstants.Type.KEYSYM. getOrCreate (GLFW.GLFW_KEY_R),
action = BindAction.TOGGLE,
modifiers = setOf (InputBind.Modifier.CONTROL)
)
// Shift+Alt+F
val bind = InputBind (
boundKey = InputConstants.Type.KEYSYM. getOrCreate (GLFW.GLFW_KEY_F),
action = BindAction.HOLD,
modifiers = setOf (
InputBind.Modifier.SHIFT,
InputBind.Modifier.ALT
)
)
Mouse Bindings
// Right mouse button
val bind = InputBind (
InputConstants.Type.MOUSE,
GLFW.GLFW_MOUSE_BUTTON_RIGHT,
BindAction.TOGGLE
)
// Middle mouse + Ctrl
val bind = InputBind (
boundKey = InputConstants.Type.MOUSE. getOrCreate (GLFW.GLFW_MOUSE_BUTTON_MIDDLE),
action = BindAction.HOLD,
modifiers = setOf (InputBind.Modifier.CONTROL)
)
Module Integration
Every ClientModule has a keybind:
open class ClientModule (
name: String ,
category: ModuleCategory ,
bind: Int = InputConstants.UNKNOWN. value ,
bindAction: BindAction = BindAction.TOGGLE,
state: Boolean = false ,
// ...
) {
internal val bindValue = bind ( "Bind" , InputBind (
InputConstants.Type.KEYSYM,
bind,
bindAction
))
val bind get () = bindValue. get ()
}
Module Keybind Usage
// Access module's bind
val flyBind = ModuleFly.bind
// Modify module's bind
ModuleFly.bindValue. bind ( "key.keyboard.f" )
// Set bind with modifiers
ModuleFly.bindValue. bind (
key = InputConstants.Type.KEYSYM. getOrCreate (GLFW.GLFW_KEY_V),
action = BindAction.HOLD,
modifiers = setOf (InputBind.Modifier.CONTROL)
)
// Unbind
ModuleFly.bindValue. unbind ()
Event Matching
The system matches events to bindings:
Keyboard Events
// Check if key press matches
fun matchesKeyPress (event: KeyboardKeyEvent ): Boolean {
return event.action == GLFW.GLFW_PRESS
&& matchesKey (event.keyCode, event.scanCode)
&& matchesModifiers (event.mods)
}
// Check if release affects this bind
fun matchesKeyRelease (event: KeyboardKeyEvent ): Boolean {
if (event.action != GLFW.GLFW_RELEASE) return false
val keyReleased = matchesKey (event.keyCode, event.scanCode)
val modifierReleased = event.key. toModifierOrNull ()
. let { it in modifiers && ! it !! .isAnyPressed }
return keyReleased || modifierReleased
}
Mouse Events
// Check if mouse press matches
fun matchesMousePress (event: MouseButtonEvent ): Boolean {
return event.action == GLFW.GLFW_PRESS
&& matchesMouse (event.button)
&& matchesModifiers (event.mods)
}
// Check if release affects this bind
fun matchesMouseRelease (event: MouseButtonEvent ): Boolean {
if (event.action != GLFW.GLFW_RELEASE) return false
val buttonReleased = matchesMouse (event.button)
val modifierReleased = event.key. toModifierOrNull ()
. let { it in modifiers && ! it !! .isAnyPressed }
return buttonReleased || modifierReleased
}
Releasing a required modifier also deactivates the bind in Hold mode.
Helper Functions
Bind Management
// Bind to a named key
fun Value < InputBind > . bind (name: String ) =
set ( get (). copy (boundKey = inputByName (name)))
// Bind with full configuration
fun Value < InputBind > . bind (
key: InputConstants .Key,
action: BindAction ,
modifiers: Set < Modifier >
) = set ( get (). copy (
boundKey = key,
action = action,
modifiers = modifiers
))
// Unbind
fun Value < InputBind > . unbind () = set (InputBind.UNBOUND)
Rendering
Generate display text for a bind:
fun InputBind . renderText (): Component = buildList {
// Main key
add ( variable (boundKey.displayName). bold ())
// Modifiers
if (modifiers. isNotEmpty ()) {
modifiers. forEach {
add ( regular ( " + " ))
add ( variable (it.platformRenderName))
}
}
// Action type
add ( regular ( " (" ))
add ( variable (action.tag))
add ( regular ( ")" ))
}. asText ()
Output examples:
R (Toggle)
Ctrl + V (Hold)
Shift + Alt + F (Smart)
Special Keys
Unbound Key
companion object {
@JvmField
val UNBOUND = InputBind (
InputConstants.UNKNOWN,
BindAction.TOGGLE,
emptySet ()
)
}
val isUnbound: Boolean
get () = this .boundKey == InputConstants.UNKNOWN
Key Names
val keyName: String
get () = when {
isUnbound -> "None"
else -> this .boundKey.name
. split ( '.' )
. drop ( 2 ) // Drop "key.keyboard" or "key.mouse"
. joinToString (separator = "_" )
. uppercase ()
}
Examples:
key.keyboard.r → R
key.mouse.left → LEFT
key.keyboard.left.shift → LEFT_SHIFT
Configuration
Binds are stored in module configurations:
{
"Fly" : {
"Bind" : {
"key" : "key.keyboard.f" ,
"action" : "Toggle" ,
"modifiers" : []
}
},
"Sprint" : {
"Bind" : {
"key" : "key.keyboard.v" ,
"action" : "Hold" ,
"modifiers" : [ "Control" ]
}
}
}
Code References
InputBind.kt Complete keybind system - line 51
utils/input/InputBind.kt (339 lines)
ClientModule.kt Module bind integration - line 77
features/module/ClientModule.kt
Best Practices
Choose Appropriate Actions
Use Toggle for permanent states, Hold for temporary actions, Smart for flexibility
Avoid Conflicts
Check for existing binds before assigning to prevent conflicts
Use Modifiers Wisely
Modifiers allow more binds without conflicts, but too many make them hard to use
Consider Vanilla Binds
Avoid overriding important vanilla Minecraft keybinds
Platform Compatibility
Test binds on different platforms as some keys may not be available
Document Custom Binds
If creating macros, document the key combinations for users