windows 10+: make 256/true colors "work" without -Da#539
Conversation
9bc9b18 to
3c3ec82
Compare
|
Forced pushed with refined commit message. No code changes. |
To get 256/true colors working with -R (win10+ only), "sgr mode" (-Da) should be used to bypass the internal SGR processing and passthrough everything to the terminal directly - which can handle it on win10+. "sgr mode" was and is working well for some releases now. But by default (without -Da), we do internal SGR processing and apply the values to the console outselves, including some transformation of -D<x> values unique to windows (like underline to cyan). In the previous release v643, the default behavior (no -Da) was: - ANSI 8 colors SGR and attributes (1 for bold, etc) work well. - Unknown values under 50 (like 38, 48) were processed as 0 (reset). - SGR values of 50 or more aborted the internal SGR processor and switched to passthrough till the end of the current input buffer. - On abort+passthrough, the internal SGR state was kept unmodified. This resulted in broken behavior for 256/true colors, e.g.: - SGR 38;5;33 was incorrectly applied as reset, blink, yellow-fg. - SGR 38;5;50 was passthrough, and the internal state became blink. The passthrough was clearly broken on pre-win10, as the terminal can't handle it, but on win10+, the passthrough seems to work if not looking too closely. E.g. in some syntax highlighters where all the 256/true colors include at least one value >= 50, it did appear working on win10+. In release v661 the behavior was simplified and tightened: - ANSI 8 colors SGR and attributes (1 for bold, etc) work well. - Any unknown value (like 38, 48) aborts the rest of this sequence without passthrough (prior values at this seq, if any, do apply). As a result, 256/true colors sequences are ignored, there's no issue on pre-win10, we never misinterpret such sequences, never passthrough, and the internal state is always in-sync with the actual terminal. However, it does look like a regression compared to cases which did appear to be working in v643. This commit fixes this "regression", but without the downsides of v643: - On win10+, instead of aborting the sequence on unknown values like v661 does, we now switch to "controlled passthrough" mode until reset is detected, and then switch back to the internal SGR handler. As a result: - On pre-win10 it's like v661 (256/true colors are ignored cleanly). - On win10+: all 256/true colors always "work", never misinterpreted. - The internal state is in-sync - cleared on reset after passthrough. - Like -Da, the -D<x> transformations are skipped during passthrough. Fixes gwsw#538
3c3ec82 to
d32fcdf
Compare
|
Rewrote the commit message to explain how v661 can be considered a regression compared to v643 in some cases, and how this commit does the best of both worlds to a degree which it appears perfect. |
|
So, I started doogfooding master just now, and noticed that there's an issue, possibly related to this commit. Less crashes with this file (16 lines true color "plane") before completing two lines - true-color.zip . Each line is ~ 1400 bytes of true color escape sequences, and ends in SGR reset. I suspect a buffer overflow issue which got exposed by this PR, but I don't have more details currently. I'll debug and post updates here. Meanwhile, please wait withthe release. |
|
The offending commit is mine - f9f2498 just before v651. Debugging. |
|
OK, I still don't know why it crashes, but this is not a new issue. It was exposed by this part of the diff of commit f9f2498 : @@ -299,16 +312,20 @@ static void win_flush(void)
}
if (q == p ||
- code > 49 || code < 0 ||
(!is_ansi_end(*q) && *q != ';'))
{
+ /*
+ * can't parse. passthrough
+ * till the end of the buffer
+ */
p_next = q;
break;
}I.e. this code aborts the parsing and switches to passthrough based on the inaccurate test of And that commit ignores the rest of the sequence instead of switching to passthrough. I tested also with commit 52b8e35 - which is 1 commit before we started touching I still don't know why, but I suspect it's an edge case bug which my new SGR parser fixes (it doesn't crash with my own from-scratch parser, and this is why I haven't noticed it, because in my build I use my parser, but I wanted to dogfood master a bit before your release, and I discovered it). @gwsw any thoughts? I still have a hunch about buffer overflow, but that test line (>49, <0) is just a trigger. There's nothing special about these values, and these thresholds are arbitrary, and there are unrecognized numbers even within this range (like 38), and the code implicitly does SGR reset in such case - it assumes any unrecognized value must have been 0: default:
/* case 0: all attrs off */So there's nothing special about values outside this range which would trigger bad behavior. Amd I think it has to be the fact that it doesn't abort early, combined with this specific input. But I don't yet know why that results in a crash. |
|
It's "fixed" If I change: static char obuf[OUTBUF_SIZE];into static char obuf[OUTBUF_SIZE * 2];I.e. from 1024 to 2048 if I got the numbers right. But it's not related to 1024 specifically. If I change it to half the size instead (512), then it still goes further and completes almost 4 lines before crashing. So there's some math or behavior error depending on how the input length stars align. |
|
OK, I sort of got it. This loop: for (anchor = p_next = obuf;
(p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )can be entered (I'm guessing not in the first iteration) when Now only remains to find the offensive asumption or calculation inside that loop. This shouldn't take long. |
See the commit message for full description.
TL;DR:
Before commit f9f2498 (which is just before v651), unknown SGR values resulted in aborting the SGR parsing and instead passthrough till the rest of the buffer to the terminal directly.
This had broken output if the terminal doesn't support VT (e.g. win7), but seemingly correct output with VT, e.g. 256 colors on win10.
Commit f9f2498 changed this process, by ignoring only the rest of the current sequence if an unknown value is encountered, so that the next sequences in the buffer are still processed normally.
Additionally, we then never lose track of the terminal state, because either we recognize a value and apply it, or we do neither of those. Specifically, we never apply a value which we can't recognize/track.
While it fixed the junk output when VT is disabled, it also had an effect which made 256 and true colors stop "working" on win10+.
So with the latest release v661, it can be observed than 256 and true colors stopped working compared to the previous release v643.
So this commit brings back this behavior of "working" 256/true colors on win10+ by default, but a bit more refined than before f9f2498 .
Fixes #538