makc

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 9, 2026 License: MIT Imports: 18 Imported by: 0

README

makc

Pronounced mak-see
Like Maksim without the final m.
Mouse And Keyboard Control for Go.

CI Go Reference License

makc is a no-cgo mouse and keyboard control package for Windows, macOS, and Linux.

The current rewrite replaces the old C header, embedded DLL, and skip_hook submodule with pure Go backends built on:

Install

go get github.com/aiwaki/makc

The module path is currently github.com/aiwaki/makc; it does not use a /v2 suffix yet.

Quick Start

package main

import (
	"context"
	"log"
	"time"

	"github.com/aiwaki/makc"
)

func main() {
	ctx := context.Background()

	client, err := makc.Open()
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	profile := makc.BalancedInputProfile(42)
	sequence := makc.NewInputSequence(
		makc.MoveStep(makc.Rel(10, 0)),
		makc.PauseStep(80*time.Millisecond),
		makc.MoveStep(makc.Rel(-10, 0)),
		makc.TextStep("makc", profile.Typing),
	)

	if err := client.Run(ctx, sequence); err != nil {
		log.Fatal(err)
	}
}

For a safer runnable version that does not click or type unless explicitly asked:

go run ./examples/sequence
go run ./examples/sequence -text "hello"
go run ./examples/sequence -click

Features

  • Mouse state: cursor position, screen size, and button state.
  • Mouse injection: relative/absolute movement, buttons, wheel, horizontal wheel, batches, drags, deterministic paths, and seeded natural paths.
  • Keyboard state: key state, virtual-key events, scan-code events, combos, and Unicode text where the backend supports it.
  • Timing helpers: click holds, double-click cadence, key holds, fixed/variable typing delays, and reusable Fast, Balanced, and Careful input profiles.
  • Mixed sequences: build move -> pause -> click -> type -> hotkey workflows with InputSequence, then run them through Client.Run(ctx, sequence).
  • Input listeners: Windows low-level hooks, Windows Raw Input, and Linux evdev.
  • Runtime diagnostics: display stack, backend dependency probes, Linux portal hints, and smoke tooling for local or VM validation.

The legacy pkg/types, pkg/types/buttons, and pkg/types/keys packages are kept as deprecated compatibility aliases. New code should import the root github.com/aiwaki/makc package directly.

Release notes are tracked in CHANGELOG.md.

Backends

MouseInjectionAuto and KeyboardInjectionAuto select the preferred backend for the current platform:

Platform Mouse Keyboard Notes
Windows InjectMouseInput when exported, otherwise SendInput InjectKeyboardInput when exported, otherwise SendInput SendInput supports per-client dwExtraInfo tagging. Tested Windows 11 Inject*Input builds reject non-zero extra info, so makc sends zero extra info there.
macOS CoreGraphics CGEvent CoreGraphics CGEvent Requires Accessibility permission for event injection.
Linux /dev/uinput /dev/uinput Requires permission to open /dev/uinput; listening uses evdev.

Explicit backend selection:

client, err := makc.Open(
	makc.WithMouseInjection(makc.MouseInjectionInjectMouseInput),
	makc.WithKeyboardInjection(makc.KeyboardInjectionInjectKeyboardInput),
)

Linux notes:

  • uinput supports relative mouse movement, buttons, wheel events, mapped key events, and raw Linux key-code scan events.
  • Linux input state and listening use /dev/input/event* devices.
  • Cursor position, screen size, and absolute movement use an optional purego X11/Xlib layer when DISPLAY is set.
  • Wayland absolute cursor control and Unicode text injection still need a display-server-specific layer and currently return ErrUnsupported.
  • GNOME/XDG Desktop Portal diagnostics are available, but the actual libei backend is still future work.

API Map

Common operations:

  • makc.Open, Client.Close, Client.RuntimeInfo
  • Client.Mouse.Move, MoveTo, MoveBy, Click, DoubleClick, Wheel, HWheel, Drag, DragBy, Inject
  • Client.Keyboard.Tap, TapWithHold, Combo, TypeText, TypeTextWithProfile, ScanTap, Inject
  • Client.Listen
  • Client.Run, Client.RunSteps

Profiles and sequences:

  • Movement: InstantMovement, LinearMovement, EaseInOutMovement, NaturalMovement, NaturalMovementWithJitter
  • Timing: FixedInterval, VariableInterval, ClickProfile, TypingProfile
  • Presets: InstantInputProfile, FastInputProfile, BalancedInputProfile, CarefulInputProfile
  • Sequence steps: MoveStep, ClickStep, DoubleClickStep, KeyTapStep, ComboStep, TextStep, PauseStep, MouseStep, KeyboardStep

Parsing helpers for CLIs and config files:

  • ParseKey
  • ParseMouseButton

Examples

go run ./examples/mouse
go run ./examples/keyboard
go run ./examples/sequence

The sequence example only performs a tiny relative move by default. Pass -click or -text "hello" when you intentionally want those events.

Checks

Run the default local checks:

bash scripts/check.sh

Set MAKC_CHECK_PARALLELS=1 to include a short Windows Parallels smoke pass.

Build smoke binaries manually:

GOOS=windows GOARCH=arm64 go build -o dist/makc-smoke-windows-arm64.exe ./cmd/makc-smoke
GOOS=darwin GOARCH=arm64 go build -o dist/makc-smoke-darwin-arm64 ./cmd/makc-smoke
GOOS=linux GOARCH=arm64 go build -o dist/makc-smoke-linux-arm64 ./cmd/makc-smoke

The smoke command opens the selected backend and reads current state where the backend supports it. Useful flags:

./dist/makc-smoke -runtime-info
./dist/makc-smoke -capabilities
./dist/makc-smoke -inject -dx 1 -dy 1
./dist/makc-smoke -tap shift -tap-hold 30ms
./dist/makc-smoke -type "hello" -type-profile variable -type-min-delay 40ms -type-max-delay 120ms
./dist/makc-smoke -click -click-count 2 -click-hold 30ms -click-interval 120ms

