HOOZiDocs
Skip to content

input ​

Input queries + keybind formatter + clipboard + hardware device output.


input.is_active(path) → bool ​

Parameters: path : string — control full path (hotkey control)

Returns whether this hotkey is currently active (depending on mode: Toggle = toggled on / Hold = currently held / Always = always true).


input.format(path, fn) ​

Attaches dynamic text + color hints to a keybind control. The formatter is only invoked while the hotkey is active (i.e. shown in the "active keybinds" overlay).

Parameters:

  • path : string — menu element full path or Pattern B hotkey ID (both accepted)
  • fn : function(id : string) → string | { text = string, color = {r,g,b,a} }

Automatic path resolution ​

Pass the menu element full path (the natural form) — if that element is a KeybindControl that internally remaps its keybind to a stable ID, resolution follows through to that stable ID.

Call formResolution
input.format("settings.display.menu", fn)→ resolves to hotkey.menu_open
input.format("hotkey.menu_open", fn)→ used directly
input.format("KbHints.g.alarm", fn)→ user-defined KeybindControl with no remap, uses full path

fn return type ​

ReturnBehavior
stringDisplay the text in the theme color
table {text, color={r,g,b,a}}Custom text + color
otherLOG_ERR + falls back to default display

Owner-scoped: automatically unregistered when the script unloads.


input.unformat(path) ​

Path resolution behaves identically to input.format.


input.clipboard_get() → string ​

Reads the system clipboard. Returns an empty string when empty.

input.clipboard_set(text) ​

Writes the system clipboard.

lua
local key = input.clipboard_get()
if key ~= "" then gui.notify:info("read: " .. key) end

input.clipboard_set("hello")

Not provided: input.is_down(vk) — on a dual-machine setup, the OS key API reads the machine running the program, not the game player. To query player input, use game.localplayer.* fields (e.g. is_zooming, is_grenade). See Getting Started.


Formatter examples ​

lua
-- formatter #1: plain string
input.format("KbHints.g.alarm", function(id)
    return alarm_armed and "ARMED" or "OFF"
end)

-- formatter #2: return {text, color} table
input.format("KbHints.g.stopwatch", function(id)
    local sec = os.clock() - start_t
    return {
        text  = string.format("%.1fs", sec),
        color = { 0.55, 0.85, 1.0, 1.0 },
    }
end)

-- formatter #3: dynamic color by threshold
input.format("KbHints.g.counter", function(id)
    local label = string.format("× %d", counter)
    if counter == 0     then return { text = label, color = { 0.6, 0.6, 0.6, 1.0 } } end
    if counter < 10     then return { text = label, color = { 0.3, 1.0, 0.3, 1.0 } } end
    return { text = label, color = { 1.0, 0.45, 0.25, 1.0 } }
end)

Hardware device output ​

Sends real mouse and keyboard input to the game machine through an external input device (KMBOX Net / KMBOX B+ / MAKCU).

With no device connected everything is a no-op returning false — no error is raised, and no alternate path is taken. Check input.device_connected() before relying on any of these.

Relative movement only

input.move is a relative displacement. There is no input.move_to(x, y).

MAKCU firmware does have a moveto(x, y) command (KMBOX Net / B+ do not), but per the official API it "internally calculates the needed x,y movement to reach the requested position on the screen" — the wire format is still a relative report, and it needs a trustworthy notion of where the cursor currently is.

In an FPS that reference point does not exist: the game consumes raw input deltas with the system cursor locked to the window center, so screen coordinates have no correspondence to the in-game view angle. In a dual-machine setup this program cannot read the game machine's cursor position either. moveto is therefore useless for aiming, and the facade does not expose it.

To aim at a world position, use game.aimbot.set_predictor and let the aim pipeline read view_angle and solve for the required delta.


input.backend() → string ​

Name of the current input device backend. Use it to branch on capability — backends support different actions (see the table below).

ValueMeaning
"disable"No device selected
"write_view"Memory takeover (no hardware; everything in this section is inert)
"kmboxnet"KMBOX Net (ethernet)
"kmboxb+"KMBOX B+ (serial)
"makcu"MAKCU (serial)

input.device_connected() → bool ​

Whether a device is connected and usable.

Backend capability matrix ​

