Package github.com/matjam/bunyip/input
input
Package input reports the state of the keyboard, mouse and gamepads, and names the keys, buttons and axes the platform layers fill that state with.
State holds every key, button, stick and the pointer. Level queries (KeyDown, MouseDown, Gamepad.Down) report what is held now. Edge queries (KeyPressed, KeyReleased and their mouse and gamepad equivalents) report what changed since the last update, and the engine clears them after each one, so a press is reported exactly once however many updates or frames it spans. During Draw the edges cover the whole drawn frame instead, so an immediate-mode interface built in Draw sees every change. KeyHeld, KeysDown, KeyRepeated and MouseDoubleClicked cover charged shots, combos, scrolling menus and double clicks. Chars and Composition carry typed text with the keyboard layout and input method applied.
Keys are named by physical position, so KeyW is the key in W's place on a US keyboard whatever it prints, which is what movement bindings need. Key.String names physical positions; the engine's Context.KeyboardLayout supplies native binding labels and physical/logical lookup snapshots. Actions maps named actions to any keys, buttons and axes, with dead zones, rebinding through Listen and JSON bindings for a settings file, so game code asks for "jump" and supports a gamepad without a second set of checks.
Package input reports the state of the keyboard, mouse and gamepads: what is held, what changed this update, where the pointer is and what text was typed, in view units. Read Context.Input on the game loop goroutine. During Update, edge accessors cover events since the preceding update; during Draw, keyboard and mouse edges, text and motion cover the drawn frame. These views are cleared independently, so drawing does not consume input pending for Update. Repeated OS key events count as presses.
Index
- Constants
- type Action
- type Actions
func NewActions() *Actionsfunc (a *Actions) Action(name string) Actionfunc (a *Actions) Bind(action string, sources ...Source)func (a *Actions) Bindings(action string) []Sourcefunc (a *Actions) Down(s *State, action string) boolfunc (a *Actions) Listen(s *State) (Source, bool)func (a *Actions) MarshalJSON() ([]byte, error)func (a *Actions) Names() []stringfunc (a *Actions) Pressed(s *State, action string) boolfunc (a *Actions) Rebind(action string, sources ...Source)func (a *Actions) Released(s *State, action string) boolfunc (a *Actions) Unbind(action string)func (a *Actions) UnmarshalJSON(b []byte) errorfunc (a *Actions) Value(s *State, action string) float32
- type Gamepad
- type GamepadAxis
- type GamepadButton
- type GamepadInfo
- type Key
- type KeyDescription
- type KeySymbol
- type KeyboardLayout
- type Mods
- type MouseButton
- type Source
func KeySource(k Key) Sourcefunc MouseSource(b MouseButton) Sourcefunc PadAxis(a GamepadAxis) Sourcefunc PadButton(b GamepadButton) Sourcefunc ParseSource(text string) (Source, error)func (s Source) MarshalText() ([]byte, error)func (s Source) Neg() Sourcefunc (s Source) String() stringfunc (s *Source) UnmarshalText(b []byte) error
- type SourceKind
- type State
func (s *State) AppendKeysDown(dst []Key) []Keyfunc (s *State) Chars() []runefunc (s *State) Composition() stringfunc (s *State) Gamepad(i int) *Gamepadfunc (s *State) KeyDown(k Key) boolfunc (s *State) KeyHeld(k Key) float32func (s *State) KeyPressed(k Key) boolfunc (s *State) KeyReleased(k Key) boolfunc (s *State) KeyRepeated(k Key) boolfunc (s *State) KeysDown() []Keyfunc (s *State) Mods() Modsfunc (s *State) Mouse() (x, y float32)func (s *State) MouseDelta() (dx, dy float32)func (s *State) MouseDoubleClicked(b MouseButton) boolfunc (s *State) MouseDown(b MouseButton) boolfunc (s *State) MousePos() lin.Vec2func (s *State) MousePressed(b MouseButton) boolfunc (s *State) MouseReleased(b MouseButton) boolfunc (s *State) Scroll() (dx, dy float32)func (s *State) SetStep(seconds float32)
Constants
const (
DoubleClickTime = 0.4
DoubleClickDistance = 6
)
DoubleClickTime is how close two presses must be to count as a double click, in seconds, and DoubleClickDistance how close in view units.
Types
type Action source
type Action struct {
// contains filtered or unexported fields
}
Action is a resolved handle to one named action. Looking the name up once at startup and keeping the handle spares the string hash on every query, which a game makes several times per action per frame. The zero Action is bound to nothing and reads as off.
A handle stays valid across Bind, Rebind and Unbind: it names the action, not the sources behind it.
Bindings source
func (h Action) Bindings() []Source
Bindings returns the handle's sources, as Actions.Bindings does.
Down source
func (h Action) Down(s *State) bool
Down reports whether the handle's action is on, as Actions.Down does.
Name source
func (h Action) Name() string
Name is the action the handle was resolved from, empty for the zero Action.
Pressed source
func (h Action) Pressed(s *State) bool
Pressed reports whether the handle's action went on since the last update, as Actions.Pressed does.
type Actions source
type Actions struct {
// Pad is which gamepad the pad sources read; the first by default.
Pad int
// DeadZone is the stick travel ignored around centre; zero means 0.2.
DeadZone float32
// contains filtered or unexported fields
}
Actions maps named actions ("jump", "fire", "move_x") to the keys, buttons and axes that trigger them, so game code asks for actions and a settings screen rebinds them. An action bound to several sources fires from any; an axis action sums its sources (the D key and the A key with Neg, plus a stick) and clamps to -1..1. Bindings save and load as JSON, and Listen captures the next input for rebinding. Use Actions on the game loop goroutine; concurrent binding changes and queries require external synchronization.
Action source
func (a *Actions) Action(name string) Action
Action resolves a name to a handle, which Value, Down, Pressed and Released read without hashing the name again. Resolve the handles a game uses once, in Init, and keep them:
g.jump = actions.Action("jump")
if g.jump.Pressed(ctx.Input) { ... }
The name need not be bound yet; binding it later fills the same handle.
Bind source
func (a *Actions) Bind(action string, sources ...Source)
Bind adds sources to an action, keeping any it has.
Bindings source
func (a *Actions) Bindings(action string) []Source
Bindings returns an action's sources, for showing in a settings screen or a prompt ("press [Space]").
Down source
func (a *Actions) Down(s *State, action string) bool
Down reports whether the action is on: any bound key or button held, or a bound axis past half.
Listen source
func (a *Actions) Listen(s *State) (Source, bool)
Listen returns the first input that went on this update, for a rebinding screen: press a key, click a button, push a stick past half. Escape is never captured, so a screen can cancel with it.
MarshalJSON source
func (a *Actions) MarshalJSON() ([]byte, error)
MarshalJSON writes the bindings as {"action": ["key:Space", ...]}.
Pressed source
func (a *Actions) Pressed(s *State, action string) bool
Pressed reports whether any of the action's sources went on since the last update: a key or button press, or an axis crossing half.
Rebind source
func (a *Actions) Rebind(action string, sources ...Source)
Rebind replaces an action's sources.
Released source
func (a *Actions) Released(s *State, action string) bool
Released reports whether any of the action's sources went off since the last update.
Unbind source
func (a *Actions) Unbind(action string)
Unbind removes an action's sources. Handles taken for the name stay valid and read as off until it is bound again.
UnmarshalJSON source
func (a *Actions) UnmarshalJSON(b []byte) error
UnmarshalJSON reads what MarshalJSON wrote, replacing the bindings. Handles taken with Action keep pointing at their names, so loading a player's saved bindings does not invalidate them.
Value source
func (a *Actions) Value(s *State, action string) float32
Value is the action's value now: 1 while a bound key or button is held, a stick's travel for an axis, the sum over sources clamped to -1..1. An axis source contributes its positive side; bind its Neg for the other, so "move_x" is (D, A.Neg, LeftX, LeftX.Neg).
This looks the name up on every call. A game querying the same actions every frame resolves them once with Action and asks the handle.
type Gamepad source
type Gamepad struct {
Info GamepadInfo // native metadata and mapped capabilities; empty when disconnected
Connected bool // controller is currently present
Name string // device-provided display name
Buttons [GamepadButtonCount]bool // held buttons
Axes [GamepadAxisCount]float32 // raw axes before Axis applies its dead zone
// contains filtered or unexported fields
}
Gamepad is one controller's current state.
Axis source
func (g *Gamepad) Axis(a GamepadAxis) float32
Axis returns an analogue value, replacing values strictly between -0.08 and 0.08 with zero; values outside that range are not rescaled.
JustConnected source
func (g *Gamepad) JustConnected() bool
JustConnected reports whether the controller appeared since the last update, for a "player 2 joined" prompt.
JustDisconnected source
func (g *Gamepad) JustDisconnected() bool
JustDisconnected reports whether the controller went away since the last update, so a game can pause.
type GamepadAxis source
type GamepadAxis uint8
GamepadAxis indexes the analogue inputs, each in -1..1 (triggers 0..1).
type GamepadButton source
type GamepadButton uint8
GamepadButton indexes the buttons of a standard extended gamepad.
const (
ButtonA GamepadButton = iota // bottom face button (Cross on PlayStation)
ButtonB // right face button (Circle)
ButtonX // left face button (Square)
ButtonY // top face button (Triangle)
ButtonLeftShoulder
ButtonRightShoulder
ButtonLeftStick
ButtonRightStick
ButtonMenu // Start / Options
ButtonOptions // Back / Share
ButtonHome
ButtonDpadUp
ButtonDpadDown
ButtonDpadLeft
ButtonDpadRight
GamepadButtonCount
)
Standard controller buttons. GamepadButtonCount is the array bound, not a button; face-button names describe positions across brands.
type GamepadInfo source
type GamepadInfo struct {
Name, Backend string
VendorID, ProductID uint16
Buttons [GamepadButtonCount]bool
Axes [GamepadAxisCount]bool
}
GamepadInfo describes the controls mapped into Gamepad, not every hardware input. Zero numeric IDs and false masks mean unknown or unavailable. Names and backend identifiers are descriptive, not persistent device identity.
type Key source
type Key uint8
Key identifies a physical key by position, independent of the active keyboard layout, using the names of the equivalent US layout key.
const (
KeyUnknown Key = iota
KeyA
KeyB
KeyC
KeyD
KeyE
KeyF
KeyG
KeyH
KeyI
KeyJ
KeyK
KeyL
KeyM
KeyN
KeyO
KeyP
KeyQ
KeyR
KeyS
KeyT
KeyU
KeyV
KeyW
KeyX
KeyY
KeyZ
Key0
Key1
Key2
Key3
Key4
Key5
Key6
Key7
Key8
Key9
KeyF1
KeyF2
KeyF3
KeyF4
KeyF5
KeyF6
KeyF7
KeyF8
KeyF9
KeyF10
KeyF11
KeyF12
KeyF13
KeyF14
KeyF15
KeyF16
KeyF17
KeyF18
KeyF19
KeyF20
KeySpace
KeyEnter
KeyEscape
KeyTab
KeyBackspace
KeyDelete
KeyInsert
KeyHome
KeyEnd
KeyPageUp
KeyPageDown
KeyLeft
KeyRight
KeyUp
KeyDown
KeyMinus
KeyEqual
KeyLeftBracket
KeyRightBracket
KeyBackslash
KeySemicolon
KeyApostrophe
KeyGrave
KeyComma
KeyPeriod
KeySlash
KeyCapsLock
KeyNumLock
KeyScrollLock
KeyPrintScreen
KeyPause
KeyMenu
KeyLeftShift
KeyRightShift
KeyLeftControl
KeyRightControl
KeyLeftAlt
KeyRightAlt
KeyLeftSuper
KeyRightSuper
KeyFunction
KeyWorld1 // the key left of Z on ISO layouts
KeyWorld2
KeyKeypad0
KeyKeypad1
KeyKeypad2
KeyKeypad3
KeyKeypad4
KeyKeypad5
KeyKeypad6
KeyKeypad7
KeyKeypad8
KeyKeypad9
KeyKeypadDecimal
KeyKeypadDivide
KeyKeypadMultiply
KeyKeypadSubtract
KeyKeypadAdd
KeyKeypadEnter
KeyKeypadEqual
KeyCount // number of key codes; not a key
)
Physical key identifiers in US-layout positions. KeyUnknown means no recognized key; KeyCount is an array bound, not a key.
type KeyDescription source
type KeyDescription struct {
Label string // native/layout label, empty if unavailable
Symbol KeySymbol // unmodified level-zero logical symbol, empty if unknown
}
KeyDescription describes one physical key in a keyboard-layout snapshot.
type KeySymbol source
type KeySymbol string
KeySymbol is a layout-dependent logical key. Namespaces are disjoint: text:<UTF-8> is unmodified printable text, key:<name> is a named nonprinting key, and dead:<native name> is a dead key. Empty means unknown. Dead-key names are backend-specific; these symbols do not describe IME composition. Named keys use physical-key names where equivalents exist (for example key:Enter), or a native name otherwise. Printable names cannot collide.
TextSymbol source
func TextSymbol(text string) KeySymbol
TextSymbol constructs a printable logical key for reverse lookup. Empty text produces the unknown symbol. Text may contain more than one rune.
type KeyboardLayout source
type KeyboardLayout struct {
Name string // system-provided layout name or identifier, empty if unavailable
Keys [KeyCount]KeyDescription
}
KeyboardLayout is a snapshot of the active native layout. Refresh it when displaying bindings after a layout change. Queries do not change keyboard modifiers, dead-key state or IME composition. Symbols use the native layout's unmodified level with locks off: on Windows and common XKB layouts keypad digits therefore map to navigation, while macOS keypads remain numeric. Keys are indexed by physical Key. This is for binding UI, not per-frame polling.
KeysFor source
func (l KeyboardLayout) KeysFor(symbol KeySymbol) []Key
KeysFor returns every physical key producing symbol, in Key order. A symbol may have several keys (for example a digit and its keypad equivalent). Unknown symbols return no keys.
type Mods source
type Mods uint8
Mods is a set of modifier keys held during an event.
type MouseButton source
type MouseButton uint8
MouseButton numbers buttons from the left; Left, Right and Middle are the conventional three and further buttons follow in device order.
type Source source
type Source struct {
Kind SourceKind // device input category
Code int // Key, MouseButton, GamepadButton or GamepadAxis
Scale float32 // signed contribution at full travel; zero means 1
}
Source is one physical input an action can be bound to: a key, a mouse button, a gamepad button, or a gamepad axis in one direction. Scale is what it contributes to the action's value when fully on (1 by default, -1 for the negative side of an axis pair).
PadAxis source
func PadAxis(a GamepadAxis) Source
PadAxis binds the positive side of a gamepad axis; Neg flips it.
ParseSource source
func ParseSource(text string) (Source, error)
ParseSource reads a source written by String.
MarshalText source
func (s Source) MarshalText() ([]byte, error)
MarshalText writes the source as String does.
Neg source
func (s Source) Neg() Source
Neg returns the source contributing the opposite sign: the A key of an A and D pair, the left half of a stick's x axis.
String source
func (s Source) String() string
String names the source the way bindings are saved: "key:Space", "mouse:Left", "pad:A", "axis:LeftX", "-axis:LeftX" for the negative side, with a scale other than 1 or -1 appended as "*0.5".
UnmarshalText source
func (s *Source) UnmarshalText(b []byte) error
UnmarshalText reads what MarshalText wrote.
type State source
type State struct {
// contains filtered or unexported fields
}
State is the keyboard and mouse as a game sees them each update: what is
held, what changed since the last update, where the pointer is and what
text was typed. The engine feeds it from platform events.
The zero value is idle. State is not safe for concurrent access.
Key and mouse accessors require valid enum values below their Count.
Example
package main
import (
"fmt"
"github.com/matjam/bunyip/input"
)
func main() {
// The engine fills the state from platform events and hands it to the
// game as ctx.Input. Levels say what is held now, edges what changed
// since the last update, so a press is seen exactly once.
move := func(in *input.State) (dx float32, jump bool) {
if in.KeyDown(input.KeyD) {
dx++
}
if in.KeyDown(input.KeyA) {
dx--
}
return dx, in.KeyPressed(input.KeySpace)
}
var in input.State // ctx.Input under engine.Run
fmt.Println(move(&in))
}
0 false
AppendKeysDown source
func (s *State) AppendKeysDown(dst []Key) []Key
AppendKeysDown appends every key currently held to dst, in key order, and returns the extended slice. Pass a slice kept between frames, truncated to zero length, and the scan allocates nothing:
g.held = in.AppendKeysDown(g.held[:0])
Chars source
func (s *State) Chars() []rune
Chars returns committed text typed since the last update, in order (or, during Draw, since the last drawn frame). The returned slice is borrowed from State; copy it to retain text beyond the current call to the game's Update or Draw, and do not modify it.
Composition source
func (s *State) Composition() string
Composition returns the input method's uncommitted text, such as the syllables of a Japanese word still being converted. Text fields show it after the committed text; it is empty when nothing is being composed.
Gamepad source
func (s *State) Gamepad(i int) *Gamepad
Gamepad returns a snapshot of controller i (0..MaxGamepads-1); a disconnected one reads as idle. Out-of-range indices return an idle snapshot. Changing the snapshot does not modify State. During Draw button edges cover the whole frame, as for keys and mouse buttons; connection flags and axis transitions still cover the update only.
KeyHeld source
func (s *State) KeyHeld(k Key) float32
KeyHeld reports how long the key has been held, in seconds of updates, and zero when it is up: charge-up attacks, hold-to-confirm, cheat codes that want a long press.
KeyPressed source
func (s *State) KeyPressed(k Key) bool
KeyPressed reports whether the key went down since the last update (or, during Draw, since the last frame). Key repeats count as presses so held keys scroll and step.
KeyReleased source
func (s *State) KeyReleased(k Key) bool
KeyReleased reports whether the key went up since the last update (or, during Draw, since the last drawn frame).
KeyRepeated source
func (s *State) KeyRepeated(k Key) bool
KeyRepeated reports whether the key produced an operating-system repeat since the last update (or, during Draw, since the last frame). KeyPressed already counts repeats. KeyPressed(k) && !KeyRepeated(k) filters repeats, but also filters an initial press if both kinds of event arrived in the same update or frame.
KeysDown source
func (s *State) KeysDown() []Key
KeysDown returns every key currently held, in key order: for rebinding screens and combos.
It allocates the slice it returns, so it is for a settings screen or a combo check that runs on demand, not for every frame of play. To ask every frame, keep a slice and use AppendKeysDown, or ask about the keys the game cares about with KeyDown.
Mods source
func (s *State) Mods() Mods
Mods returns the active modifiers, including Caps Lock and Num Lock. Modifier changes are reported even when no key is pressed or released.
Mouse source
func (s *State) Mouse() (x, y float32)
Mouse returns the pointer position in view units.
MouseDelta source
func (s *State) MouseDelta() (dx, dy float32)
MouseDelta returns pointer movement since the last update, in view units; it keeps reporting while the cursor is captured.
MouseDoubleClicked source
func (s *State) MouseDoubleClicked(b MouseButton) bool
MouseDoubleClicked reports whether the button was pressed twice within DoubleClickTime and DoubleClickDistance, on the second press.
MouseDown source
func (s *State) MouseDown(b MouseButton) bool
MouseDown reports whether the button is held.
MousePos source
func (s *State) MousePos() lin.Vec2
MousePos returns the pointer position as a vector.
MousePressed source
func (s *State) MousePressed(b MouseButton) bool
MousePressed reports whether the button went down since the last update (or, during Draw, since the last frame).
MouseReleased source
func (s *State) MouseReleased(b MouseButton) bool
MouseReleased reports whether the button went up since the last update (or, during Draw, since the last drawn frame).
Source files
actions.go actions_test.go bench_test.go edges_test.go example_test.go gamepad.go gamepad_frame_test.go gamepad_info.go hook.go keys.go keys_string.go layout.go layout_test.go state.go state_test.go