Parallels Smoke

For Windows 11 on Parallels Desktop:

bash scripts/parallels-smoke.sh
bash scripts/parallels-smoke.sh -backend injectmouseinput -inject -dx 1 -dy 1
bash scripts/parallels-smoke.sh -backend injectmouseinput -drag -dx 80 -dy 40
bash scripts/parallels-smoke.sh -backend injectmouseinput -drag -profile natural -seed 42 -dx 80 -dy 40
bash scripts/parallels-smoke.sh -keyboard-backend injectkeyboardinput -tap shift -scan 0x2A
bash scripts/parallels-smoke.sh -listen -include-injected -listen-count 3
bash scripts/parallels-smoke.sh -listen -listen-backend rawinput -listen-count 1
bash scripts/parallels-smoke.sh -backend sendinput -keyboard-backend sendinput -input-tag 0x1234 -listen -normalize-own-injected -listen-count 3

The script uses prlctl exec --current-user so Windows APIs run in the interactive user session. If Parallels reports a successful resume but the VM immediately returns to paused, disable the VM idle pause option:

prlctl set "Windows 11" --pause-idle off

MAKC_PARALLELS_TIMEOUT controls the prlctl exec watchdog in seconds. Set it to 0 to disable the watchdog while debugging Parallels itself.

Linux Diagnostics

On a Linux desktop or VM with Go installed:

bash scripts/linux-smoke.sh

The helper loads uinput when possible, builds makc-smoke, and sends a tiny relative mouse move plus a Shift tap through the Linux uinput backend by default. Pass regular makc-smoke flags after the script name to customize the run.

On macOS with Parallels Desktop and a Linux VM with Parallels Tools installed:

bash scripts/parallels-linux-smoke.sh

Inside a Linux guest, discover the active GUI/session-bus environment:

bash scripts/linux-session-env.sh
bash scripts/linux-session-env.sh --exec ./dist/makc-smoke-linux -runtime-info

Read-only XDG Desktop Portal RemoteDesktop diagnostics:

bash scripts/linux-session-env.sh --exec bash scripts/linux-portal-info.sh
bash scripts/linux-session-env.sh --exec bash scripts/linux-gnome-remote-desktop-info.sh

Stateful portal handshake diagnostic:

GOOS=linux GOARCH=arm64 go build -o dist/makc-portal-handshake-linux-arm64 ./cmd/makc-portal-handshake
bash scripts/linux-session-env.sh --exec ./dist/makc-portal-handshake-linux-arm64
bash scripts/linux-session-env.sh --exec ./dist/makc-portal-handshake-linux-arm64 -select-devices -start -timeout 300s

cmd/makc-portal-handshake default mode creates and closes a transient portal session. It does not call Start, request permissions, or inject input unless you pass the corresponding flags. -start may show a desktop permission prompt.

To allow non-root Linux uinput injection, run this inside the Linux guest and then log out and back in:

sudo bash scripts/linux-uinput-permissions.sh "$USER"

Documentation

Overview

Package makc provides no-cgo mouse and keyboard control primitives.

The API is centered around Client, which owns a platform backend and exposes Mouse, Keyboard, input sequence, runtime diagnostics, and low-level listener APIs. Native calls are bound directly from Go through purego, with x/sys/windows used for Win32 helpers.

Index

Constants

View Source
const WheelDelta = 120

WheelDelta is one standard wheel detent in Windows mouse data units.

Variables

View Source
var (
	// ErrUnsupported is returned when the selected backend cannot run on the
	// current operating system or platform build.
	ErrUnsupported = errors.New("makc: unsupported backend")

	// ErrClosed is returned after Client.Close has been called.
	ErrClosed = errors.New("makc: client is closed")
)
View Source
var InstantClick = ClickProfile{Count: 1}

InstantClick is the default click profile: one down event followed by one up event without an explicit pause.

View Source
var InstantMovement = MovementProfile{Steps: 1}

InstantMovement jumps to the target in one event.

View Source
var InstantTyping = TypingProfile{}

InstantTyping emits text without explicit pauses.

Functions

This section is empty.

Types

type ClickProfile

type ClickProfile struct {
	// Count is the number of clicks to generate. Values less than one become
	// one click.
	Count int

	// Hold is the pause between button down and button up.
	Hold time.Duration

	// Interval controls pauses between repeated clicks.
	Interval IntervalProfile
}

ClickProfile describes one or more button clicks with optional hold and between-click timing.

func ClickWithHold

func ClickWithHold(hold time.Duration) ClickProfile

ClickWithHold creates a single-click profile that holds the button before release.

func DoubleClick

func DoubleClick(hold, interval time.Duration) ClickProfile

DoubleClick creates a two-click profile with a fixed interval between clicks.

func MultiClick

func MultiClick(count int, hold time.Duration, interval IntervalProfile) ClickProfile

MultiClick creates a profile for repeated clicks.

func (ClickProfile) Events

func (p ClickProfile) Events(button MouseButton) []MouseEvent

Events returns mouse events for clicking the given button.

type Client

type Client struct {
	Mouse    *Mouse
	Keyboard *Keyboard
	// contains filtered or unexported fields
}

Client owns the lifecycle of a makc backend.

func Open

func Open(opts ...Option) (*Client, error)

Open initializes a makc client.

func (*Client) Close

func (c *Client) Close() error

Close releases backend resources. It is safe to call Close more than once.

func (*Client) InputTag

func (c *Client) InputTag() uintptr

InputTag returns the backend tag used by this client for injected inputs when the current platform supports tagging. A zero tag means input tagging is disabled or unavailable.

func (*Client) Listen

func (c *Client) Listen(ctx context.Context, opts ListenOptions) (*Listener, error)

Listen starts an input listener.

func (*Client) Run

func (c *Client) Run(ctx context.Context, sequence InputSequence) error

Run executes a sequence with this client.

func (*Client) RunSteps

