/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
116 lines
5.3 KiB
Shell
116 lines
5.3 KiB
Shell
# /etc/zsh/zshrc — the system-wide interactive zsh configuration for SchOS.
|
||
#
|
||
# Deliberately short. This sets up the things an interactive shell is useless
|
||
# without -- history, completion, and the keys a console sends -- and stops.
|
||
# Anything beyond that belongs in a user's own ~/.zshrc, which is the file zsh
|
||
# reads after this one.
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# History
|
||
# ---------------------------------------------------------------------------
|
||
HISTFILE=${HOME:-/root}/.zsh_history
|
||
HISTSIZE=2000
|
||
SAVEHIST=2000
|
||
setopt APPEND_HISTORY INC_APPEND_HISTORY SHARE_HISTORY
|
||
setopt HIST_IGNORE_DUPS HIST_IGNORE_SPACE HIST_REDUCE_BLANKS
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Behaviour
|
||
# ---------------------------------------------------------------------------
|
||
setopt AUTO_CD # `cd /tmp` is implied by typing `/tmp`
|
||
setopt EXTENDED_GLOB
|
||
setopt INTERACTIVE_COMMENTS # a # in an interactive line is a comment
|
||
setopt NO_BEEP # SchOS has no audio, but it would also scroll
|
||
unsetopt FLOW_CONTROL # ^S/^Q are worth more as keys than as flow control
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Keys
|
||
# ---------------------------------------------------------------------------
|
||
# Emacs bindings, because that is what every other line editor on the system
|
||
# does -- mksh's default and the kernel's own canonical-mode editing both.
|
||
bindkey -e
|
||
|
||
# Home, End, Delete and the arrows, taken from terminfo rather than written
|
||
# out as escape sequences. The sequences differ between the SchOS console and
|
||
# a serial terminal; the terminal's own description does not.
|
||
typeset -A _sch_keys=(
|
||
"${terminfo[khome]}" beginning-of-line
|
||
"${terminfo[kend]}" end-of-line
|
||
"${terminfo[kdch1]}" delete-char
|
||
"${terminfo[kich1]}" overwrite-mode
|
||
"${terminfo[kcuu1]}" up-line-or-search
|
||
"${terminfo[kcud1]}" down-line-or-search
|
||
"${terminfo[kcub1]}" backward-char
|
||
"${terminfo[kcuf1]}" forward-char
|
||
)
|
||
for _k _w in "${(@kv)_sch_keys}"; do
|
||
[[ -n $_k ]] && bindkey -- "$_k" "$_w"
|
||
done
|
||
unset _sch_keys _k _w
|
||
|
||
# A terminal that has just been resized, or that zsh has just taken over, may
|
||
# not have told terminfo yet. This puts the keypad in the mode the escape
|
||
# sequences above were described for, and takes it back out on exit.
|
||
if (( ${+terminfo[smkx]} && ${+terminfo[rmkx]} )); then
|
||
autoload -Uz add-zle-hook-widget 2>/dev/null
|
||
function _sch_zle_init { echoti smkx }
|
||
function _sch_zle_line_finish { echoti rmkx }
|
||
zle -N zle-line-init _sch_zle_init
|
||
zle -N zle-line-finish _sch_zle_line_finish
|
||
fi
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Completion
|
||
# ---------------------------------------------------------------------------
|
||
# Completion is NOT initialised here yet, and the reason is a filesystem
|
||
# measurement rather than anything to do with zsh.
|
||
#
|
||
# A small write to SchFS costs about 58 ms, because every operation is its own
|
||
# transaction group and a commit is two cache flushes and an uberblock write --
|
||
# see the comment at the top of kernel/fs/schfs/kmount.c, which describes the
|
||
# trade-off exactly. Measured on this machine, 200 writes:
|
||
#
|
||
# to one open file on SchFS 11.75 s (58 ms each)
|
||
# open + write + close each 21.39 s (107 ms each)
|
||
# to /dev/null 0.14 s (0.7 ms each)
|
||
#
|
||
# compdump writes the digest with a few thousand separate `print` calls, so
|
||
# compinit -d takes minutes and looks exactly like a hang. compinit itself is
|
||
# fine: with -D, which skips the dump, it finishes in 3.6 seconds.
|
||
#
|
||
# So this waits for deferred commits. When small writes stop costing a disk
|
||
# barrier each, delete this comment and uncomment the three lines below.
|
||
#
|
||
# autoload -Uz compinit
|
||
# compinit -u -d ${HOME:-/root}/.zcompdump
|
||
#
|
||
# Until then, completion is available on demand in a shell you do not mind
|
||
# waiting three seconds for:
|
||
#
|
||
# autoload -Uz compinit && compinit -D -u
|
||
#
|
||
# -u is not optional whenever this is turned back on. Without it compinit runs
|
||
# compaudit, decides the function directories are "insecure" because SchOS has
|
||
# no ownership model to check, and *prompts* for an answer on a shell that is
|
||
# still starting up -- which is a real hang, unlike the one above.
|
||
|
||
# These take effect as soon as compinit is turned on above; a zstyle with no
|
||
# completion system to read it is inert, not an error.
|
||
zstyle ':completion:*' menu select
|
||
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}' # case-insensitive
|
||
zstyle ':completion:*' list-colors ''
|
||
zstyle ':completion:*:descriptions' format '%F{244}%d%f'
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Prompt
|
||
# ---------------------------------------------------------------------------
|
||
# The accent violet from include/theme.h, which is 99 in the 256-colour cube
|
||
# and falls back to magenta on the 8-colour console. The last-status marker is
|
||
# the only thing here that ever turns red -- same rule as the rest of the
|
||
# system: normal states are calm, only failure is loud.
|
||
setopt PROMPT_SUBST
|
||
# The chevron is », not ❯. The console font is CP437 and has a glyph for
|
||
# the first and none for the second, so ❯ renders as '?' -- correctly, since
|
||
# drawing a *different* character would be worse, but it still looks broken.
|
||
PROMPT='%F{magenta}%B%n@schos%b%f %F{cyan}%~%f %(?.%F{green}».%F{red}»)%f '
|
||
RPROMPT=''
|