# Pointer Shape (OSC 22)

> Change the shape of the mouse pointer while it is over the terminal.

- **Sequence:** `OSC 22 ; Pt ST`
- **ECMA-48:** [§8.3.89 OSC – Operating System Command, p. 51](https://archive.org/details/ecma-48-5th-edition-june-1991/page/n64/mode/1up)
- **Specification:** [Mouse pointer shapes, kitty documentation](https://sw.kovidgoyal.net/kitty/pointer-shapes/)
- **xterm:** [Operating System Commands](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)

`OSC 22 ; Pt ST` sets the shape of the mouse pointer over the terminal to
`Pt`, so that a program can show a hand over a link or a watch while it is
busy. It is xterm's, and xterm takes `Pt` as a name from the X cursor font:
`left_ptr` for an arrow, `hand2`, `watch`, `xterm` for the I-beam, and so on.
A name it does not know, or none at all, gives the I-beam, its default
([`xtermSetupPointer`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/misc.c#L991-L1029),
[`LookupCursorShape`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/misc.c#L891-L988)).

kitty extended it in its
[Mouse pointer shapes](https://sw.kovidgoyal.net/kitty/pointer-shapes/)
document, which uses the CSS cursor names (`default`, `pointer`, `wait`,
`text`, …) rather than X's, adds a stack, `OSC 22 ; > name ST` to push and
`OSC 22 ; < ST` to pop, and lets a program ask, with `OSC 22 ; ? names ST`,
which shapes the terminal has and which is current.

libghostty-vt keeps a shape its host can read and draw. It accepts the CSS
names, and the X names that have a CSS equivalent: `watch` is `wait`,
`left_ptr` is `default`, `xterm` is `text`. An empty `Pt` puts back `text`.
It ignores a name it does not know, rather than falling back as xterm does,
and it does not have kitty's stack or queries. The cases follow a default
xterm, and give each shape by its CSS name, which is what a case's
`pointer` line checks; the X cursors xterm sets are given by their CSS
equivalents. Those where a name is one only libghostty-vt knows, or neither
does, are known differences.

## Support

| Terminal | Version | Support | Notes |
| --- | --- | --- | --- |
| VT220 | [manual](https://vt100.net/docs/vt220-rm/), 2nd ed., 1984 | No | OSC is not among the controls it recognizes ([§4.2](https://vt100.net/docs/vt220-rm/chapter4.html#S4.2)) |
| VT510 | [manual](https://vt100.net/docs/vt510-rm/), 1st ed., 1993 | No | ignores an OSC string ([chapter 4](https://vt100.net/docs/vt510-rm/chapter4.html)) |
| xterm | patch 412, [xterm-411a](https://github.com/ThomasDickey/xterm-snapshots/tree/xterm-411a) | Yes | X cursor font names; an unknown name gives the I-beam |
| libghostty-vt | [0f171f6](https://github.com/ghostty-org/ghostty/tree/0f171f650a1cbbd6df4a2573b6b0c7de57cc6d90) | Partly | CSS names and X names with a CSS equivalent; ignores unknown names; no stack or queries (OSC22-5, OSC22-6) |
| Konsole | 26.11.70, [a24c3d71](https://invent.kde.org/utilities/konsole/-/tree/a24c3d71be3684f24d030aa2dbe9bd723d985233) | Partly | CSS names and Qt's, not X's `hand2` or `watch`; an unknown name resets it; no stack or queries |
| kitty | 0.49.2, its documentation | Yes | CSS names, a stack and queries: [Mouse pointer shapes](https://sw.kovidgoyal.net/kitty/pointer-shapes/) |
| ConEmu | build 230724, its [documentation](https://conemu.github.io/en/AnsiEscapeCodes.html) | No | not in its [ANSI escape codes](https://conemu.github.io/en/AnsiEscapeCodes.html) |

## Validation

### OSC22-1: The I-beam to begin with

Input, one step per line:

```text
A
```

Expected screen:

```text
|A_____|
pointer text
```

### OSC22-2: An X cursor name

Input, one step per line:

```text
\e]22;watch\e\\
```

Expected screen:

```text
|______|
pointer wait
```

### OSC22-3: Another

Input, one step per line:

```text
\e]22;left_ptr\e\\
```

Expected screen:

```text
|______|
pointer default
```

### OSC22-4: Empty puts back the I-beam

Input, one step per line:

```text
\e]22;watch\e\\
\e]22;\e\\
```

Expected screen:

```text
|______|
pointer text
```

### OSC22-5: A CSS name, which xterm does not know

*A known difference: libghostty-vt does not pass this case.*

Input, one step per line:

```text
\e]22;watch\e\\
\e]22;pointer\e\\  # not an X cursor: the I-beam
```

Expected screen:

```text
|______|
pointer text
```

### OSC22-6: A name neither knows

*A known difference: libghostty-vt does not pass this case.*

Input, one step per line:

```text
\e]22;watch\e\\
\e]22;nosuch\e\\
```

Expected screen:

```text
|______|
pointer text
```

### OSC22-7: kitty's query is not answered

Input, one step per line:

```text
\e]22;?__current__\e\\
```

Expected screen:

```text
|______|
reply none
```

---

This is the Markdown version of <https://control-codes.page/osc/pointer/>. On that page every validation case runs live in libghostty-vt, the terminal emulation core of Ghostty, compiled to WebAssembly.

The validation cases are written in the notation described in <https://control-codes.page/notation/index.html.md>.