func (c *Client) RunSteps(ctx context.Context, steps ...InputStep) error

RunSteps executes steps with this client.

func (*Client) RuntimeInfo

func (c *Client) RuntimeInfo(ctx context.Context) (RuntimeInfo, error)

RuntimeInfo returns input-related diagnostics for this client's process.

type DisplayInfo

type DisplayInfo struct {
	// Server is makc's normalized display server classification.
	Server DisplayServer

	// SessionType is the raw XDG_SESSION_TYPE value when available.
	SessionType string

	// CurrentDesktop is the raw XDG_CURRENT_DESKTOP value when available.
	CurrentDesktop string

	// DesktopSession is the raw DESKTOP_SESSION value when available.
	DesktopSession string

	// Display is the raw DISPLAY value when available.
	Display string

	// WaylandDisplay is the raw WAYLAND_DISPLAY value when available.
	WaylandDisplay string
}

DisplayInfo describes the active display server environment.

type DisplayServer

type DisplayServer string

DisplayServer identifies the desktop display/input stack visible to makc.

const (
	DisplayServerUnknown  DisplayServer = "unknown"
	DisplayServerHeadless DisplayServer = "headless"
	DisplayServerX11      DisplayServer = "x11"
	DisplayServerWayland  DisplayServer = "wayland"
	DisplayServerQuartz   DisplayServer = "quartz"
	DisplayServerWin32    DisplayServer = "win32"
)

type InputEvent

type InputEvent struct {
	// Kind identifies the event payload and operation.
	Kind InputEventKind

	// Time is when makc observed the event.
	Time time.Time

	// Injected reports whether the platform marked the event as injected.
	Injected bool

	// LowerIntegrityInjected reports the Windows lower-integrity injected
	// marker when available.
	LowerIntegrityInjected bool

	// Own reports whether the event matches this client's input tag where the
	// backend supports tagging.
	Own bool

	// Raw reports whether the event came from a raw input stream.
	Raw bool

	// Device is a backend-specific device handle or identifier.
	Device uintptr

	// ExtraInfo is backend-specific event metadata, such as Win32 dwExtraInfo.
	ExtraInfo uintptr

	// Mouse contains mouse event details when Kind is a mouse event.
	Mouse MouseInputEvent

	// Keyboard contains keyboard event details when Kind is InputEventKey.
	Keyboard KeyboardInputEvent
}

InputEvent is emitted by Listener.Events.

type InputEventKind

type InputEventKind uint8

InputEventKind describes one listened input event.

const (
	InputEventMouseMove InputEventKind = iota + 1
	InputEventMouseButton
	InputEventMouseWheel
	InputEventMouseHWheel
	InputEventKey
)

type InputProfile

type InputProfile struct {
	// Movement controls cursor paths generated by MovementEvents.
	Movement MovementProfile

	// Click controls single-click timing generated by ClickEvents.
	Click ClickProfile

	// DoubleClick controls double-click timing generated by DoubleClickEvents.
	DoubleClick ClickProfile

	// Typing controls per-rune text timing generated by TextEvents.
	Typing TypingProfile

	// KeyHold is the pause inserted between key-down and key-up in KeyTapEvents.
	KeyHold time.Duration
}

InputProfile groups movement, click, and keyboard timing into one reusable profile for higher-level workflows.

func BalancedInputProfile

func BalancedInputProfile(seed int64) InputProfile

BalancedInputProfile returns the default profile for ordinary UI automation.

func CarefulInputProfile

func CarefulInputProfile(seed int64) InputProfile

CarefulInputProfile returns a slower profile for fragile or latency-prone UI.

func FastInputProfile

func FastInputProfile(seed int64) InputProfile

FastInputProfile returns a short, responsive profile for quick UI work.

func InstantInputProfile

func InstantInputProfile() InputProfile

InstantInputProfile returns a profile with no explicit timing pauses.

func (InputProfile) ClickEvents

func (p InputProfile) ClickEvents(button MouseButton) []MouseEvent

ClickEvents returns mouse click events using the profile click timing.

func (InputProfile) DoubleClickEvents

func (p InputProfile) DoubleClickEvents(button MouseButton) []MouseEvent

DoubleClickEvents returns mouse double-click events using the profile timing.

func (InputProfile) KeyTapEvents

func (p InputProfile) KeyTapEvents(key Key) []KeyboardEvent

KeyTapEvents returns key tap events using the profile key hold.

func (InputProfile) MovementEvents

func (p InputProfile) MovementEvents(from, to Point) []MouseEvent

MovementEvents returns mouse movement events using the profile movement.

func (InputProfile) TextEvents

func (p InputProfile) TextEvents(text string) []KeyboardEvent

TextEvents returns keyboard text events using the profile typing timing.

type InputSequence

type InputSequence struct {
	// Steps is the ordered list of operations run by Client.Run.
	Steps []InputStep
}

InputSequence is an ordered, cancelable list of input steps.

func NewInputSequence

func NewInputSequence(steps ...InputStep) InputSequence

NewInputSequence creates a sequence from steps.

func (InputSequence) Append

func (s InputSequence) Append(steps ...InputStep) InputSequence

Append returns a copy of the sequence with steps appended.

func (InputSequence) WithPause

func (s InputSequence) WithPause(duration time.Duration) InputSequence

WithPause returns a copy of the sequence with a pause step appended.

type InputStep

type InputStep struct {
	// Kind selects which payload the step executes.
	Kind InputStepKind

	// Mouse contains events for InputStepMouse.
	Mouse []MouseEvent

	// Keyboard contains events for InputStepKeyboard.
	Keyboard []KeyboardEvent

	// Duration is the delay for InputStepPause.
	Duration time.Duration
}

InputStep is one executable sequence step. Mouse and keyboard steps may contain pause events; they are executed by the corresponding device API.

func ClickStep

func ClickStep(button MouseButton, profile ClickProfile) InputStep

ClickStep creates a mouse click step using a click profile.

func ComboStep

func ComboStep(keys ...Key) InputStep

