Builtin Library
README.md
surface
Active input-surface classification. Answers "what device class is the
player using right now" — "touch" or "kbm" — and notifies on
change; backs Zin.surface. Virtual touch controls mount whenever the
surface is "touch", in edit and play alike.
The frame rule
Resolution runs once per engine frame from the Zin.tick pipeline: a
per-event observer collects which device classes produced events this
frame, and the frame resolver applies one rule — touch wins a frame
that contains any touch event. The primary touch contact also drives
the mouse path (a projected mouse.down/move/up in the same
frame), so classifying by the latest single event would flip to kbm
on every tap; the frame rule keeps projection-generated mouse events
from masking the touch that caused them.
Capability default
Before any input arrives, the class defaults to "touch" on sessions
with a touch source (touch_capable — latched by the first contact or
declared at boot by Android / the web client) and "kbm" otherwise, so
touch-first devices present touch controls from the first frame.
Exports
M.current() -> string— the active surface class,"touch"or"kbm".M.onChange(fn: (string, string?) -> ()) -> number— subscribe to class flips;fn(now, prev)whereprevis the previously reported class (the first-tick capability default when no event had resolved yet). Returns a handle.M.off(handle: number) -> boolean— unsubscribe anonChangehandle.M._beginFrame()— internal: clear this frame's per-class flags; wired byZin.tick.M._observeEvent(ev: any)— internal: per-event observer; wired byZin.tick.M._resolveFrame()— internal: resolve this frame's class and fireonChangeon a flip; wired byZin.tick.M._reset()— test-only: clear resolution state and subscriptions.
Types:
SurfaceClass = "touch" | "kbm"
Usage
local Surface = require("@builtin::modules.zinput.surface")
if Surface.current() == "touch" then
showTouchOverlay()
end
Surface.onChange(function(now, prev)
print("surface flipped to", now)
end)
Notes
onChangefires from theZin.tickpipeline on the frame the class flips; without a tick, resolution never advances andcurrent()keeps returning the capability default.- The change signal compares against the last reported class, whose initial value is the capability default captured on the first tick of the session — a phone's first contact confirms the default silently, while a desktop touchscreen's first contact reports kbm→touch.
- Handler errors during
onChangedispatch are caught and logged, not propagated.