The cursed cursor

One of the oddities of terminal emulators, compared to modern applications, is their different philosophy of the cursor. In terminal emulators, the cursor stands over a character. During decades of evolvement of user-friendly software, the world has pretty much settled on the cursor being a thin vertical line between characters.

The BiDi algorithm shuffles the characters around, but doesn’t provide a mapping for the boundaries between them. So the legacy philosophy of the cursor actually comes in handy for BiDi: the cursor can just follow the character it belongs to.

Many terminal emulators offer an I-beam shaped cursor placed at the left side of its cell. In most applications it reasonably correctly emulates the behavior of modern graphical cursors (there are some minor differences, e.g. an I-beam still cannot be placed after the last column).

At the very least, for symmetry reasons, this needs to become the right side in explicit RTL mode, and in implicit mode when the character underneath has resolved RTL directionality.

In VTE’s current implementation, if an implicit paragraph has at least one foreign directionality character, the I-beam cursor slightly modifies its shape to show the directionality of the character underneath, similarly to Qt. It looks like for LTR characters and for RTL ones. We might extend this behavior to the rectangle and underline cursors too, subject to a good visual design. Since it’s a tiny display-only addition not affecting the placement of the I-beam, we haven’t introduced escape sequences to control this behavior.

See later for a few ideas on different cursor placement strategies as future extensions.

The “about to wrap” state

Assuming 80 columns numbered from 1 to 80, when a letter is printed in the last column, the following happens:

In xterm and many other emulators, the cursor stays in the 80th column (both logically and visually), and a special flag is set so that receiving the next letter will first wrap to the next line. If, let’s say, the cursor is moved upwards instead, this flag is cleared.

In VTE, instead of this special bit, the cursor goes to column 81 (not visible on the screen). If, let’s say, a cursor moving escape sequence is received, the cursor is first confined to the visible area (to column 80). If the cursor position is queried, the reported value is confined.

These two approaches result in the same emulation behavior (apart from possible implementation bugs), it’s just the visual representation of the cursor that’s different, plus the internal logic.

With xterm’s cursor wrapping model, if the cursor follows the letter underneath (the obvious behavior) and a user types something in cooked mode, the cursor is normally after the last visual letter (even when typing BiDi). However, when the line gets fully filled up, it might suddenly jump somewhere to the middle of the line (over the most recently typed character). This would look quite silly. It’s more consistent to make it disappear (it just continues walking and walks out of view), which behavior matches VTE’s cursor wrapping model.

This whole hack is a technical necessity, since (at least with block and underline cursor shapes, or with hardware terminals) the cursor cannot be displayed beyond the last character. (With I-beam, it actually could.) I’d much rather think of it as a necessary hack in the view rather than in the model, and VTE’s current approach is closer to this (even if some possible subsequent escape sequences unfortunately require to confine the cursor in the model, too).

See also VTE #17.