Documentation
¶
Overview ¶
Package input is a pure-Go macOS mouse and keyboard synthesis library.
input uses CGEvent (Quartz Event Services) to post mouse and keyboard events system-wide. It's the foundation for automation, UI testing, screen-demo tooling, and remote-control agents — anything that needs to drive the cursor or type text.
Quick start ¶
ctx := context.Background()
if err := input.Load(); err != nil { log.Fatal(err) }
// Move cursor and click
input.ClickAt(ctx, 400, 300)
// Type text
input.Type(ctx, "hello, world")
// Cmd+C hotkey
input.Hotkey(ctx, input.ModCommand, input.KeyC)
Permissions ¶
macOS requires the invoking binary to be listed in System Settings → Privacy & Security → Accessibility for synthesized events to take effect. Without permission, calls return nil (no error) but the events have no visible effect — this matches macOS's silent-failure model. Use Trusted to check, and PromptTrust to trigger the system dialog.
Dylib placement ¶
input-go ships a universal (arm64+x86_64) companion dylib via //go:embed. On the first call into the package, the embedded bytes are extracted to ~/Library/Caches/input-go/<hash>/libinput_sync.dylib and Dlopened. Set DylibPath to a non-empty value before the first call if you ship a custom-built or patched dylib.
Index ¶
- Constants
- Variables
- func Click(ctx context.Context, button MouseButton, clicks int, opts ...PostOption) error
- func ClickAt(ctx context.Context, x, y float64, opts ...PostOption) error
- func ClickAtButton(ctx context.Context, x, y float64, button MouseButton, clicks int, ...) error
- func CursorPosition() (x, y float64, err error)
- func DoubleClick(ctx context.Context, x, y float64, opts ...PostOption) error
- func Drag(ctx context.Context, fromX, fromY, toX, toY float64, duration time.Duration, ...) error
- func Hold(ctx context.Context, mods Modifier, keys []Key, opts ...PostOption) func()
- func Hotkey(ctx context.Context, mods Modifier, key Key, opts ...PostOption) error
- func KeyDown(ctx context.Context, key Key, mods Modifier, opts ...PostOption) error
- func KeyUp(ctx context.Context, key Key, mods Modifier, opts ...PostOption) error
- func Load() error
- func Move(ctx context.Context, x, y float64, opts ...PostOption) error
- func MoveBy(ctx context.Context, dx, dy float64, opts ...PostOption) error
- func MoveSmooth(ctx context.Context, x, y float64, duration time.Duration, opts ...PostOption) error
- func PasteText(ctx context.Context, text string, opts ...PostOption) error
- func Press(ctx context.Context, key Key, opts ...PostOption) error
- func PromptTrust() bool
- func ReadClipboard(ctx context.Context) (string, error)
- func RequireTrust() error
- func ResolvedDylibPath() string
- func RightClick(ctx context.Context, x, y float64, opts ...PostOption) error
- func ScreenSize() (w, h float64, err error)
- func Scroll(ctx context.Context, dx, dy int, opts ...PostOption) error
- func ScrollSmooth(ctx context.Context, dx, dy int, duration time.Duration, opts ...PostOption) error
- func Trusted() bool
- func Type(ctx context.Context, text string, opts ...PostOption) error
- func TypeSlow(ctx context.Context, text string, perCharDelay time.Duration, ...) error
- func WriteClipboard(ctx context.Context, text string) error
- type Key
- type Modifier
- type MouseButton
- type PostOption
Constants ¶
const Version = "0.3.0"
Version is the semantic-version tag of this package. Kept in sync with git tags; updated per release.
Variables ¶
var DylibPath = ""
DylibPath is an optional override for the location of libinput_sync.dylib. Default (empty): extract the embedded copy to the user cache directory. Set to a non-empty path BEFORE the first call into this package if shipping a custom-built dylib.
var ErrInvalidArg = errors.New("input: invalid argument")
ErrInvalidArg is returned for malformed inputs (e.g. an empty key, a negative click count).
var ErrNotTrusted = errors.New("input: accessibility permission not granted")
ErrNotTrusted is returned when the current process lacks Accessibility permission AND the caller explicitly requested a trust check (see RequireTrust). Regular input calls DO NOT return this error — they silently no-op, matching macOS behavior.
Functions ¶
func Click ¶
func Click(ctx context.Context, button MouseButton, clicks int, opts ...PostOption) error
Click presses and releases `button` at the current cursor position. `clicks` is the click count (1 = single, 2 = double, 3 = triple). A zero or negative `clicks` is treated as 1.
func ClickAt ¶
func ClickAt(ctx context.Context, x, y float64, opts ...PostOption) error
ClickAt moves the cursor to (x, y) and single-left-clicks. This is the most common convenience — 90% of automation needs it.
func ClickAtButton ¶
func ClickAtButton(ctx context.Context, x, y float64, button MouseButton, clicks int, opts ...PostOption) error
ClickAtButton is like ClickAt but with a configurable button and click count.
func CursorPosition ¶
CursorPosition returns the current cursor location in global screen coordinates (origin at top-left).
func DoubleClick ¶
func DoubleClick(ctx context.Context, x, y float64, opts ...PostOption) error
DoubleClick is shorthand for ClickAtButton(..., ButtonLeft, 2).
func Drag ¶
func Drag(ctx context.Context, fromX, fromY, toX, toY float64, duration time.Duration, opts ...PostOption) error
Drag performs a "mouse-down → move → mouse-up" sequence: press the left button at `from`, interpolate the cursor toward `to` over `duration`, release. Used for drag-select, drag-to-reorder, slider control, etc.
func Hold ¶ added in v0.2.0
func Hold(ctx context.Context, mods Modifier, keys []Key, opts ...PostOption) func()
Hold presses the given modifiers and keys, then returns a release closure that lifts them. Designed for `defer` to guarantee teardown even on panic or early return:
defer input.Hold(ctx, input.ModShift, nil)() input.Press(ctx, input.KeyTab) // Shift+Tab input.Press(ctx, input.KeyTab) // still Shift+Tab
`keys` is a slice (rather than variadic) because the function already has a variadic `opts` tail; pass nil when only modifiers are needed:
defer input.Hold(ctx, input.ModCommand, nil, input.WithPID(p))()
Without this helper, any error path between KeyDown and KeyUp leaks a "stuck" modifier — every subsequent input gets corrupted at the OS level (sticky shift / cmd / etc.) until the user notices and taps the modifier manually. Hold + defer eliminates the whole class of bug.
Multiple keys are pressed in order, released in reverse order — so `Hold(ctx, ModCommand|ModShift)` releases Shift before Command, the natural teardown order.
The release closure is idempotent — calling it twice is safe but only the first call has effect. Release errors are silently dropped (macOS's silent-failure model means there's no useful action anyway).
Returns a no-op release if Load failed or ctx is already canceled.
func Hotkey ¶
Hotkey presses `key` with `mods` held: down-modifiers → down-key → up-key → up-modifiers. This is how you send Cmd+C, Cmd+Shift+T, etc.
input.Hotkey(ctx, input.ModCommand, input.KeyC) // copy input.Hotkey(ctx, input.ModCommand|input.ModShift, input.KeyT) // reopen tab
func KeyDown ¶
KeyDown posts a key-down event for `key`. Optionally carries modifier flags — pass 0 for no modifiers.
func Load ¶
func Load() error
Load explicitly loads the companion dylib. It's idempotent: subsequent calls return the same cached error (or nil).
Resolution order:
- If DylibPath is non-empty, use it (user override).
- Otherwise, extract the embedded universal dylib to the cache directory and Dlopen from there.
Load is called automatically by every public function; the exported form exists so applications can fail fast at startup.
func Move ¶
func Move(ctx context.Context, x, y float64, opts ...PostOption) error
Move jumps the cursor instantly to (x, y) in global screen coordinates. (0, 0) is the top-left of the main display. Pass WithPID to route the event to a specific app without focus steal.
func MoveBy ¶
func MoveBy(ctx context.Context, dx, dy float64, opts ...PostOption) error
MoveBy offsets the cursor by (dx, dy) from its current position.
func MoveSmooth ¶
func MoveSmooth(ctx context.Context, x, y float64, duration time.Duration, opts ...PostOption) error
MoveSmooth moves the cursor from its current position to (x, y) by linearly interpolating through intermediate points. `duration` controls total animation length; shorter values feel snappier. A `duration` of 0 degenerates to Move.
This is the animation primitive used for "screen demo" recordings where a teleporting cursor looks jarring.
func PasteText ¶ added in v0.3.0
func PasteText(ctx context.Context, text string, opts ...PostOption) error
PasteText injects a string by writing to the macOS clipboard, firing ⌘V via the standard Hotkey path, and restoring the user's previous clipboard text ~150ms later.
Why a separate function rather than just Type: Type synthesizes a key event per character at full keyboard rate (~100 char/s). IME front-ends (Pinyin / Wubi / Kotoeri) frequently can't keep up with that rate — characters get dropped or the IME's composition state desyncs. PasteText sidesteps the IME entirely by routing through the system pasteboard.
Restore semantics: PasteText always saves the previous clipboard before writing + restores it after the keystroke. Non-text pasteboard content (image / file / RTF) does NOT survive the round-trip — by macOS pasteboard design — but plain text (the 95% case) does. If you don't want the restore, set the clipboard yourself with WriteClipboard after PasteText returns.
Routing: [PostOption]s (e.g. WithPID) apply to the ⌘V keystroke, so paste can target a background app without stealing focus from the user's foreground window. The pbcopy/pbpaste shell-outs are system-wide either way — there's only one macOS pasteboard.
if err := input.PasteText(ctx, "你好世界"); err != nil { ... }
input.PasteText(ctx, longChineseText, input.WithPID(pid))
Errors:
- pbcopy / pbpaste failures propagate as "pbcopy: …" / "pbpaste: …". Rare unless running in a sandbox without /usr/bin.
- The ⌘V keystroke uses the same error chain as Hotkey.
func Press ¶
func Press(ctx context.Context, key Key, opts ...PostOption) error
Press taps a key: down then up. This is the common case for any key that doesn't need to be held.
func PromptTrust ¶
func PromptTrust() bool
PromptTrust triggers the system "<app> wants access to control your computer" dialog if permission has not yet been granted. Returns current trust state (true if granted, false if the user hasn't responded yet or denied).
This is a one-shot prompt — subsequent calls return the current state without showing the dialog again (until the user resets TCC).
func ReadClipboard ¶ added in v0.3.0
ReadClipboard returns the macOS clipboard's current text content via pbpaste. Useful as a building block for code that needs to inspect / round-trip the pasteboard without going through the whole PasteText flow.
func RequireTrust ¶
func RequireTrust() error
RequireTrust returns nil if Accessibility permission is granted, or ErrNotTrusted otherwise. Convenience for callers that want a hard fail rather than silent no-ops.
func ResolvedDylibPath ¶
func ResolvedDylibPath() string
ResolvedDylibPath returns the filesystem path that Load used (or would use) to Dlopen the dylib. Intended for diagnostics.
func RightClick ¶
func RightClick(ctx context.Context, x, y float64, opts ...PostOption) error
RightClick is shorthand for ClickAtButton(..., ButtonRight, 1).
func ScreenSize ¶
ScreenSize returns the main display's pixel dimensions.
func Scroll ¶
func Scroll(ctx context.Context, dx, dy int, opts ...PostOption) error
Scroll posts a scroll-wheel event. `dy` is vertical (positive scrolls content up — same as flicking your finger upward on a trackpad); `dx` is horizontal (positive scrolls right). Units are pixels.
func ScrollSmooth ¶
func ScrollSmooth(ctx context.Context, dx, dy int, duration time.Duration, opts ...PostOption) error
ScrollSmooth posts a series of smaller scroll events to approximate a single larger scroll. Looks more natural than a single big jump and avoids "overshoot" on momentum-scrolling apps.
func Trusted ¶
func Trusted() bool
Trusted returns true if the current process has Accessibility permission granted. Does not prompt — use PromptTrust for that.
func Type ¶
func Type(ctx context.Context, text string, opts ...PostOption) error
Type synthesizes keyboard events that produce `text` in the currently focused text field. Works for arbitrary UTF-8 including emoji and CJK, because it uses CGEventKeyboardSetUnicodeString rather than translating through the current keyboard layout.
Note: because this bypasses the keyboard layout, typing "A" is a literal A glyph, not "shift+a". Apps that listen for raw keycodes (games, key remappers) won't see a modifier press.
func TypeSlow ¶
func TypeSlow(ctx context.Context, text string, perCharDelay time.Duration, opts ...PostOption) error
TypeSlow is like Type but inserts `perCharDelay` between characters. Useful for demos where "instant paste" looks artificial, or for apps that throttle rapid-fire input.
func WriteClipboard ¶ added in v0.3.0
WriteClipboard writes text to the macOS clipboard via pbcopy. Companion to ReadClipboard. Use this if you want to populate the clipboard without immediately firing ⌘V.
Types ¶
type Key ¶
type Key int32
Key is a macOS virtual keycode. Values match <Carbon/HIToolbox/Events.h>.
Use the named constants (KeyA, KeySpace, ...) rather than numeric values — Apple's keycodes are ANSI-layout specific and not obvious.
const ( KeyA Key = 0x00 KeyB Key = 0x0B KeyC Key = 0x08 KeyD Key = 0x02 KeyE Key = 0x0E KeyF Key = 0x03 KeyG Key = 0x05 KeyH Key = 0x04 KeyI Key = 0x22 KeyJ Key = 0x26 KeyK Key = 0x28 KeyL Key = 0x25 KeyM Key = 0x2E KeyN Key = 0x2D KeyO Key = 0x1F KeyP Key = 0x23 KeyQ Key = 0x0C KeyR Key = 0x0F KeyS Key = 0x01 KeyT Key = 0x11 KeyU Key = 0x20 KeyV Key = 0x09 KeyW Key = 0x0D KeyX Key = 0x07 KeyY Key = 0x10 KeyZ Key = 0x06 Key0 Key = 0x1D Key1 Key = 0x12 Key2 Key = 0x13 Key3 Key = 0x14 Key4 Key = 0x15 Key5 Key = 0x17 Key6 Key = 0x16 Key7 Key = 0x1A Key8 Key = 0x1C Key9 Key = 0x19 KeyReturn Key = 0x24 KeyTab Key = 0x30 KeySpace Key = 0x31 KeyDelete Key = 0x33 // Backspace on PC keyboards KeyEscape Key = 0x35 KeyForwardDelete Key = 0x75 // fn+delete / dedicated Del key KeyLeftCommand Key = 0x37 KeyLeftShift Key = 0x38 KeyCapsLock Key = 0x39 KeyLeftOption Key = 0x3A KeyLeftControl Key = 0x3B KeyRightShift Key = 0x3C KeyRightOption Key = 0x3D KeyRightControl Key = 0x3E KeyFn Key = 0x3F KeyArrowLeft Key = 0x7B KeyArrowRight Key = 0x7C KeyArrowDown Key = 0x7D KeyArrowUp Key = 0x7E KeyF1 Key = 0x7A KeyF2 Key = 0x78 KeyF3 Key = 0x63 KeyF4 Key = 0x76 KeyF5 Key = 0x60 KeyF6 Key = 0x61 KeyF7 Key = 0x62 KeyF8 Key = 0x64 KeyF9 Key = 0x65 KeyF10 Key = 0x6D KeyF11 Key = 0x67 KeyF12 Key = 0x6F KeyHome Key = 0x73 KeyEnd Key = 0x77 KeyPageUp Key = 0x74 KeyPageDown Key = 0x79 KeyMinus Key = 0x1B KeyEqual Key = 0x18 KeyLeftBracket Key = 0x21 KeyRightBracket Key = 0x1E KeyBackslash Key = 0x2A KeySemicolon Key = 0x29 KeyQuote Key = 0x27 KeyComma Key = 0x2B KeyPeriod Key = 0x2F KeySlash Key = 0x2C KeyGrave Key = 0x32 )
US-ANSI virtual keycodes — stable across macOS versions. Source: <HIToolbox/Events.h> kVK_* constants.
type Modifier ¶
type Modifier uint64
Modifier is a bitmask of keyboard modifier flags. Combine with bitwise OR: `input.ModCommand | input.ModShift`. Values match CGEventFlags.
const ( // ModCommand is the Command (⌘) modifier. ModCommand Modifier = 1 << 20 // kCGEventFlagMaskCommand // ModShift is the Shift (⇧) modifier. ModShift Modifier = 1 << 17 // kCGEventFlagMaskShift // ModOption is the Option/Alt (⌥) modifier. ModOption Modifier = 1 << 19 // kCGEventFlagMaskAlternate // ModControl is the Control (⌃) modifier. ModControl Modifier = 1 << 18 // kCGEventFlagMaskControl // ModFunction is the Function (fn) modifier. ModFunction Modifier = 1 << 23 // kCGEventFlagMaskSecondaryFn )
type MouseButton ¶
type MouseButton int32
MouseButton enumerates the three mouse buttons that CGEvent understands.
const ( ButtonLeft MouseButton = 0 ButtonRight MouseButton = 1 ButtonOther MouseButton = 2 )
type PostOption ¶ added in v0.2.0
type PostOption func(*postConfig)
PostOption tunes how an event is posted to the OS. Pass via the variadic `opts` tail of any event-posting function.
func WithPID ¶ added in v0.2.0
func WithPID(pid int32) PostOption
WithPID routes events directly to the given process via CGEventPostToPid, avoiding focus steal — your foreground app stays foreground while input-go drives a background app. pid=0 (the default when the option is omitted) keeps the legacy system-wide HID event tap behavior, which moves focus to the targeted control's owning app.
Verified working on Lark / VSCode / Chrome and other Electron + Web View hosts. Some Apple sandboxed apps (newer Mail / Messages) may ignore PID-targeted events — fall back to the default if you see no effect.
input.Click(ctx, 400, 300, input.WithPID(int32(targetPID))) input.Type(ctx, "hello", input.WithPID(int32(targetPID))) input.Hotkey(ctx, input.ModCommand, input.KeyS, input.WithPID(int32(targetPID)))
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
input
command
Command input drives the mouse and keyboard from the command line.
|
Command input drives the mouse and keyboard from the command line. |
|
internal
|
|
|
dylib
Package dylib embeds the ObjC companion library so downstream users can simply `go get` input-go without building C code on their machine.
|
Package dylib embeds the ObjC companion library so downstream users can simply `go get` input-go without building C code on their machine. |