---
title: Input
description: Mouse, keyboard, clipboard, and the single lease that governs all three.
---

## Coordinates

Every coordinate is in the pixel space of the machine's **current** display, and one outside it is
**rejected, not clamped**: a click 200 pixels off the edge is a bug worth surfacing, not one to
land somewhere plausible. Nothing is scaled for you.

[Screenshot](/docs/sdk/screen) first, read the coordinates off the frame, then click.

## Mouse

<CodeGroup>

```ts TypeScript
await machine.mouse.move(100, 200);

await machine.mouse.click({ at: { x: 640, y: 400 } });
await machine.mouse.click({ at: { x: 640, y: 400 }, button: "right" });
await machine.mouse.click(); // wherever the pointer already is
await machine.mouse.click({ at: p, down: true }); // hold across several calls

await machine.mouse.doubleClick({ x: 640, y: 400 }); // `at` is positional here
await machine.mouse.rightClick({ x: 640, y: 400 });

await machine.mouse.drag({ from: { x: 10, y: 10 }, to: { x: 200, y: 200 } });
await machine.mouse.scroll({ direction: "down", amount: 3, at: { x: 640, y: 400 } });
```

```python Python
machine.mouse.move(100, 200)

machine.mouse.click(at={"x": 640, "y": 400})
machine.mouse.click(at={"x": 640, "y": 400}, button="right")
machine.mouse.click()                                   # wherever the pointer already is
machine.mouse.click(at=p, down=True)                    # hold across several calls

machine.mouse.double_click({"x": 640, "y": 400})        # `at` is positional here
machine.mouse.right_click({"x": 640, "y": 400})

machine.mouse.drag(to={"x": 200, "y": 200}, from_={"x": 10, "y": 10})
machine.mouse.scroll(direction="down", amount=3, at={"x": 640, "y": 400})
```

</CodeGroup>

:::note
`click` takes the point under an `at` key; `doubleClick` and `rightClick` take a bare point
positionally. In Python, `drag`'s starting point is `from_` — `from` is a reserved word, and the
wire field it maps to is still `from`.
:::

Buttons are `left`, `middle` and `right`. `count: 2` is what `doubleClick` sends. `drag` takes an
optional `steps` to shape the path; `scroll` defaults to `amount: 3` and moves the pointer first
when you give it an `at`.

There is **no read-only pointer query**. Moving the pointer to find out where it is defeats the
question, which is why the [Anthropic adapter](/docs/integrations/anthropic#two-members-are-withheld)
withholds `cursor_position` rather than declaring a tool that cannot work.

## Keyboard

<CodeGroup>

```ts TypeScript
await machine.keyboard.type("hello");
await machine.keyboard.type("slowly", { delayMs: 40 });
await machine.keyboard.press("Return");
await machine.keyboard.hotkey("ctrl", "l");
```

```python Python
machine.keyboard.type("hello")
machine.keyboard.type("slowly", delay=0.04)
machine.keyboard.press("Return")
machine.keyboard.hotkey("ctrl", "l")
```

</CodeGroup>

`type` is for text and `press` is for keys. Typing `"Return"` types six characters; pressing it
sends the key. Key names are the guest's own — `Return`, `Tab`, `Escape`, `F6`, `BackSpace`.

## Clipboard

<CodeGroup>

```ts TypeScript
await machine.clipboard.write("hello");
await machine.clipboard.write("hello", { selection: "primary" });

const { text, selection } = await machine.clipboard.read();
```

```python Python
machine.clipboard.write("hello")
machine.clipboard.write("hello", selection="primary")

clip = machine.clipboard.read()
clip["text"], clip["selection"]
```

</CodeGroup>

X11 has two selections and both are addressable: `clipboard` (the default, what Ctrl-V pastes)
and `primary` (what a middle click pastes). **Reading is a read**, so it needs no lease.

## The input lease

At most one session may send input to a machine at a time. Anything that types, clicks, drags,
scrolls, resizes the display or [opens a URL](/docs/sdk/browser) acquires the lease first. Reads
never do.

Both SDKs handle it for you:

<CodeGroup>

```ts TypeScript
await machine.keyboard.type("hello"); // acquires the lease if it does not hold one
machine.holdsInput; // true
```

```python Python
machine.keyboard.type("hello")          # acquires the lease if it does not hold one
machine.holds_input                     # True
```

</CodeGroup>

The lease is opened on first use with a 60-second TTL and renewed at half of it. `machine.close()`
releases it.

### A lost lease is never taken back

If a renewal fails, the SDK stops holding the lease and **does not re-acquire it**:

<CodeGroup>

```ts TypeScript
machine.holdsInput; // false after a takeover
await machine.keyboard.type("more");
// RasterError: conflict — someone else holds the input lease
```

```python Python
machine.holds_input   # False after a takeover
machine.keyboard.type("more")
# RasterError: conflict - someone else holds the input lease
```

</CodeGroup>

That is the important half. A lost lease means a person took over, and silently taking it back
would put an agent and a person on the same keyboard, which is the one thing single-holder input
exists to prevent. Catch the `conflict` and decide deliberately.

The SDKs never send `force`. Taking a lease from its holder is human takeover — a deliberate act
by a person, not something a library does on a click. To do it on purpose, see
[take input control](/docs/how-to/take-input-control).

### Not everything is leased

[Terminals](/docs/sdk/terminal) are outside the lease entirely, and so are
[screen streams](/docs/sdk/streams). A viewer that cannot type can still see.
