--- .title = "Pointer Shape (OSC 22)", .description = "Change the shape of the mouse pointer while it is over the terminal.", .date = .date("2026-10-02T00:00:00"), .layout = "sequence.shtml", .custom = { "mnemonic": "OSC 22", "name": "Change Pointer Shape", "sequence": "OSC 22 ; Pt ST", "ecma48": "8.3.89", "ecma48_name": "OSC – Operating System Command", "ecma48_page": 51, "xterm": "Operating System Commands", "spec": "https://sw.kovidgoyal.net/kitty/pointer-shapes/", "spec_name": "Mouse pointer shapes, kitty documentation", }, --- `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 | [919dca5](https://github.com/ghostty-org/ghostty/tree/919dca5c848d43a489a4cb2e1feade016f0fa2db) | 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 >[]($block.attrs("vt-case")) > ``` > A > ``` > ``` > |A_____| > pointer text > ``` ## OSC22-2: An X cursor name >[]($block.attrs("vt-case")) > ``` > \e]22;watch\e\\ > ``` > ``` > |______| > pointer wait > ``` ## OSC22-3: Another >[]($block.attrs("vt-case")) > ``` > \e]22;left_ptr\e\\ > ``` > ``` > |______| > pointer default > ``` ## OSC22-4: Empty puts back the I-beam >[]($block.attrs("vt-case")) > ``` > \e]22;watch\e\\ > \e]22;\e\\ > ``` > ``` > |______| > pointer text > ``` ## OSC22-5: A CSS name, which xterm does not know >[]($block.attrs("vt-case","known-difference")) > ``` > \e]22;watch\e\\ > \e]22;pointer\e\\ # not an X cursor: the I-beam > ``` > ``` > |______| > pointer text > ``` ## OSC22-6: A name neither knows >[]($block.attrs("vt-case","known-difference")) > ``` > \e]22;watch\e\\ > \e]22;nosuch\e\\ > ``` > ``` > |______| > pointer text > ``` ## OSC22-7: kitty's query is not answered >[]($block.attrs("vt-case")) > ``` > \e]22;?__current__\e\\ > ``` > ``` > |______| > reply none > ```