ComboStep creates a keyboard combo step.

func DoubleClickStep

func DoubleClickStep(button MouseButton, hold, interval time.Duration) InputStep

DoubleClickStep creates a double-click step with fixed hold and interval timing.

func KeyTapStep

func KeyTapStep(key Key, hold time.Duration) InputStep

KeyTapStep creates a key tap step with an optional hold pause.

func KeyboardStep

func KeyboardStep(events ...KeyboardEvent) InputStep

KeyboardStep creates a keyboard event step.

func MouseStep

func MouseStep(events ...MouseEvent) InputStep

MouseStep creates a mouse event step.

func MoveStep

func MoveStep(move MouseMove) InputStep

MoveStep creates a mouse movement step.

func PauseStep

func PauseStep(duration time.Duration) InputStep

PauseStep creates a sequence-level pause step.

func TextStep

func TextStep(text string, profile TypingProfile) InputStep

TextStep creates a text input step using a typing profile.

type InputStepKind

type InputStepKind uint8

InputStepKind describes one step in an InputSequence.

const (
	InputStepNone InputStepKind = iota
	InputStepMouse
	InputStepKeyboard
	InputStepPause
)

type IntervalProfile

type IntervalProfile struct {
	// Min is the lower bound for generated delays.
	Min time.Duration

	// Max is the upper bound for generated delays.
	Max time.Duration

	// Seed makes variable delays reproducible.
	Seed int64
}

IntervalProfile generates deterministic delays between input events.

func FixedInterval

func FixedInterval(delay time.Duration) IntervalProfile

FixedInterval creates a profile that returns the same delay every time.

func VariableInterval

func VariableInterval(min, max time.Duration, seed int64) IntervalProfile

VariableInterval creates a seeded profile that returns delays in [min, max].

func (IntervalProfile) Durations

func (p IntervalProfile) Durations(count int) []time.Duration

Durations returns count deterministic delays.

type Key

type Key uint16

Key is a Windows virtual-key code.

const (
	KeyUnknown Key = 0x00

	KeyLeftButton   Key = 0x01
	KeyRightButton  Key = 0x02
	KeyCancel       Key = 0x03
	KeyMiddleButton Key = 0x04
	KeyXButton1     Key = 0x05
	KeyXButton2     Key = 0x06

	KeyBackspace   Key = 0x08
	KeyTab         Key = 0x09
	KeyClear       Key = 0x0C
	KeyEnter       Key = 0x0D
	KeyShift       Key = 0x10
	KeyControl     Key = 0x11
	KeyAlt         Key = 0x12
	KeyPause       Key = 0x13
	KeyCapsLock    Key = 0x14
	KeyEscape      Key = 0x1B
	KeySpace       Key = 0x20
	KeyPageUp      Key = 0x21
	KeyPageDown    Key = 0x22
	KeyEnd         Key = 0x23
	KeyHome        Key = 0x24
	KeyLeft        Key = 0x25
	KeyUp          Key = 0x26
	KeyRight       Key = 0x27
	KeyDown        Key = 0x28
	KeySelect      Key = 0x29
	KeyPrint       Key = 0x2A
	KeyExecute     Key = 0x2B
	KeyPrintScreen Key = 0x2C
	KeyInsert      Key = 0x2D
	KeyDelete      Key = 0x2E
	KeyHelp        Key = 0x2F

	Key0 Key = 0x30
	Key1 Key = 0x31
	Key2 Key = 0x32
	Key3 Key = 0x33
	Key4 Key = 0x34
	Key5 Key = 0x35
	Key6 Key = 0x36
	Key7 Key = 0x37
	Key8 Key = 0x38
	Key9 Key = 0x39

	KeyA Key = 0x41
	KeyB Key = 0x42
	KeyC Key = 0x43
	KeyD Key = 0x44
	KeyE Key = 0x45
	KeyF Key = 0x46
	KeyG Key = 0x47
	KeyH Key = 0x48
	KeyI Key = 0x49
	KeyJ Key = 0x4A
	KeyK Key = 0x4B
	KeyL Key = 0x4C
	KeyM Key = 0x4D
	KeyN Key = 0x4E
	KeyO Key = 0x4F
	KeyP Key = 0x50
	KeyQ Key = 0x51
	KeyR Key = 0x52
	KeyS Key = 0x53
	KeyT Key = 0x54
	KeyU Key = 0x55
	KeyV Key = 0x56
	KeyW Key = 0x57
	KeyX Key = 0x58
	KeyY Key = 0x59
	KeyZ Key = 0x5A

	KeyLeftWindows  Key = 0x5B
	KeyRightWindows Key = 0x5C
	KeyApps         Key = 0x5D
	KeySleep        Key = 0x5F

	KeyNumpad0   Key = 0x60
	KeyNumpad1   Key = 0x61
	KeyNumpad2   Key = 0x62
	KeyNumpad3   Key = 0x63
	KeyNumpad4   Key = 0x64
	KeyNumpad5   Key = 0x65
	KeyNumpad6   Key = 0x66
	KeyNumpad7   Key = 0x67
	KeyNumpad8   Key = 0x68
	KeyNumpad9   Key = 0x69
	KeyMultiply  Key = 0x6A
	KeyAdd       Key = 0x6B
	KeySeparator Key = 0x6C
	KeySubtract  Key = 0x6D
	KeyDecimal   Key = 0x6E
	KeyDivide    Key = 0x6F

	KeyF1  Key = 0x70
	KeyF2  Key = 0x71
	KeyF3  Key = 0x72
	KeyF4  Key = 0x73
	KeyF5  Key = 0x74
	KeyF6  Key = 0x75
	KeyF7  Key = 0x76
	KeyF8  Key = 0x77
	KeyF9  Key = 0x78
	KeyF10 Key = 0x79
	KeyF11 Key = 0x7A
	KeyF12 Key = 0x7B

	KeyNumLock      Key = 0x90
	KeyScrollLock   Key = 0x91
	KeyLeftShift    Key = 0xA0
	KeyRightShift   Key = 0xA1
	KeyLeftControl  Key = 0xA2
	KeyRightControl Key = 0xA3
	KeyLeftAlt      Key = 0xA4
	KeyRightAlt     Key = 0xA5

	KeySemicolon          Key = 0xBA
	KeyEquals             Key = 0xBB
	KeyComma              Key = 0xBC
	KeyMinus              Key = 0xBD
	KeyDot                Key = 0xBE
	KeySlash              Key = 0xBF
	KeyBackQuote          Key = 0xC0
	KeyLeftSquareBracket  Key = 0xDB
	KeyBackslash          Key = 0xDC
	KeyRightSquareBracket Key = 0xDD
	KeySingleQuote        Key = 0xDE

	KeyQuestionMark = KeySlash
	KeyMenu         = KeyAlt
)

