schcom: repaint what changed, and stop rendering frames nobody sees

Moving the mouse repainted the whole screen. At 1280x800 that is a
million pixels of desktop fill, every surface re-blitted, and a 4 MB copy
to the framebuffer -- per event. Under emulation the pointer lagged the
mouse by whole seconds, and that was not the emulation's fault: moving a
10x10 cursor does not require touching four megabytes.

Two changes, and the second matters as much as the first.

Damage tracking: one rectangle, unioned from whatever actually changed,
and only those rows are redrawn and copied out. A pointer move damages
where it was and where it is; a commit damages that surface; a client
going away damages what it had. One rectangle rather than a list, because
in practice the changes are a cursor and the window under it, and a list
costs more bookkeeping than it saves.

Draining input: a single mouse movement produces a burst of EV_REL ending
in EV_SYN, and the loop handled one event per poll and repainted. So the
compositor rendered every intermediate position of a cursor that was
never seen at those positions. Both queues are now drained before
anything is painted. handle_input grew a distinct "queue empty" return
for it -- EV_SYN changes nothing but is not the end of the burst, and a
drain that stopped there would leave the rest queued.

Also cuts the loader's resolution prompt from four seconds to one and a
half, on both the BIOS and UEFI paths. Four was mine and it was too long:
it is paid on every unattended boot, and adding four seconds to every
start to offer a menu nobody usually wants makes the common case worse to
serve the rare one.

schcom-test passes, including two overlapping clients and stacking order.
bootcheck 108 [ok].

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-08-10 05:54:06 +02:00
commit bf9ac843f7
4 changed files with 130 additions and 37 deletions

View file

@ -337,8 +337,10 @@ reqh:
# path, so whatever is chosen here is what the framebuffer, the console and the
# compositor all get.
#
# Four seconds, then it boots on its own. A loader that waits for a keypress is
# a machine that does not come back up after a power cut.
# A second and a half, then it boots on its own. This was four seconds and that
# was simply too long: it is paid on every unattended boot, and a loader that
# adds four seconds to every start to offer a menu nobody usually wants has made
# the common case worse to serve the rare one.
#
# In: CX = default mode. Out: CX = chosen mode. Clobbers AX, BX, DX, SI.
res_menu:
@ -346,9 +348,9 @@ res_menu:
movw $resprompt, %si
call print16
# Poll for ~4s. INT 1Ah tick count runs at 18.2 Hz, so 72 ticks is four
# seconds and needs no calibration against CPU speed -- a delay loop would be
# a different length on every machine.
# Poll for ~1.5s. INT 1Ah tick count runs at 18.2 Hz, so 27 ticks is a second
# and a half and needs no calibration against CPU speed -- a delay loop would
# be a different length on every machine.
movb $0x00, %ah
int $0x1A # CX:DX = ticks since midnight
movw %dx, %bx # BX = starting tick, low word
@ -358,7 +360,7 @@ res_menu:
movb $0x00, %ah
int $0x1A
subw %bx, %dx
cmpw $72, %dx
cmpw $27, %dx
jb .Lrwait
movw $restimeout, %si
call print16
@ -467,7 +469,7 @@ resmodes:
.word 0x0145 # 1280x1024 verified
resprompt:
.asciz "Press R within 4s to choose a resolution... "
.asciz "Press R for resolution... "
restimeout:
.asciz "keeping 800x600\r\n"
resmenu:

View file

@ -261,8 +261,10 @@ load_elf(EFI_FILE_PROTOCOL *file)
// and which index means what varies by machine. So this walks them, and the
// menu is built from what is actually there rather than from a table.
//
// Four seconds, then it boots on its own. A loader that waits for a keypress
// is a machine that does not come back up after a power cut.
// A second and a half, then it boots on its own. This was four seconds and that
// was too long: it is paid on every unattended boot, and adding four seconds to
// every start to offer a menu nobody usually wants makes the common case worse
// to serve the rare one.
static void
print_dec(UINT32 v)
@ -330,8 +332,8 @@ choose_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop)
{
UINT32 current = gop->Mode->Mode;
print(L"Press R within 4s to choose a resolution... ");
CHAR16 c = wait_key(4000);
print(L"Press R for resolution... ");
CHAR16 c = wait_key(1500);
if (c != L'r' && c != L'R') {
print(L"keeping ");
print_dec(gop->Mode->Info->HorizontalResolution); print(L"x");