From samuel.thibault at ens-lyon.org Sun Oct 4 14:09:44 2026 From: samuel.thibault at ens-lyon.org (Samuel Thibault) Date: Sun, 4 Oct 2026 16:09:44 +0200 Subject: [BRLTTY] Linux console hacking (was: Re: Footsteps towards better accessibility in Linux) In-Reply-To: References: <87msb422fk.fsf@sange.fi> <1p6pn777-2r6s-982s-20s3-48r6srp380s0@syhkavp.arg> <68o223pn-q9qp-6or4-3326-3q8n4p8s3527@syhkavp.arg> <51pn63n3-9149-pqpq-2374-892rn49rs040@syhkavp.arg> Message-ID: Hello, We have investigated the alternative screen spurious speech question. Putting some quotes below for reminding the context: Nicolas Pitre, le lun. 30 mars 2026 10:10:25 -0400, a ecrit: > On Sun, 29 Mar 2026, S?bastien Hinderer wrote: > > Nicolas Pitre (2026/03/28 20:41 -0400): > > > The alternate screen is not cleared "at program exit". It doesn't know > > > if a program exited or not. > > > > > > What happens here is: > > > > > > - When the sequence "\e[?1049h" is sent to the terminal, the alternate > > > screen is activated. It is always blank upon activation. > > > > > > - When "\e[?1049l" is sent to the terminal, the normal screen is > > > restored i.e. you'll end up with the same screen content that existed > > > at the moment "\e[?1049h" was sent and the alternate screen with its > > > content is discarded. > > > > > > You can even play with this using echo -e at the shell prompt. > > > Really there isn't more to it than that. > > > > Okay thanks for having explained the mechanism. However I continue to > > observe that when an application knows how to use the alternative > > screen, then right after the switch to the alternatvie screen BRLTTY > > pornounces unrelated content, as if there were some leftover of a > > previous session under the alternative screeen. > > Does the braille display contain unrelated content too, even if only for > a fraction of a second? S?bastien told me that he didn't see the unreleated content on the Braille display, but I'm thinking that perhaps it's really too fast for the dots to have time to raise. I have had a look at enter_alt_screen, it uses csi_J(CSI_J_FULL) to clear the screen. I see in csi_J that it first calls flush_scrollback and then only calls scr_memsetw. Thus flush_scrollback happens to move the cursor, before the screen is cleared. I guess that what happens is that this cursor move makes brltty have the time to read the top of the screen before it is cleared. Samuel From nico at fluxnic.net Mon Oct 5 00:50:58 2026 From: nico at fluxnic.net (Nicolas Pitre) Date: Sun, 4 Oct 2026 20:50:58 -0400 (EDT) Subject: [BRLTTY] Linux console hacking (was: Re: Footsteps towards better accessibility in Linux) In-Reply-To: References: <87msb422fk.fsf@sange.fi> <1p6pn777-2r6s-982s-20s3-48r6srp380s0@syhkavp.arg> <68o223pn-q9qp-6or4-3326-3q8n4p8s3527@syhkavp.arg> <51pn63n3-9149-pqpq-2374-892rn49rs040@syhkavp.arg> Message-ID: <5fc0ae58-39ee-39c5-b5e1-ab23ede9c725@fluxnic.net> On Sun, 4 Oct 2026, Samuel Thibault wrote: > I have had a look at enter_alt_screen, it uses csi_J(CSI_J_FULL) to > clear the screen. I see in csi_J that it first calls flush_scrollback > and then only calls scr_memsetw. Thus flush_scrollback happens to move > the cursor, before the screen is cleared. I guess that what happens is > that this cursor move makes brltty have the time to read the top of the > screen before it is cleared. That can't be it. do_con_write() holds the console lock for the whole write() buffer, so the full \e[?1049h -> enter_alt_screen() -> csi_J() sequence is atomic with respect to vcs_read(), which needs the same lock. And flush_scrollback() doesn't actually move the cursor: it recomputes vc_pos from the same x/y, and no VT_UPDATE notification is sent until the end of do_con_write(). The actual problem _could_ be on the BRLTTY side. A screen snapshot is made of three separate system calls: 1. ioctl(VT_GETCONSIZECSRPOS) for the size and cursor position 2. pread() on /dev/vcsa for glyphs and attributes 3. pread() on /dev/vcsu for the Unicode text On top of that, vcs_read() drops the console lock between page-sized chunks, so even a single read of a large screen isn't atomic. When a curses program exits, it typically moves the cursor to the bottom row and then sends \e[?1049l, which restores the main screen and the cursor position saved on entry. If that lands between steps 1 and 2, BRLTTY gets the cursor from the alternate screen and the text from the restored main screen. Autospeak follows the cursor, so it speaks the bottom line of the main screen, i.e. stale output from before the program was started. The next refresh corrects things right away, which is why nothing shows up on the braille display. But speech can't be taken back, and since the corrected cursor line is usually blank, autospeak has nothing new to say that would interrupt it. The kernel does raise POLLPRI on /dev/vcsa for that update, and BRLTTY does refresh again, but only after the core has already processed the inconsistent snapshot. The potential fix is in BRLTTY's Linux screen driver: - read the vcsa content first: each vcsa read resets its pending update indicator, so everything read afterwards is covered (the size from the ioctl is only used to validate what was read); - after the snapshot, poll /dev/vcsa with a zero timeout, and retake the snapshot (up to 3 attempts) if an update is pending, which also covers a resize in the middle. That way an inconsistent snapshot never reaches the core. It's here: https://github.com/npitre/brltty/tree/linux-screen-snapshot S?bastien, could you give it a try? Running with -l scrdrv logs "screen updated during cache refresh - retrying" each time the race is caught, which would confirm the diagnosis. Nicolas