func ParseKey

func ParseKey(name string) (Key, error)

ParseKey returns a virtual-key code by its stable lower-case name.

Numeric values are accepted with Go-style base prefixes, for example 0x41 for KeyA.

func (Key) String

func (k Key) String() string

String returns a stable, lower-case name for a key.

type Keyboard

type Keyboard struct {
	// contains filtered or unexported fields
}

Keyboard exposes keyboard state and injection operations.

func (*Keyboard) Combo

func (k *Keyboard) Combo(ctx context.Context, keys ...Key) error

Combo presses keys in order and releases them in reverse order.

func (*Keyboard) Down

func (k *Keyboard) Down(ctx context.Context, key Key) (bool, error)

Down reports whether a key is currently pressed.

func (*Keyboard) Inject

func (k *Keyboard) Inject(ctx context.Context, events ...KeyboardEvent) error

Inject sends keyboard events. Consecutive non-pause events are sent as one backend batch; pause events flush the current batch and then sleep.

func (*Keyboard) InjectionBackend

func (k *Keyboard) InjectionBackend() KeyboardInjectionBackend

InjectionBackend returns the backend currently used for keyboard injection.

func (*Keyboard) Press

func (k *Keyboard) Press(ctx context.Context, key Key) error

Press injects a key-down event.

func (*Keyboard) Release

func (k *Keyboard) Release(ctx context.Context, key Key) error

Release injects a key-up event.

func (*Keyboard) ScanPress

func (k *Keyboard) ScanPress(ctx context.Context, scanCode uint16, extended bool) error

ScanPress injects a scan-code key-down event.

func (*Keyboard) ScanRelease

func (k *Keyboard) ScanRelease(ctx context.Context, scanCode uint16, extended bool) error

ScanRelease injects a scan-code key-up event.

func (*Keyboard) ScanTap

func (k *Keyboard) ScanTap(ctx context.Context, scanCode uint16, extended bool) error

ScanTap injects scan-code key-down and key-up events.

func (*Keyboard) State

func (k *Keyboard) State(ctx context.Context, key Key) (State, error)

State returns the current state of a key.

func (*Keyboard) Tap

func (k *Keyboard) Tap(ctx context.Context, key Key) error

Tap injects a key-down followed by a key-up event.

func (*Keyboard) TapWithHold

func (k *Keyboard) TapWithHold(ctx context.Context, key Key, hold time.Duration) error

TapWithHold injects a key-down event, waits for hold, and then injects a key-up event.

func (*Keyboard) TypeText

func (k *Keyboard) TypeText(ctx context.Context, text string) error

TypeText injects Unicode text. It does not depend on the active keyboard layout for the characters it can represent as UTF-16.

func (*Keyboard) TypeTextWithProfile

func (k *Keyboard) TypeTextWithProfile(ctx context.Context, text string, profile TypingProfile) error

TypeTextWithProfile injects Unicode text as per-rune events with profile timing between runes.

type KeyboardEvent

type KeyboardEvent struct {
	// Kind selects which payload the event carries.
	Kind KeyboardEventKind

	// Key is the virtual key payload for KeyboardEventKey.
	Key Key

	// ScanCode is the hardware scan-code payload for KeyboardEventScanCode.
	ScanCode uint16

	// State is the key state for key and scan-code events.
	State State

	// Extended marks scan-code events that use an extended key.
	Extended bool

	// Text is the Unicode text payload for KeyboardEventText.
	Text string

	// Duration is the delay for KeyboardEventPause.
	Duration time.Duration
}

KeyboardEvent is one keyboard operation in a batch. Pause events are interpreted by the Go layer and are not sent to the backend.

func ComboEvents

func ComboEvents(keys ...Key) []KeyboardEvent

ComboEvents presses keys in order and releases them in reverse order.

func KeyDownEvent

func KeyDownEvent(key Key) KeyboardEvent

KeyDownEvent creates a virtual-key down event.

func KeyEvent

func KeyEvent(key Key, state State) KeyboardEvent

KeyEvent creates a virtual-key event.

func KeyTapEvents

func KeyTapEvents(key Key) []KeyboardEvent

KeyTapEvents creates key-down and key-up events.

func KeyTapEventsWithHold

func KeyTapEventsWithHold(key Key, hold time.Duration) []KeyboardEvent

KeyTapEventsWithHold creates key-down and key-up events separated by a hold pause.

func KeyUpEvent

func KeyUpEvent(key Key) KeyboardEvent

KeyUpEvent creates a virtual-key up event.

func KeyboardPauseEvent

func KeyboardPauseEvent(duration time.Duration) KeyboardEvent

KeyboardPauseEvent creates a delay between event batches.

func ScanCodeEvent

func ScanCodeEvent(scanCode uint16, state State, extended bool) KeyboardEvent

ScanCodeEvent creates a layout-independent scan-code event.

func TextEvent

func TextEvent(text string) KeyboardEvent

TextEvent creates a Unicode text input event.

type KeyboardEventKind

type KeyboardEventKind uint8

KeyboardEventKind describes the operation represented by a KeyboardEvent.

