/dev/tty1..tty6, Alt+F1..F6 or `chvt N`. Each is an independent terminal --
its own screen, cursor, line discipline, foreground process group and
session. Two shells on two terminals are unrelated processes that share a
keyboard and a screen.
What made it 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 wants the screen: there was nowhere to put terminal 2 while
terminal 1 was visible, and nothing to repaint terminal 1 from. Each terminal
owns a grid of cells now and the framebuffer shows whichever grid is active,
so vt_switch() is set-index, repaint, return -- nothing is saved on the way
out because the outgoing grid has been current all along.
The rule the layer is built on: a terminal does not know whether it is
visible. Writes land in the grid, the cursor moves, the grid scrolls, all
unconditionally; drawing is one question asked at the bottom of vt.c. Every
alternative spreads "am I on screen" through the escape parser, and then a
terminal switched away from mid-sequence comes back wrong.
Everything that was a single global is per-terminal now: tty_fg_pgrp (deleted),
termios, the input ring, the escape parser, the UTF-8 decoder, and the cursor
-- which also stops being pixels in gfx.c and becomes cells in the terminal.
TIOCSCTTY was `return 0` and now records proc->ctty, which is what /dev/tty
resolves through; /dev/tty was an alias for /dev/console with the same minor,
and they are different questions.
kbd.c names Alt+F1..F6 as KEY_ALT_F1+n, above 0xFF on purpose -- every byte
value is already a character or a UTF-8 continuation, and a chord colliding
with one would be typed into the terminal instead of switching away from it.
What the chord means is decided in console.c, where the terminals are.
VT_ACTIVATE / VT_WAITACTIVE / VT_GETSTATE / VT_OPENQRY, Linux's numbers.
The argument to ACTIVATE is by value, not a pointer -- same shape as TCFLSH.
Two bugs found by screenshotting the result rather than reading the log:
- zsh emits 0x0F to leave alt-charset mode, because that is what the `linux`
terminfo says rmacs is, and the console drew CP437 glyph 0x0F: a sun, in
the prompt, on every line. A terminal never draws a C0 control character.
- SGR 7 was unimplemented, so zsh's partial-line marker rendered as a bare
'#' instead of a reversed block. Reverse video swaps the colours at draw
time, not at store time, so turning it off restores what was underneath.
Verified by screendump: tty1 and tty2 show different screens, and switching
back repaints tty1's earlier content from its grid. schfs-check passes.
docs/architecture/terminals.md, including what it does not do -- no
scrollback, no reflow, no VT_PROCESS mode for a display server to veto a
switch, which Schcom will need before it owns Alt+F7.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
83 lines
3.6 KiB
C
83 lines
3.6 KiB
C
#pragma once
|
|
|
|
// Drawing, and the text console under it, on whatever display is bound.
|
|
//
|
|
// This was kernel/dev/vga.c, and every consumer called it by that name:
|
|
// panic.c drew the crash screen with vga_fill_rect, the console printed with
|
|
// vga_putc, /dev/fb0 handed out vga_fb_phys(). None of that code has anything
|
|
// to do with a VGA -- it is bitmap drawing over a linear framebuffer, and the
|
|
// only VGA-specific part was where the framebuffer came from.
|
|
//
|
|
// So the driver pushes its scanout in here (gfx_attach_scanout) and everything
|
|
// above draws through these calls. A second display driver becomes a second
|
|
// caller of one function rather than an edit to the crash screen. This is the
|
|
// same arrangement as include/netdev.h, for the same reason.
|
|
//
|
|
// Not the same thing as the display class in include/display.h. That is the
|
|
// compositor's interface -- acquire a back buffer, present a rectangle, wait
|
|
// for vblank -- and it is about *frames*. This is the kernel's own drawing
|
|
// surface, used by the console and the crash screen, which do not double
|
|
// buffer and must keep working when nothing else in the system does.
|
|
|
|
// A linear framebuffer, exactly as the hardware presents it.
|
|
struct fb_info {
|
|
volatile uchar *ptr; // kernel-mapped base, or 0 if there is none
|
|
uint64 phys; // physical base, for mmap and for page tables
|
|
uint64 size; // bytes, stride * height rather than w * h * bpp
|
|
uint32 stride; // bytes per row, which may exceed width * bpp/8
|
|
uint32 bpp;
|
|
uint32 width, height;
|
|
};
|
|
|
|
// A driver publishing the framebuffer it owns. Called once, early: the console
|
|
// draws long before any bus is enumerated, so whoever owns the display the
|
|
// loader left running has to say so before then.
|
|
void gfx_attach_scanout(const struct fb_info *);
|
|
|
|
// The scanout as attached, for the few callers that need the memory itself --
|
|
// /dev/fb0 maps it, and fork copies the mapping. Returns 0 if none.
|
|
const struct fb_info *gfx_scanout(void);
|
|
|
|
int gfx_ready(void);
|
|
void gfx_dims(int *w, int *h);
|
|
uint32 gfx_width(void);
|
|
uint32 gfx_height(void);
|
|
uint64 gfx_fb_phys(void);
|
|
uint64 gfx_fb_size(void);
|
|
uint32 gfx_fb_stride(void);
|
|
uint32 gfx_fb_bpp(void);
|
|
volatile uchar *gfx_fb_ptr(void);
|
|
|
|
// Pixels and shapes.
|
|
void gfx_draw_pixel(int x, int y, uint32 color);
|
|
void gfx_clear(uint32 color);
|
|
void gfx_fill_rect(int x, int y, int w, int h, uint32 color);
|
|
void gfx_fill_circle(int cx, int cy, int r, uint32 color);
|
|
void gfx_draw_logo(int x, int y, uint32 color, uint32 bg);
|
|
void gfx_logo_centered(int cy, uint32 color, uint32 bg);
|
|
|
|
// The built-in 8x16 font: glyphs at a position, and text that leaves the
|
|
// cursor alone. The crash screen lays itself out rather than printing, which
|
|
// is what needs the second one.
|
|
void gfx_draw_char(int x, int y, uchar c, uint32 color);
|
|
void gfx_text_at(int x, int y, const char *s, uint32 color);
|
|
int gfx_char_w(void);
|
|
int gfx_char_h(void);
|
|
|
|
// The text console: a cursor, scrolling, and the erase operations an ANSI
|
|
// escape parser needs. See kernel/core/console.c.
|
|
void gfx_putc(int c, uint32 color);
|
|
void gfx_puts(int x, int y, char *s, uint32 color);
|
|
void gfx_set_bg(uint32 color);
|
|
void gfx_screen_reset(void);
|
|
int gfx_cols(void);
|
|
int gfx_rows(void);
|
|
|
|
// For the text layer in kernel/dev/vt.c: one cell with its own colours, and
|
|
// a screen scroll that leaves the cursor alone.
|
|
void gfx_draw_cell(int col, int row, uchar glyph, uint32 fg, uint32 bg);
|
|
void gfx_scroll_up(uint32 bg);
|
|
void gfx_cursor_get(int *col, int *row);
|
|
void gfx_cursor_set(int col, int row);
|
|
void gfx_erase_display(int mode);
|
|
void gfx_erase_line(int mode);
|