saneterm

Modern line-oriented terminal emulator without support for TUIs

git clone https://git.8pit.net/saneterm.git

 1# Design
 2
 3This document describe internal implementation details of `saneterm`.
 4
 5## Input Handling
 6
 7Input is handled line-wise, i.e. data is not written to the PTY until
 8the user types a newline character. This allows implementing more
 9advanced line editing features (e.g. readline-like keybindings) in
10`saneterm`. This line-based design is inspired by [`9term`][9term man page].
11Similar to `9term`, it also requires `saneterm` to disable local echo
12via `termios(3)`. Applications forcing local echo (e.g. `ssh(1)`) are
13currently not supported.
14
15The line-based input forms a contiguous document collected in a
16[Gtk TextBuffer][gtk textbuffer]. Text can be edited anywhere in the
17buffer. In order to determine which changes should be send to the PTY,
18`saneterm` records the last output point of the child process. Only text
19entered beyond this output point is written to the PTY.
20
21## Control Codes
22
23In the Unix world, terminal emulators are usually [character-orientated][char terms].
24That is, each typed character is written directly to the PTY. That
25includes control characters like backspace, ctrl+z, ctrl+c, et cetera.
26The current line discipline settings determine how these characters are
27supposed to be interpreted. For example, `^Z` (ctrl+z) causes the line
28discipline to send a `SIGTSTP` signal by default. Details of the TTY
29subsystem are also further described in an [article by Linus Ã…kesson][tty demystified].
30
31In the line-based context there are two possible approaches regarding
32the handling of these [control characters][wikipedia c0 and c1]:
33
341. The corresponding ASCII code for the control character can be
35   *buffered* in the line buffer. Essentially, it is treated as a normal
36   character and send to the program when the user enters a newline.
37   This is the approach employed by 9term.
382. Special handling for control characters could be added to the
39   terminal emulator itself by *intercepting* key bindings directly. For
40   instance, ctrl+c could be hardcoded to always send the interrupt
41   control code. This would allow bypassing the line-based buffer and
42   sending control codes to the PTY directly.
43
44Presently, `saneterm` implements the latter approach. That is, custom
45[Gtk signals][gtk signals] are defined for control commands, e.g.
46`interrupt` for ctrl+c. These signals are then bound to pre-defined key
47combinations, i.e. the `interrupt` signal is bound to `ctrl+c`. The
48signal handler for the `interrupt` signal then determines the current
49ASCII control character for `VINTR` using `termios(3)` and sends this
50character to the PTY. At the moment, these key bindings are ignored if
51the cursor is not at the buffer position where the next character would
52be entered.
53
54### Buffering
55
56Line-based buffer of control characters, as done by `9term`, is also not
57trivial to implement. It requires translating Gtk key events to ASCII
58control characters and a printable representation of each control
59character for the [Gtk TextView][gtk textview] used by `saneterm`.
60Special care also needs to be taken to ensure that this printable
61representation behaves like a single character. For instance, if the
62printable representation for ctrl+z (`0x1a`) is `^Z`, a standard Gtk
63`backspace` signal must remove the entire thing (i.e. the `^` and the
64`Z` character) and not just the `Z` character.
65
66### Intercepting
67
68This approach seems more intuitiv in the Unix world. For instance, to
69send a `SIGTSTP` signal one just has to press ctrl+z (as one would in a
70character-orientated terminal) instead of pressing ctrl+z and then
71enter. It does also have some caveats as keycodes are normally
72configured using `termios(3)`. As an example, it possible to bind
73`SIGINT` to a different keycode using `stty intr <keycode>` but since
74`saneterm` keybindings are defined separately it would not respect that
75setting. For the same reason, it is also difficult to support
76noncanoical mode as defined in `termios(3)`.
77
78The `saneterm` handlers also need to query the `termios(3)` setting on
79each Gtk signal to determine the current control character, which should
80be send to the PTY, using `termios(3)`.  Additionally, the line buffer
81is bypassed on these signals and any data presently stored in it is
82never received by the application. In this regarding `VEOF` (ctrl+d) is
83handled in a special way as it also causes the current line buffer to be
84written to the PTY.
85
86[9term man page]: https://9fans.github.io/plan9port/man/man1/9term.html
87[gtk textbuffer]: https://developer.gnome.org/gtk3/stable/GtkTextBuffer.html
88[tty demystified]: https://www.linusakesson.net/programming/tty/
89[wikipedia c0 and c1]: https://en.wikipedia.org/wiki/C0_and_C1_control_codes
90[gtk signals]: https://developer.gnome.org/gtk-tutorial/stable/x159.html
91[gtk textview]: https://developer.gnome.org/gtk3/stable/GtkTextView.html
92[char terms]: https://en.wikipedia.org/wiki/Computer_terminal#Character-oriented_terminal