const (
	KeyboardEventKey KeyboardEventKind = iota + 1
	KeyboardEventScanCode
	KeyboardEventText
	KeyboardEventPause
)

type KeyboardInjectionBackend

type KeyboardInjectionBackend uint8

KeyboardInjectionBackend selects the keyboard input injection primitive.

const (
	// KeyboardInjectionAuto selects the preferred backend for the current
	// platform. On Windows this prefers InjectKeyboardInput when user32 exports
	// it and falls back to SendInput otherwise. On macOS this selects CGEvent.
	// On Linux this selects uinput.
	KeyboardInjectionAuto KeyboardInjectionBackend = iota

	// KeyboardInjectionSendInput uses the documented Win32 SendInput API.
	KeyboardInjectionSendInput

	// KeyboardInjectionInjectKeyboardInput uses user32!InjectKeyboardInput when
	// the symbol is present. This backend is intentionally isolated because the
	// symbol is not available on every Windows build.
	KeyboardInjectionInjectKeyboardInput

	// KeyboardInjectionCGEvent uses CoreGraphics CGEvent APIs on macOS.
	KeyboardInjectionCGEvent

	// KeyboardInjectionUInput uses the Linux uinput kernel interface.
	KeyboardInjectionUInput
)

func (KeyboardInjectionBackend) String

func (b KeyboardInjectionBackend) String() string

type KeyboardInputEvent

type KeyboardInputEvent struct {
	// Key is the virtual key for keyboard events when known.
	Key Key

	// ScanCode is the hardware scan code reported by the backend when known.
	ScanCode uint16

	// State is the key state for key events.
	State State

	// Extended reports whether the backend marked the key as extended.
	Extended bool

	// AltDown reports whether Alt was down during the key event.
	AltDown bool
}

KeyboardInputEvent contains listened keyboard event data.

type LinuxRuntimeInfo

type LinuxRuntimeInfo struct {
	// UInput describes access to /dev/uinput.
	UInput RuntimeDeviceInfo

	// EvdevDevices is the number of readable /dev/input/event* devices found.
	EvdevDevices int

	// X11 describes the optional Xlib dependency.
	X11 RuntimeDependency

	// WaylandClient describes the optional Wayland client dependency.
	WaylandClient RuntimeDependency

	// LibEI describes the optional libei dependency.
	LibEI RuntimeDependency

	// LibOeffis describes the optional liboeffis dependency.
	LibOeffis RuntimeDependency

	// Portal describes session-bus signals used by desktop portal diagnostics.
	Portal RuntimePortalInfo
}

LinuxRuntimeInfo contains Linux-specific runtime diagnostics.

type ListenBackend

type ListenBackend uint8

ListenBackend selects the input listening primitive.

const (
	// ListenBackendAuto uses the platform default. On Windows this uses
	// low-level hooks. On Linux this uses evdev.
	ListenBackendAuto ListenBackend = iota

	// ListenBackendLowLevelHook uses WH_MOUSE_LL and WH_KEYBOARD_LL.
	ListenBackendLowLevelHook

	// ListenBackendRawInput uses RegisterRawInputDevices and WM_INPUT.
	ListenBackendRawInput

	// ListenBackendEvdev reads Linux /dev/input/event* devices.
	ListenBackendEvdev
)

func (ListenBackend) String

func (b ListenBackend) String() string

type ListenMask

type ListenMask uint8

ListenMask selects which input families should be listened to.

const (
	ListenMouse ListenMask = 1 << iota
	ListenKeyboard
	ListenAll = ListenMouse | ListenKeyboard
)

type ListenOptions

type ListenOptions struct {
	// Backend selects the listening primitive. Zero uses the platform default.
	Backend ListenBackend

	// Mask selects which input families should be emitted. Zero means ListenAll.
	Mask ListenMask

	// Buffer sets the Events channel capacity. Values less than one use the
	// package default.
	Buffer int

	// IncludeInjected includes events that the backend reports as injected.
	IncludeInjected bool

	// NormalizeOwnInjected clears injected markers from events tagged as this
	// client's own input before filtering.
	NormalizeOwnInjected bool
}

ListenOptions configures input listening.

type Listener

type Listener struct {
	// Events receives listened input events until the listener stops.
	Events <-chan InputEvent
	// contains filtered or unexported fields
}

Listener owns an active input listener.

func (*Listener) Close

func (l *Listener) Close()

Close requests listener shutdown.

func (*Listener) Wait

func (l *Listener) Wait() error

Wait blocks until the listener stops.

type Mouse

type Mouse struct {
	// contains filtered or unexported fields
}

Mouse exposes mouse state and injection operations.

func (*Mouse) Click

func (m *Mouse) Click(ctx context.Context, button MouseButton) error

Click injects a button-down followed by a button-up event.

func (*Mouse) ClickWithProfile

func (m *Mouse) ClickWithProfile(ctx context.Context, button MouseButton, profile ClickProfile) error

ClickWithProfile injects one or more clicks using explicit hold and between-click timing.

func (*Mouse) DoubleClick

func (m *Mouse) DoubleClick(ctx context.Context, button MouseButton, hold, interval time.Duration) error

DoubleClick injects two clicks with a fixed interval between them.

func (*Mouse) Down

func (m *Mouse) Down(ctx context.Context, button MouseButton) (bool, error)

Down reports whether a mouse button is currently pressed.

func (*Mouse) Drag

func (m *Mouse) Drag(ctx context.Context, button MouseButton, to Point, profile MovementProfile) error

Drag presses a button at the current cursor position, follows a deterministic path to the target point, and releases the button.

func (*Mouse) DragBy

func (m *Mouse) DragBy(ctx context.Context, button MouseButton, dx, dy int, profile MovementProfile) error

DragBy drags by a relative delta from the current cursor position.

func (*Mouse) DragFrom

func (m *Mouse) DragFrom(ctx context.Context, button MouseButton, from, to Point, profile MovementProfile) error

DragFrom moves to the start point, presses a button, follows a deterministic path to the end point, and releases the button.

