Subject: bug#79298: patch: full color in windows terminal

From: Eli Zaretskii <[email-masked]>
Date: Thu, 04 Sep 2025 09:37:28 +0300

> Date: Thu, 04 Sep 2025 06:01:41 +0000
> From: Ewan <[email-masked]>
> Cc: [email-masked]
> 
> > I meant hiding it at the beginning of an update (in
> > w32con_update_begin) and showing it at the end of the update, which we
> > already do. Then we can move it at will without it appearing on the
> > screen, right?
> 
> Correct, and I tried this initially, but while it fixes the "jumping" 
> problem, it causes the cursor to flash in-place irregularly, since it 
> is hidden during updates, and updates have a variable duration (meaning 
> you can't mitigate it with a tailored system cursor blink rate). The 
> strategy of hiding it only when updating the echo area was a response to
> this issue. It minimizes the "off" time, so the flashing in-place is 
> reduced to a slight flicker.
> 
> > > Previously, we maintained the "cursor" throughout the update as an internal
> > > coordinate variable, without "realizing" the position in the system cursor,
> > > through a call to SetConsoleCursorPosition, until the after-update hook.
> > > That is no longer possible -- the system cursor position must be realized
> > > in real-time. Hiding the system cursor the whole time, and replacing it
> > > with a (nominal) "cursor" drawn in with the rest of the glyphs, achieves
> > > the same effect that we had before, in light of the new constraint. The
> > > (actual, system) cursor jumps around, invisibly, during the write, while
> > > the (nominal, visible) cursor stays stable, then makes one discrete move
> > > (when its new row is written).
> > 
> > 
> > It sounds like you are describing what I suggested above, so we agree
> > on how to handle this when WriteConsole is in use? 
> 
> Not quite. By "the whole time", I mean always, whether updating or not.
> We effectively replace the internal coordinate variable with the actual
> system cursor by making it invisible at all times, and moving it around.
> 
> > But what do you mean by "replacing it with a (nominal) "cursor" drawn 
> > in with the rest of the glyphs" in this context? 
> > ... why "replacing", and what is the "nominal cursor"?
> 
> You "replace" the (previously, visible) system cursor with a glyph that
> has its regular face_id temporarily replaced by the CURSOR_FACE_ID. The 
> "nominal cursor" is then just a temporary assignment of the cursor face
> to a whichever glyph will coincide with the (invisible) system cursor at 
> the end of the update cycle. You compute this position just before the 
> glyph matrix is written to the screen buffer. Visually, it is like the 
> solid GUI cursor, as its appearance is determined by the cursor face.
> 
> Here's a recent build, in case you want to try for yourself:
> https://github.com/ewantown/wemacs-build/releases/tag/v31.35.20250904.0a313d5
> 
> And here's a video demo, in case you don't want to run strangers' code:
> https://sendvid.com/o4rr6p2b

I'm not yet sure I understand the issues and the proposed solutions.

Let's back up a notch: Emacs in -nw mode on Unix systems already hides
the cursor before writing glyphs, see tty_write_glyphs_1 and
tty_write_glyphs_with_face.  Why cannot we do the same in
w32con_write_glyphs and w32con_write_glyphs_with_face, when
WriteConsole is in use?  Or what are the problems with doing that?

In general, drawing our own cursor on TTY frames sounds like a
complication I would like to avoid.  We don't do anything like that on
Unix, so I'd prefer Windows to work similarly, using the system
cursor.

> > I know that Windows 11 has ConHost which supports the control
> > sequences.  It's the potential issues with older systems (AFAIR,
> > ConHost premiered in XP) that I'm bothered with.  Although I don't
> > expect any such issues, it would be nice not to risk them at all, by
> > keeping as much of the current code as possible. If someone does
> > report such issues, testing a fix for them will be quite tricky, given
> > the almost non-existent access to such old systems, and the fact that
> > Emacs compiled with MinGW64 cannot even run on them.
> 
> I see, fair concern. FWIW I have not /removed/ any of the old code. 
> The old path is still intact. At runtime you can ensure that it is used 
> by setting w32-use-virtual-terminal-sequences to nil (done automatically
> if VT processing is not available), and w32-use-visible-system-cursor to 
> non-nil (manual - existing default is nil, unless using a screen-reader).
> Not quite as comforting as a static guarantee of backward compatibility,
> but I've tried to thread the new code through the old, branching on the
> feature flags, so the old path remains functionally untouched.

Yes, I understand.  But I would prefer to leave the old code used
always, not just under the compatibility option (which should be
needed only in rare cases anyway).