input, fb, vt, sysfs: the kernel calls Xorg makes
Xorg on a framebuffer -- the fbdev video driver, the evdev input driver, libpciaccess -- asks the kernel a fixed set of questions before it draws anything, and gives up on the first wrong answer. These are those answers. evdev: EVIOCGVERSION, GID, GNAME, GPHYS, GUNIQ, GPROP, GBIT, GKEY, GABS, GREP and the accepted setters. Xorg and libinput decide what a device *is* from the capability bitmaps; /dev/input/eventN answered only EVIOCGRAB, which reads as no device at all. Each device's bitmaps are its defaults plus whatever it has reported, so a USB tablet makes the pointer absolute. fbdev: FBIOPUT_VSCREENINFO succeeds for the mode already set, as on Linux -- the fbdev driver sets it every time it starts -- and still refuses any other. FBIOPAN_DISPLAY to the origin; FBIOBLANK. The console: KDSETMODE(KD_GRAPHICS) stops it drawing on a terminal, KDSKBMODE(K_OFF) stops keys reaching its tty, and VT_PROCESS now does the switch handshake: leaving the terminal sends relsig and waits for VT_RELDISP, arriving sends acqsig. A terminal whose owner has died goes back to the kernel whole, so a crashed X cannot leave the console dead. /sys: boot_vga, a writable enable, and graphics/fb0 under the display device, which is how the fbdev driver pairs a card with /dev/fb0. ports/linux-headers: <linux/*.h> into musl's prefix, from sabotage- linux's kernel-headers 4.19.88-2. Its checksum is recorded from the first download; upstream publishes none. cmd/xready makes each call and checks the answer, including the switch handshake on tty2 and an owner that dies holding it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
132a2981b1
commit
b1323e193a
14 changed files with 860 additions and 32 deletions
129
kernel/dev/vt.c
129
kernel/dev/vt.c
|
|
@ -20,6 +20,9 @@
|
|||
#include "gfx/theme.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "dev/vt.h"
|
||||
#include "arch/mmu.h"
|
||||
#include "core/proc.h"
|
||||
#include "uapi/errno.h"
|
||||
|
||||
int vt_active = 0;
|
||||
|
||||
|
|
@ -54,10 +57,13 @@ cell_at(struct vt *v, int col, int row)
|
|||
return &v->cells[row * v->cols + col];
|
||||
}
|
||||
|
||||
// On screen, and the console's to draw. A terminal in KD_GRAPHICS belongs to
|
||||
// a display server: its grid still takes writes -- a kernel message lands in
|
||||
// it -- but nothing is drawn, or the text would be painted over the desktop.
|
||||
static int
|
||||
is_active(struct vt *v)
|
||||
{
|
||||
return vt_ready && v->index == vt_active;
|
||||
return vt_ready && v->index == vt_active && v->kdmode != KD_GRAPHICS;
|
||||
}
|
||||
|
||||
// Paint one cell from the grid. The single place that turns stored state into
|
||||
|
|
@ -164,6 +170,8 @@ vt_init(void)
|
|||
// pgid 1, which is init's, so the first shell on each terminal already
|
||||
// sees itself as the foreground job rather than stopping with SIGTTIN.
|
||||
v->fg_pgrp = 1;
|
||||
v->leaving_to = -1;
|
||||
v->kbmode = K_UNICODE;
|
||||
termios_defaults(&v->tio);
|
||||
for (int r = 0; r < rows; r++)
|
||||
blank_row(v, r);
|
||||
|
|
@ -826,6 +834,58 @@ vt_feed_1(struct vt *v, int c)
|
|||
// Switching
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// A terminal whose VT_PROCESS owner is gone goes back to the kernel, whole:
|
||||
// switching, drawing and the keyboard. Otherwise a display server that
|
||||
// crashed would leave the screen frozen on its last frame, the keyboard
|
||||
// deaf and the chord ignored, with no way back short of a reset.
|
||||
static int
|
||||
owner_alive(struct vt *v)
|
||||
{
|
||||
if (v->mode != VT_PROCESS)
|
||||
return 0;
|
||||
if (v->owner && proc_exists(v->owner))
|
||||
return 1;
|
||||
v->mode = VT_AUTO;
|
||||
v->owner = 0;
|
||||
v->leaving_to = -1;
|
||||
v->kbmode = K_UNICODE;
|
||||
if (v->kdmode == KD_GRAPHICS) {
|
||||
v->kdmode = KD_TEXT;
|
||||
if (v->index == vt_active) {
|
||||
vt_repaint(v);
|
||||
gfx_flush();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
switch_now(int i)
|
||||
{
|
||||
struct vt *to = &vts[i];
|
||||
|
||||
vt_active = i;
|
||||
|
||||
// Repaint from the grid. Nothing was saved on the way out because nothing
|
||||
// needed to be: the outgoing terminal's grid has been current all along,
|
||||
// which is the whole reason the grid exists. A terminal in graphics mode is
|
||||
// not repainted -- its owner is told, and draws its own.
|
||||
if (to->kdmode != KD_GRAPHICS) {
|
||||
vt_repaint(to);
|
||||
gfx_flush();
|
||||
}
|
||||
if (owner_alive(to) && to->acqsig)
|
||||
sigsend(to->owner, to->acqsig);
|
||||
}
|
||||
|
||||
// Switch terminals.
|
||||
//
|
||||
// Away from one in VT_PROCESS, the switch is a request: its owner is sent
|
||||
// relsig and the switch waits for VT_RELDISP. That is how a display server
|
||||
// learns it is about to lose the screen -- it stops drawing, and only then
|
||||
// says yes. Switching without asking left it drawing straight over the text
|
||||
// console it had been switched away from. Back to such a terminal, the
|
||||
// switch happens and the owner is sent acqsig, to redraw.
|
||||
int
|
||||
vt_switch(int i)
|
||||
{
|
||||
|
|
@ -836,16 +896,67 @@ vt_switch(int i)
|
|||
if (i == vt_active)
|
||||
return 0;
|
||||
|
||||
vt_active = i;
|
||||
|
||||
// Repaint from the grid. Nothing was saved on the way out because nothing
|
||||
// needed to be: the outgoing terminal's grid has been current all along,
|
||||
// which is the whole reason the grid exists.
|
||||
vt_repaint(&vts[i]);
|
||||
gfx_flush();
|
||||
struct vt *from = &vts[vt_active];
|
||||
if (owner_alive(from) && from->relsig) {
|
||||
from->leaving_to = i;
|
||||
if (sigsend(from->owner, from->relsig) == 0)
|
||||
return 0;
|
||||
owner_alive(from); // died in between; fall through
|
||||
}
|
||||
switch_now(i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
vt_reldisp(struct vt *v, int arg)
|
||||
{
|
||||
if (arg == VT_ACKACQ)
|
||||
return 0;
|
||||
if (v->mode != VT_PROCESS || v->leaving_to < 0)
|
||||
return -EINVAL;
|
||||
int to = v->leaving_to;
|
||||
v->leaving_to = -1;
|
||||
if (arg && v->index == vt_active)
|
||||
switch_now(to);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
vt_set_process(struct vt *v, int mode, int relsig, int acqsig)
|
||||
{
|
||||
v->mode = mode;
|
||||
v->leaving_to = -1;
|
||||
if (mode == VT_PROCESS) {
|
||||
v->owner = myproc() ? myproc()->pid : 0;
|
||||
v->relsig = relsig > 0 && relsig < 32 ? relsig : 0;
|
||||
v->acqsig = acqsig > 0 && acqsig < 32 ? acqsig : 0;
|
||||
} else {
|
||||
v->owner = v->relsig = v->acqsig = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vt_set_kdmode(struct vt *v, int mode)
|
||||
{
|
||||
int was = v->kdmode;
|
||||
v->kdmode = mode;
|
||||
// Back to text on the terminal on screen: the grid has been kept current
|
||||
// underneath the whole time, so putting it back is a repaint.
|
||||
if (was == KD_GRAPHICS && mode == KD_TEXT && v->index == vt_active) {
|
||||
vt_repaint(v);
|
||||
gfx_flush();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
vt_takes_keys(struct vt *v)
|
||||
{
|
||||
if (v->kbmode == K_XLATE || v->kbmode == K_UNICODE)
|
||||
return 1;
|
||||
// A raw or K_OFF terminal whose owner died gets its keyboard back.
|
||||
return v->mode == VT_PROCESS && !owner_alive(v);
|
||||
}
|
||||
|
||||
int
|
||||
vt_index(struct vt *v)
|
||||
{
|
||||
|
|
@ -870,7 +981,7 @@ vt_index(struct vt *v)
|
|||
int
|
||||
vt_kernel_switches(void)
|
||||
{
|
||||
return vt_current()->mode != VT_PROCESS;
|
||||
return !owner_alive(vt_current());
|
||||
}
|
||||
|
||||
// Step to the previous or next terminal that is actually in use.
|
||||
|
|
|
|||
Loading…
Reference in a new issue