func (*Mouse) HWheel

func (m *Mouse) HWheel(ctx context.Context, detents int) error

HWheel injects horizontal wheel movement in detents.

func (*Mouse) Inject

func (m *Mouse) Inject(ctx context.Context, events ...MouseEvent) error

Inject sends mouse events. Consecutive non-pause events are sent as one backend batch; pause events flush the current batch and then sleep.

func (*Mouse) InjectionBackend

func (m *Mouse) InjectionBackend() MouseInjectionBackend

InjectionBackend returns the backend currently used for mouse injection.

func (*Mouse) Move

func (m *Mouse) Move(ctx context.Context, move MouseMove) error

Move injects one mouse movement operation.

func (*Mouse) MoveBy

func (m *Mouse) MoveBy(ctx context.Context, dx, dy int) error

MoveBy moves the cursor by a relative delta.

func (*Mouse) MoveTo

func (m *Mouse) MoveTo(ctx context.Context, x, y int) error

MoveTo moves the cursor to an absolute screen coordinate.

func (*Mouse) MoveToProfile

func (m *Mouse) MoveToProfile(ctx context.Context, to Point, profile MovementProfile) error

MoveToProfile moves to a point using a deterministic movement profile.

func (*Mouse) Position

func (m *Mouse) Position(ctx context.Context) (Point, error)

Position returns the current cursor position.

func (*Mouse) Press

func (m *Mouse) Press(ctx context.Context, button MouseButton) error

Press injects a button-down event.

func (*Mouse) Release

func (m *Mouse) Release(ctx context.Context, button MouseButton) error

Release injects a button-up event.

func (*Mouse) ScreenSize

func (m *Mouse) ScreenSize(ctx context.Context) (Point, error)

ScreenSize returns the primary screen size in pixels.

func (*Mouse) State

func (m *Mouse) State(ctx context.Context, button MouseButton) (State, error)

State returns the current state of a mouse button.

func (*Mouse) Wheel

func (m *Mouse) Wheel(ctx context.Context, detents int) error

Wheel injects vertical wheel movement in detents.

type MouseButton

type MouseButton uint8

MouseButton identifies a physical mouse button.

const (
	ButtonLeft MouseButton = iota + 1
	ButtonRight
	ButtonMiddle
	ButtonX1
	ButtonX2

	// ButtonSide is kept as a readable alias for the first extended button.
	ButtonSide = ButtonX1
)

func ParseMouseButton

func ParseMouseButton(name string) (MouseButton, error)

ParseMouseButton returns a mouse button by its stable lower-case name.

func (MouseButton) String

func (b MouseButton) String() string

type MouseEvent

type MouseEvent struct {
	// Kind selects which payload the event carries.
	Kind MouseEventKind

	// Move is the movement payload for MouseEventMove.
	Move MouseMove

	// Button is the button payload for MouseEventButton.
	Button MouseButton

	// State is the button state for MouseEventButton.
	State State

	// Delta is the raw wheel delta for MouseEventWheel and MouseEventHWheel.
	Delta int

	// Duration is the delay for MouseEventPause.
	Duration time.Duration
}

MouseEvent is one mouse operation in a batch. Pause events are interpreted by the Go layer and are not sent to the backend.

func MouseButtonEvent

func MouseButtonEvent(button MouseButton, state State) MouseEvent

MouseButtonEvent creates a button state event.

func MouseHWheelEvent

func MouseHWheelEvent(delta int) MouseEvent

MouseHWheelEvent creates a horizontal wheel event. The delta is in raw Windows units; use WheelDelta for one wheel detent.

func MouseMoveEvent

func MouseMoveEvent(move MouseMove) MouseEvent

MouseMoveEvent creates a movement event.

func MousePauseEvent

func MousePauseEvent(duration time.Duration) MouseEvent

MousePauseEvent creates a delay between event batches.

func MouseWheelEvent

func MouseWheelEvent(delta int) MouseEvent

MouseWheelEvent creates a vertical wheel event. The delta is in raw Windows units; use WheelDelta for one wheel detent.

type MouseEventKind

type MouseEventKind uint8

MouseEventKind describes the operation represented by a MouseEvent.

const (
	MouseEventMove MouseEventKind = iota + 1
	MouseEventButton
	MouseEventWheel
	MouseEventHWheel
	MouseEventPause
)

type MouseInjectionBackend

type MouseInjectionBackend uint8

MouseInjectionBackend selects the mouse input injection primitive.

const (
	// MouseInjectionAuto selects the preferred backend for the current platform.
	// On Windows this prefers InjectMouseInput when user32 exports it and falls
	// back to SendInput otherwise. On macOS this selects CGEvent. On Linux this
	// selects uinput.
	MouseInjectionAuto MouseInjectionBackend = iota

	// MouseInjectionSendInput uses the documented Win32 SendInput API.
	MouseInjectionSendInput

	// MouseInjectionInjectMouseInput uses user32!InjectMouseInput when the
	// symbol is present. This backend is intentionally isolated because the
	// symbol is not available on every Windows build.
	MouseInjectionInjectMouseInput

	// MouseInjectionCGEvent uses CoreGraphics CGEvent APIs on macOS.
	MouseInjectionCGEvent

	// MouseInjectionUInput uses the Linux uinput kernel interface.
	MouseInjectionUInput
)

func (MouseInjectionBackend) String

func (b MouseInjectionBackend) String() string

type MouseInputEvent

type MouseInputEvent struct {
	// Position is the absolute cursor position reported with the event.
	Position Point

	// Move is the movement payload for mouse move events.
	Move MouseMove

	// Button is the button payload for button events.
	Button MouseButton

	// State is the button state for button events.
	State State

	// Delta is the raw wheel delta for wheel events.
	Delta int
}

MouseInputEvent contains listened mouse event data.

type MouseMove

type MouseMove struct {
	// X is either the absolute horizontal coordinate or relative delta.
	X int

	// Y is either the absolute vertical coordinate or relative delta.
	Y int

	// Relative reports whether X and Y are deltas instead of screen
	// coordinates.
	Relative bool
}

