// SPDX-License-Identifier: Apache-2.0 // // Virtual terminals. // // Until this existed there was one console. `/dev/tty` was an alias for // `/dev/console` with the same major and minor, `tty_fg_pgrp` was a single // global int, and TIOCSCTTY was `return 0`. One screen, one foreground // process group, one line discipline, no way to have a second shell. // // The thing that makes switching possible is a backing store. The console // drew straight into the framebuffer and scrolled by moving pixels, which // works exactly as long as nothing else ever wants the screen: there was // nowhere to put terminal 2 while terminal 1 was visible, and nothing to // repaint terminal 1 from when it came back. So each VT owns a grid of cells // and the framebuffer shows whichever grid is active. // // A VT that is not active is not a special case. Writes to it land in its // grid, its cursor moves, it scrolls; the only thing it does not do is draw. // That is the whole switching mechanism: vt_switch repaints, and everything // else is written as though it were always on screen. #ifndef VT_H #define VT_H #include "types.h" #include "param.h" #include "posix.h" // Six, and Linux's numbering: /dev/tty1 through /dev/tty6, reached with // Alt+F1..Alt+F6. Alt+F7 is deliberately left alone -- that is where a // display server goes, and Schcom will want it. #define NVT 6 // A cell. // // `ch` is the glyph the font draws, not a codepoint: the console maps UTF-8 // to CP437 before it gets here, because that is what include/font.h contains. // Storing the codepoint would mean re-running that mapping on every repaint // and getting a different answer for anything the font cannot draw. // // The colours are palette indices rather than RGB. Sixteen ANSI colours plus // the two theme defaults fit in a byte each and make a cell four bytes; the // same cell holding two 32-bit colours would be twelve, and the grids are // static. struct vtcell { uint8 ch; uint8 fg; uint8 bg; uint8 attr; // reserved: bold/underline/reverse, which do not change }; // the glyph and so can be added without touching a grid #define VT_FG_DEFAULT 16 // SCH_FG from include/theme.h #define VT_BG_DEFAULT 17 // SCH_BG #define VT_NCOLOUR 18 // Cell attributes that do not change which glyph is drawn, only how. #define VT_ATTR_REVERSE 0x01 // The largest console a grid can hold, which is 1920x1200 with the 8x16 font. // A framebuffer wider or taller than this still works -- vt_init clamps the // grid and the rest of the screen stays background -- but it is worth raising // rather than living with, because the clamp is invisible until someone // notices the bottom of their screen never has text on it. #define VT_MAXCOLS 240 #define VT_MAXROWS 75 #define VT_INPUT_BUF 256 #define VT_MAXPARM 8 // Escape parser states. E_OSC and E_OSC_ESC exist for hyperlinks: a terminal // that does not follow OSC 8 must still consume it, or the URL is printed as // text. See the comment in kernel/core/console.c that this came from. enum { VE_NORMAL, VE_ESC, VE_CSI, VE_OSC, VE_OSC_ESC }; struct vt { int index; // 0-based; /dev/tty int cols, rows; struct vtcell *cells; // cols*rows, row-major, into vt_storage int cx, cy; // cursor, in cells // Deferred wrap, the rule every real terminal follows and none of them // document well: a glyph printed in the last column does NOT move the // cursor to the next line. It sets this instead, and the wrap happens when // the *next* glyph arrives. Anything that moves the cursor deliberately -- // CR, LF, backspace, an escape sequence -- clears it. int wrap_pending; int saved_cx, saved_cy; // DECSC/DECRC uint8 fg, bg; // current SGR colours int bold; int attr; // VT_ATTR_* applied to new cells // Escape parser. int esc_state; int esc_parm[VT_MAXPARM]; int esc_nparm; int esc_private; // UTF-8 decoder. uint32 u8_cp; int u8_need; // Line discipline. One per terminal, because ICANON and ECHO are properties // of a terminal and not of the machine. struct termios tio; struct { char buf[VT_INPUT_BUF]; uint r, w, e; uint abort_read; uint eof; } in; // Job control. This was one global for the whole system; a terminal each // is the entire point of having more than one. int fg_pgrp; int sid; // session that owns this terminal, 0 = none int opened; // times it has been opened, for /dev/tty }; // The active terminal, and the one the framebuffer is showing. extern int vt_active; void vt_init(void); void vt_resize(void); // a scanout arrived; re-measure struct vt *vt_get(int i); // 0-based, clamped, never null struct vt *vt_current(void); int vt_switch(int i); // 0-based; repaints. -1 if out of range // Output, per terminal. Drawing happens only if `v` is the active one. void vt_feed(struct vt *v, int c); // one output byte, through the parser void vt_putc(struct vt *v, int glyph); // one glyph, already mapped void vt_cursor_set(struct vt *v, int col, int row); void vt_cursor_get(struct vt *v, int *col, int *row); void vt_erase_display(struct vt *v, int mode); void vt_erase_line(struct vt *v, int mode); void vt_clear(struct vt *v); void vt_scroll(struct vt *v); void vt_repaint(struct vt *v); // grid -> framebuffer, if active uint32 vt_rgb(uint8 idx); // palette index -> RGB int vt_index(struct vt *v); // 0-based number of a terminal #endif // VT_H