Actionkmboxnetkmboxb+makcu
move / click / button (left)✅✅✅
button right / middle / side✅⚠️✅
wheel✅⚠️✅
move_curve (firmware-side interpolation)✅↩︎ falls back to move✅
key_down / key_up / key_press✅✅❌
mask left mouse button✅✅✅
mask other mouse buttons / "x" / "y"✅❌✅
mask "wheel"✅❌❌
mask keyboard keys✅❌❌
  • ⚠️ = these KMBOX B+ command names were extrapolated from the naming pattern of commands that are known to work; they are unverified on real hardware. When the firmware does not recognize a command the serial write still succeeds, so true does not guarantee anything happened.
  • ❌ = the backend lacks the capability and returns false. Do not read every false as "no device" — check this table first.
  • MAKCU firmware has no keyboard HID channel, so the three keyboard functions are always false on it.

input.move(dx, dy) → bool ​

Parameters: dx : int, dy : int — relative displacement (mickeys; positive = right / down)

input.move_curve(dx, dy, ms?) → bool ​

Parameters: dx : int, dy : int, ms : int (optional, default 20, capped at 2000)

Hands a large displacement to the firmware to push out in segments, producing a more hand-like path than small per-frame move calls.

Blocking push

The firmware holds the device for the whole ms window, stalling any aim move queued behind it. Keep it out of per-frame loops.

input.button(name, down) → bool ​

Parameters: name : string, down : bool

nameAliases
"left""lmb"
"right""rmb"
"middle""mmb" / "wheel_click"
"side1""mouse4" / "x1"
"side2""mouse5" / "x2"

input.click(name?) → bool ​

Press + release once. Omitting name means left button.

input.wheel(delta) → bool ​

Parameters: delta : int — detents (positive = scroll forward)


input.key_down(key) / input.key_up(key) / input.key_press(key) → bool ​

Parameters: key : string — a single character ("r") or a key name ("shift" / "enter" / "f1" / "space" / "lctrl", arrow keys such as "up", …)

key_press = press + release once.

The MAKCU backend has no keyboard channel; these three are always false there.


input.mask(what, enable) → bool ​

Blocks the player's physical input from reaching the game. Useful for taking over a key — the box swallows the player's own press, and the script decides when to actually emit it.

Parameters:

  • what : string — a mouse button name (same as input.button) / "x" / "y" / "wheel" / a keyboard key name
  • enable : bool

Precondition for keyboard masking

Keyboard masks only take effect when the physical keyboard is plugged into the box. If the keyboard is connected directly to the game machine the box cannot intercept it, and the call returns true with no actual effect.

All masks are released automatically when the script unloads — see "Automatic cleanup" below.


Automatic cleanup ​

Stateful device operations are tracked per owning script and reverted automatically when the script unloads (hot reload / config switch / manual unload):

OperationOn unload
input.button(name, true) heldReleased
input.key_down(key) heldReleased
input.mask(what, true)Cleared

Without this layer, a script unloaded while holding the left button would leave that button stuck in the box firmware permanently — in game it reads as continuous fire that the user cannot stop by clicking their own mouse.

click / move / wheel / key_press are instantaneous and need no cleanup.


Interaction with the trigger ​

The trigger decides whether to yield based on whether this machine is firing, and a left click sent through the box looks exactly like the player firing manually. input.click("left") and input.button("left", false) notify the trigger that the shot came from a script — scripts do not need to handle this themselves.

While a script holds the left button the trigger yields and does not fire. That is the correct behavior: fire is already sustained, so an extra trigger shot would be meaningless.


Device output examples ​

lua
-- Capability branching: backends differ in what they can do
local backend = input.backend()
if not input.device_connected() then
    gui.notify:warn("no input device connected")
    return
end

-- Trace a square slowly (firmware interpolation looks better than per-frame move)
for _, d in ipairs({ {200,0}, {0,200}, {-200,0}, {0,-200} }) do
    input.move_curve(d[1], d[2], 150)
end

-- Side-button tap: press -> 60ms -> release
input.button("side1", true)
Delay(0.06, function() input.button("side1", false) end)

-- Take over the R key: swallow the player's physical R, emit it on our terms
-- (only works on kmboxnet with the keyboard plugged into the box)
if backend == "kmboxnet" then
    input.mask("r", true)
    event.on("frame_update", function()
        if should_reload_now() then
            input.mask("r", false)
            input.key_press("r")
            input.mask("r", true)
        end
    end)
end