MouseMove describes one mouse movement operation.

func Abs

func Abs(x, y int) MouseMove

Abs moves the cursor to an absolute screen coordinate.

func Rel

func Rel(dx, dy int) MouseMove

Rel moves the cursor by a relative delta.

type MovementCurve

type MovementCurve uint8

MovementCurve selects the deterministic curve used by MovementProfile.

const (
	MovementLinear MovementCurve = iota
	MovementEaseInOut
	MovementNatural
)

type MovementProfile

type MovementProfile struct {
	// Steps is the number of move events to generate. Values less than one
	// become one step.
	Steps int

	// Duration is the total movement duration distributed between steps.
	Duration time.Duration

	// Curve selects how points are distributed along the path.
	Curve MovementCurve

	// Jitter is the maximum natural-path offset in pixels. Zero lets makc pick a
	// distance-based value for MovementNatural.
	Jitter int

	// Seed makes natural paths and pauses reproducible.
	Seed int64
}

MovementProfile describes a deterministic absolute cursor path.

func EaseInOutMovement

func EaseInOutMovement(steps int, duration time.Duration) MovementProfile

EaseInOutMovement creates a deterministic profile that accelerates and then decelerates along a straight path.

func LinearMovement

func LinearMovement(steps int, duration time.Duration) MovementProfile

LinearMovement creates a straight deterministic movement profile.

func NaturalMovement

func NaturalMovement(steps int, duration time.Duration, seed int64) MovementProfile

NaturalMovement creates a seeded Bezier movement profile with varied pauses. Pass a different seed when you want a different reproducible path.

func NaturalMovementWithJitter

func NaturalMovementWithJitter(steps int, duration time.Duration, jitter int, seed int64) MovementProfile

NaturalMovementWithJitter creates a seeded movement profile with an explicit maximum path jitter in pixels. A zero jitter lets makc choose a distance-based value.

func (MovementProfile) Events

func (p MovementProfile) Events(start, end Point) []MouseEvent

Events returns the movement events from start to end.

type Option

type Option func(*config)

Option configures a Client.

func WithInputTag

func WithInputTag(tag uintptr) Option

WithInputTag sets the backend tag used on injected inputs where supported. On Windows SendInput this maps to dwExtraInfo.

The default is a non-zero per-client tag. Passing 0 disables tagging and own-event detection.

func WithKeyboardInjection

func WithKeyboardInjection(backend KeyboardInjectionBackend) Option

WithKeyboardInjection selects the backend used by Keyboard injection.

func WithMouseInjection

func WithMouseInjection(backend MouseInjectionBackend) Option

WithMouseInjection selects the backend used by Mouse movement and button injection.

type Point

type Point struct {
	// X is the horizontal coordinate in pixels.
	X int

	// Y is the vertical coordinate in pixels.
	Y int
}

Point is a screen coordinate in pixels.

type RuntimeDependency

type RuntimeDependency struct {
	// Name is the library or service name that was probed.
	Name string

	// Available reports whether the dependency is available to this process.
	Available bool

	// Error contains a probe error message, if any.
	Error string
}

RuntimeDependency describes an optional runtime library or service.

type RuntimeDeviceInfo

type RuntimeDeviceInfo struct {
	// Path is the device path that was inspected.
	Path string

	// Exists reports whether the path exists.
	Exists bool

	// Readable reports whether the process can open the path for reading.
	Readable bool

	// Writable reports whether the process can open the path for writing.
	Writable bool

	// Error contains a probe error message, if any.
	Error string
}

RuntimeDeviceInfo describes a local device path used by an input backend.

type RuntimeInfo

type RuntimeInfo struct {
	// OS is the Go runtime operating system name.
	OS string

	// Arch is the Go runtime architecture name.
	Arch string

	// Display describes the active display/session environment.
	Display DisplayInfo

	// Linux contains Linux-specific diagnostics. It is zero-valued on other
	// platforms.
	Linux LinuxRuntimeInfo
}

RuntimeInfo describes the input-related runtime environment.

func InspectRuntime

func InspectRuntime() RuntimeInfo

InspectRuntime returns input-related diagnostics for the current process.

type RuntimePortalInfo

type RuntimePortalInfo struct {
	// SessionBusAddress is the DBUS_SESSION_BUS_ADDRESS value when available.
	SessionBusAddress string

	// SessionBus reports whether a session bus address is present.
	SessionBus bool

	// RemoteDesktopHint reports whether the environment suggests a remote
	// desktop portal may be available.
	RemoteDesktopHint bool
}

RuntimePortalInfo describes session-bus signals relevant to desktop portals.

type State

type State uint8

State describes whether a key or mouse button is up or down.

const (
	Up State = iota
	Down
)

func (State) Bool

func (s State) Bool() bool

func (State) String

func (s State) String() string

type TypingProfile

type TypingProfile struct {
	// Interval controls pauses between emitted rune events.
	Interval IntervalProfile
}

TypingProfile describes delays between per-rune text input events.

func FixedTyping

func FixedTyping(delay time.Duration) TypingProfile

FixedTyping creates a typing profile with one fixed delay between runes.

func VariableTyping

func VariableTyping(min, max time.Duration, seed int64) TypingProfile

VariableTyping creates a seeded typing profile with delays in [min, max].

func (TypingProfile) Events

func (p TypingProfile) Events(text string) []KeyboardEvent

Events returns per-rune text events with optional pauses between runes.

Directories

Path Synopsis
cmd
makc-smoke command
examples
keyboard command
mouse command
sequence command
pkg
types
Package types keeps compatibility aliases for the legacy API.
Package types keeps compatibility aliases for the legacy API.
types/buttons
Package buttons keeps compatibility aliases for the legacy API.
Package buttons keeps compatibility aliases for the legacy API.
types/keys
Package keys keeps compatibility aliases for the legacy API.
Package keys keeps compatibility aliases for the legacy API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL