Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Few windows issues post v668 #607

Open
avih opened this issue Feb 12, 2025 · 40 comments
Open

Few windows issues post v668 #607

avih opened this issue Feb 12, 2025 · 40 comments

Comments

@avih
Copy link
Contributor

avih commented Feb 12, 2025

In case you plan to do a release, there are some issues on Windows which were not there in v668, and which are probably worth fixing first (I intend to do that, or at least identify the causes):

  • In some cases "less" enters an infinite loop where it re-renders the current view continuosly, ^C or ^X don't work, and it can be only killed externally. I think it happens with arrow-down held, but not 100% sure. I didn't yet bisect it. I don't know how to reproduce it yet, but it's not infrequent. Any thoughts what might cause this?
  • I think that in some cases mouse-deinit doesn't work or not applied or something. Symptom in Windows terminal (and busybox sh): before running less: mouse-wheel-scrolls the terminal scrollback buffer (which is the right thing), but after less exits: it scrolls shell commands history at the prompt (as if arrow down/up are used) There's probably some Windows-terminal auto-thing going on (translate wheel to arrows), but it only happens after "less" exits. I think I saw this happens with -X.
  • -X should probably be interpreted on windows as "don't use alternate screen or new console buffer", which means the console needs only basic init (e.g. set vt mode), but in non-vt mode it probably should still set the console mode, but doesn't - because win32_init_term is not called at all.

Other things I want to do:

  • When vt is enabled, we can use alt-screen instead of a new console buffer, and I want to do that. New console buffer does work (cmd.exe window or openconsole.exe), but the Windows terminal (which I think is or would be the default console or some such) has a bug where scrolled-out content of a non-primary buffer spills into the main scrollback buffer - almost completely defeating the reason to use a new screen buffer (the main view is restored, but the scrollback gets trashed). They also don't intend to fix it, or at least not anytime soon - scroll-out in secondary console buffer pollutes main scrollback buffer microsoft/terminal#17874 .
  • For the sake of debugging, but possibly also otherwise, I want to add a way to completely disable vt-mode, as otherwise it's auto-enabled without a way to test non-vt code. I'd appreciate some advice on which option to use (and where to add the code), then I can make it happen depending on the state of this option. Maybe -D<something>?
  • Not sure yet about the new SGR parser, but the current one does seem to work.
  • Add support for XCFLAGS and XLDFLAGS in Makefile.wng to allow adding custom build flags (2 new lines).
  • I created a mingw makefile which can build current "less", and all the way back to v322 from 1996, which is very useful for bisecting issues (but also for posterity). There were various issues in past revisions which make them hard to build, ranging from insufficient makefile to missing funcs.h generation, and various bugs. This makefile knows to work around several issues, applying backport fixes where needed, and more. It's obviously more complex than Makefile.wng, but it is self-contained and doesn't depend on external files. I'd like to add it to the master less repo. Any name you like for it? maybe something like "Makefile.mingw-multi" or some such?

There might be more things, but I've been using v668 till now, and now I have some time for "less".

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

There's something wrong with the autowrap/ignaw thing:

  • With vt enabled, by default (LESS=, no options), the initial rendering doesn't advance to the next line if the line should wrap. I.e. the remainder of the line disappears, and the prompt is printed earlier than expected, i.e. not at the bottom - seemingly because all the remainder lines are missing. I verified that with vt enabled auto_wrap is set to 1 and so does ignaw - which happens with xterm, and should be the same with vt mode (I didn't re-verify the behavior with vt mode, but that PR/issue where we discussed it had extensive research by me, which could be wrong, but I hope it isn't).
  • With the same scenario as above (VT enabled, no options, long lines), when scrolling down one-line-at-a-time, the new lines appear weird. First down-arrow shows the next line up to the wrap-point - but the prompt remains at the same line at EOL, and the next arrow-down renders the remainder at the same line, overwriting the first part of the line, and then finally the prompt is at the next line. It keeps hapening with every wrapped line when scrolling 1-line.
  • With vt disabled, with -XS and nothing else, the inverted > marker at EOL of cropped lines is missing at the initial rendering of the content, but if scrolling down and then up then it does appear at the lines which "enter from the top" during scroll-up.

These issues don't happen with v668.

I suspect the issue is at the commits related to clear-to-EOL, but sorry, I'm just unable to follow that code... (I'm whispering, maybe reconsider reverting to the original one-liner which I suggested, which is way way simpler than what you committed).

They might also be related to the new console modes (both with/without VT) and the auto_wrap/ignaw setting on windows, in conjunction with the clear-EOL changes.

@gwsw
Copy link
Owner

gwsw commented Feb 12, 2025

  1. I did a few minutes of testing and cannot reproduce the hang or the missing mouse deinit. Let me know if you find a reproducible case for either of these.
  2. For a new option to disable VT mode, you could add a new letter to -D, similar to -Da. You could add it to opt_D in optfunc.c, in the same #if MSDOS_COMPILER block that handles 'a'.
  3. I can reproduce the missing newline when displaying a long line without -S. I tried removing the two lines in put_line() that call clear_eol, but that did not fix the issue, so I'm doubtful that the problem is related to the clear_eol change. I will look into this one some more.
  4. Do you envision that the new mingw Makefile would eventually replace Makefile.wng? Maybe just name it "Makefile.mingw"?

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

  1. I did a few minutes of testing and cannot reproduce the hang or the missing mouse deinit. Let me know if you find a reproducible case for either of these.

Interesting. This happens to me frequently (the hang). But yeah, I'll definitely try to reproduce it reliably.

4. Do you envision that the new mingw Makefile would eventually replace Makefile.wng? Maybe just name it "Makefile.mingw"?

I think not. It's a complex makefile. I don't think we want the default makefile more complex than it already is. You can check it out here avih@ba53695

And it has the advantage that old revisions don't change, so at most it would stop working in the future (if unmaintained), but it will be able to build v322 - v668 forever.

(from v537 onwards it's pretty much the current Makefile.wng, except that the current makefile didn't exist at v537, so it's hard to built with Makefile.wng at v537, which where this makefile helps).

@gwsw
Copy link
Owner

gwsw commented Feb 12, 2025

It appears that the long line display problem was caused by dee132a. That was the change that you suggested because you thought auto_wrap was being set incorrectly in Windows. With this change, long lines are displayed correctly:

diff --git a/screen.c b/screen.c
index 4532aef..06a452f 100644
--- a/screen.c
+++ b/screen.c
@@ -1612,7 +1612,7 @@ static void win32_init_vt_term(void)
                                         ENABLE_VIRTUAL_TERMINAL_PROCESSING);
    if (vt_enabled)
    {
-       auto_wrap = 1;
+       auto_wrap = 0;
        ignaw = 1;
    }
 }

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

As far as I recall, wIth xterm we have autowrap=1 and ignaw=1, and the windows terminal behaves exactly the same as xterm in this regard, and IIRC this complies with my analysis back then.

These values must be more correct as far the behavior goes.

autowrap=0 means there's no wrap at all. not "delayed" and not anything - it just stays on the same line until \n is printed. IIRC thah's how the terminfo defines it, and how it's documented in "less".

And vt mode definitely wraps - delayed - like xterm, which must mean that both should be 1.

Am I missing something?

@gwsw
Copy link
Owner

gwsw commented Feb 12, 2025

With autowrap=1 and ignaw=1, when pdone() completes a wrapped line, it does not add a newline. It expects that the next char printed will auto-wrap. This is how it works on xterm. I don't know exactly how the Windows terminal works, but based on this bug, it seems to be doing something different than xterm.

@gwsw
Copy link
Owner

gwsw commented Feb 12, 2025

BTW I was able to reproduce the hang and repaint loop once, by scrolling through main.c by holding down the down arrow key. I tried it about 10 more times and haven't been able to reproduce it again.

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

I don't know exactly how the Windows terminal works, but based on this bug, it seems to be doing something different than xterm.

Yeah, I can reproduce that it doesn't wrap at all without \n, and that adding ENABLE_WRAP_AT_EOL_OUTPUT seems to make it behave like xterm.

I'm testing by adding this function, and calling it if vt succeeds as test_wrap(con_out);:

static void test_wrap(HANDLE con) {
	CONSOLE_SCREEN_BUFFER_INFO scr;
	GetConsoleScreenBufferInfo(con, &scr);
	int width = 1 + scr.srWindow.Right - scr.srWindow.Left;

	for (int i = 0; i < 3; ++i) {
		WriteConsole(con, "\n\n\n", 3, 0, 0);

		for (int j = 0; j < width - 1 + i; ++j) {
			char c = '0' + j % 10;
			WriteConsole(con, &c, 1, 0, 0);
		}
		Sleep(1000);
	}
	sleep(3000);
	exit(42);
}

Additionally adding also DISABLE_NEWLINE_AUTO_RETURN breaks the behavior, as it appears to do what it says on the tin - that \n alone doesn't imply \r.

That being said, I need to re-read my analysis and conclusions from back then (and find this discussion first). IIRC there were some gotchas, incorrect docs, and other things, and I want to check whether that commit was hasty (the one which only sets ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING), or whether the conclusions seem incorrect currently.

If you remember where we we've had this discussion, please post a link.

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

The conclusion seems to be here #497 (comment)

Reading now.

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

Well, my conclusions back then:

ENABLE_VIRTUAL_TERMINAL_PROCESSING does what you think - it enables xterm escape sequences, and ALSO vt100-wrap, and also ignores the ENABLE_WRAP_AT_EOL_OUTPUT and DISABLE_NEWLINE_AUTO_RETURN bits.

This is really not what I'm seeing now, and I said it quite conclussively back then.

Specifically, when starting with ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING (which are documented as should be used together), then:

  • It doesn't wrap at all by default - in contrast what what I concluded, but adding ENABLE_WRAP_AT_EOL_OUTPUT does make it wrap like vt100/xterm ("delayed" - ignaw=1).
  • DISABLE_NEWLINE_AUTO_RETURN does in fact have an effect, and still does what it says on the tin (disables implicit \r) - but not what the docs say.

I'm very puzzled by this. I can't yet resolve the conflict. Maybe I didn't test with a new console buffer? (I did test these independently of "less", I want to say as close as possible to the "less" use case, but I need to find these test programs to know for a fact).

It's also possible that a newer windows terminal which I'm using now behaves differently. I need to also test it with the windows console (which I haven't so far), although it might have changed too in Windows updates...

Regardless of VT mode, non-VT also has issues with long lines, both with and without -X (scroll few lines down, then few lines up, and it's missing the pre-wrap part of the lines).

First, disable vt: change vt_enabled = SetConsoleMode... to vt_enabled = 0 && SetConsoleMode(...).

I'm pretty sure the -X code ("no_init") is not modified by that commit, because win32_init_term is definitely not called - I added exit(42) call at the begining of this function, and it doesn't exit when using -X, but does exit without -X.

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

Specifically, when starting with ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING (which are documented as should be used together), then:

It doesn't wrap at all by default - in contrast what what I concluded, but adding ENABLE_WRAP_AT_EOL_OUTPUT does make it wrap like vt100/xterm ("delayed" - ignaw=1).

DISABLE_NEWLINE_AUTO_RETURN does in fact have an effect, and still does what it says on the tin (disables implicit \r) - but not what the docs say.

That was in the windows terminal.

In the windows console (cmd.exe window) ENABLE_WRAP_AT_EOL_OUTPUT does indeed get ignored when VT is enabled as I concluded back then, and it does vt-wrap (ignaw=1) even without this bit set.

So the initial rendering with VT enabled and long lines is correct at the windows console with current master. Nothing is missing and the prompt is at the bottom.

However, scrolling (one line at a time) few lines down and then few lines up, exposes the same issue as without VT - when scrolling up only the remainder of long lines show (enter from the top), and the begining of long lines dissappear.

The same binary at the windows terminal is still broken like before starting at the initial rendering.

DISABLE_NEWLINE_AUTO_RETURN still has an effect also with VT at the windows console, in contrast to my initial conclusion.

I can only conclude that the windows terminal changed since I concluded it, and I don't know what to say about the windows console and DISABLE_NEWLINE_AUTO_RETURN, other than hand waving that maybe it changed too during windows update.

Either way, for VT mode, this works now and before in both the console and the terminal, and has vt100-wrap (ignaw=1):

ENABLE_PROCESSED_OUTPUT |
ENABLE_VIRTUAL_TERMINAL_PROCESSING |
ENABLE_WRAP_AT_EOL_OUTPUT

Non-VT is still broken with long lines in the windows console too, both with and without -X.

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

BTW I was able to reproduce the hang and repaint loop once, by scrolling through main.c by holding down the down arrow key. I tried it about 10 more times and haven't been able to reproduce it again.

I think it happens when reaching the bottom of the content, so maybe it would be easier to reproduce with content only some lines taller than the window height. I still didn't try to find a reliable reproducer.

@gwsw
Copy link
Owner

gwsw commented Feb 12, 2025

I've reproduced the hang 4 more times by scrolling main.c. It did not happen at the end of the content. However, in all four cases it happened at the exact same point in the file, with line 311 displayed at the top of the screen and line 338 at the bottom. That can't be a coincidence, but I don't yet know whether it's related to the file content or if it happens at the same point due to some timing issue.

@gwsw
Copy link
Owner

gwsw commented Feb 12, 2025

This is not definitive because the hang is very intermittent, but so far I have only reproduced it with the down arrow key. Holding down 'j' has not reproduced it. That might suggest it has something to do with the code that handles extended keys in Windows. But every case so far has always occurred at the same point in the file. This is true regardless of whether I start scrolling from the beginning of the file or press 'f' first and then start scrolling. I'm not sure how to interpret this set of symptoms.

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

Maybe bisect? there are ~ 43 commits since v668, so that's 5-6 bisection steps?

It would be best if we could automate the testing, even if not reproducible, I'd imagine that holding the down arrow over 100 consecutive invocation of "less" should reproduce it reliably-ish.

Maybe try to add some code inside less which simulates this injection somehow? then running less with the same file in a loop would automatically "hold the down arrow" and should hopefully either complete the 100 iterations, or hang during one of them?

EDIT: or invoke it in a loop and just keep holding the down arrow yourself, or put something on your down arrow key to keep it held for you? (and it needs the option to quit when reaching EOF).

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

I think I found another symptom of this issue, but with a different thing on screen:

I'm using this line in busybox-w32 sh:

while sleep 0.5; do head -n 50 < ../testlines.txt | LESS= ./less -E; done

Where the window is 33 lines tall and 64 cols wide, and most of the lines in testlines.txt are wider than the terminal.

I'm holding down the arrow key, and within one or two iterations of "less" I get a whole screen full of:

:...skipping...
:...skipping...
:...skipping...
:...skipping...
...

which similarly cannot be interrupted and can only be killed from the outside (obviously also after stopping holding the down arrow).

EDIT: and my auto-repeat is the fastest which can be set on Windows, which I believe is about 30 repeats/sec.

EDIT2: hmm.. but it doesn't happen if I save that output as a 50-lines file and use it directly in less in the same loop without that pipe...

@avih
Copy link
Contributor Author

avih commented Feb 12, 2025

I'm holding down the arrow key, and within one or two iterations of "less" I get a whole screen full of:

:...skipping...
:...skipping...
:...skipping...
:...skipping...
...

I've bisected this issue to 9ce4c71 ("Implement 23ff6a4 for Windows. Allow any key, not just ctrl-C and ctrl-X, to interrupt a file read.")

It reproduces very reliably in bad revisions: it happens in exactly the second iteration of the loop.

Maybe it's the cause of both symptoms?

gwsw added a commit that referenced this issue Feb 13, 2025
9ce4c71 changed iread on Windows
so that if WIN32kbhit detects a keypress, it ungets the key and
returns EOI. The problem is, the next call to iread will then
detect that same queued key, unget it, and immediately return,
so we are stuck in a loop.

Change iread so that it looks only for a non-queued key; that is
a newly pressed key from the terminal's input.

Related to #607.
@gwsw
Copy link
Owner

gwsw commented Feb 13, 2025

Let me know if 3894f47 fixes it for you.

@avih
Copy link
Contributor Author

avih commented Feb 13, 2025

Let me know if 3894f47 fixes it for you.

It fixes the loop use case, and seems highly relevant to the other symptom as well, so for now I'm assuming it also got fixed.

I'll let you know if it happens again. Thanks.

@avih
Copy link
Contributor Author

avih commented Feb 13, 2025

... cannot reproduce ... the missing mouse deinit.

Not an issue in master. It resulted from a WIP patch of mine to hack alt-screen instead of new console buffer. I guess I didn't track the handles variables correctly, and it probably applied the mouse-deinit to the wrong console buffer.

It reproduces reliably with my alt-screen hack, and goes away immediately if I disable it.

So one less thing to worry about.

EDIT:
Well, it's not a handles issue, because all this hack does is to run also this line if vt_enabled = SetConsoleMode... succeeds (right after setting autowrap and ignaw):

WriteConsole(con_out, "\033[?1049h", 8, 0, 0);  // ALTSCR ON

So the normal init adds a new console buffer, but then the VT init switches to the alt-screen inside that new buffer, with the expectation that it both won't trash the scrollback because alt-screen (this does work as expected), and it will also make everything go away when returning to the main console buffer at the normal term deinit (this also normally works).

That's when I realized that VT init is called with -X, but the normal init isn't, so this hack is broken with -X, first, because it enters alt-screen to begin with - which we probably don't want to do with -X, and then also because it doesn't exit from alt-screen (and also there was no new console buffer because no normal init/deinit, so it actually switches the main buffer to alt-screen, and it remains like that on exit).

Hence the 3rd issue in my original post that -X should probably still set console mode even without VT.

I don't know whether it's supposed to exit alt-screen when "less" exits (without less explicitly leaving alt-screen), and why the mouse mode remains in effect. Maybe because it stays in alt-screen and that wheel-to-arrows WT auto-thing only applies in alt-screen (which can make sense).

But I'll need to keep this issue in mind when writing a proper alt-screen patch (which wouldn't be much bigger, just more correct), and ensure that indeed the mouse mode is restored as it should. So it wasn't for nothing afterall.

@avih
Copy link
Contributor Author

avih commented Feb 13, 2025

With vt enabled, by default (LESS=, no options), the initial rendering doesn't advance to the next line if the line should wrap.

With the same scenario as above (VT enabled, no options, long lines), when scrolling down one-line-at-a-time, the new lines appear weird.

These are verified fixed by this:

Either way, for VT mode, this works now and before in both the console and the terminal, and has vt100-wrap (ignaw=1):

ENABLE_PROCESSED_OUTPUT |
ENABLE_VIRTUAL_TERMINAL_PROCESSING |
ENABLE_WRAP_AT_EOL_OUTPUT

Then I reverted the background fix - ecf29c5 with these results:

With vt disabled, with -XS and nothing else, the inverted > marker at EOL of cropped lines is missing at the initial rendering of the content, but if scrolling down and then up then it does appear at the lines which "enter from the top" during scroll-up.

The above got fixed by the revert.

Do keep in mind that with -X it doesn't apply the no-wrap-at-all console mode, and therewore uses whatever mode the console happens to be at. That being said, I think that reverted commit should not have any effect on the behavior in such case, but it did change it for the worse (and fixed by me reverting it just now for the sake of testing).

Regardless of VT mode, non-VT also has issues with long lines, both with and without -X (scroll few lines down, then few lines up, and it's missing the pre-wrap part of the lines).

The above, in all 4 combos of VT on/off and -X on/off, still have this issue in exactly the same form (as far as I can tell), so I'm guessing there's also something else in play, because, again, the autowrap mode commit has no effect when using -X while VT is disabled, because win32_init_term is never called in this case, and reverting the background-fix commit didn't fix it, but it did work in v668.

So I bisected it (patching the VT console mode fix where required), which identified the offending commit as da2a9ec ("New strategy for handling highlighting").

Can't say I immediately see a connection between highlight strategy and broken long lines, but hopefully you do.

@avih
Copy link
Contributor Author

avih commented Feb 13, 2025

Here's what I suggest: you handle the issues resulting from the background-fix (which is now only required for djgpp - not win XP/7/8) and the new highlight strategy, because they're really outside of my comfort zone, and I'll do the rest (VT console mode, correct init with and without -X, and everything else which I said I wanted to do).

Sounds reasonable? Because I think otherwise we'd step on eachothers toes...

Then I'd like to use the updated code as my main "less" for a while, in case some new issues show up (I noticed all the issues in this thread barely minutes after I built it, and there could be more...).

@gwsw
Copy link
Owner

gwsw commented Feb 13, 2025

Well this is puzzling. It seems that GetConsoleScreenBufferInfo() is returning an incorrect terminal size if I resize the terminal while running less. For example, I have a cmd window that is 75x30. mode con /status correctly shows the size. I run less and while it's running, I resize the terminal to 65x30. I exit less and run mode con /status, and it incorrectly still shows 75 columns. If I then run less again, the call to GetConsoleScreenBufferInfo also incorrectly returns 75 columns. This same thing happens using less-668. I'm running Windows 11 which I just upgraded to recently. Does this work differently in W11 vs. W10? Or has it always worked this way? I thought GetConsoleScreenBufferInfo was supposed to return the current size of the window.

@gwsw
Copy link
Owner

gwsw commented Feb 13, 2025

After applying the ENABLE_WRAP_AT_EOL_OUTPUT patch, I am not seeing any display issues with long lines, as long as I make sure that mode con /status is returning the correct dimensions by resizing the window outside of less. Can you provide a reproducible case?

@gwsw
Copy link
Owner

gwsw commented Feb 13, 2025

Hm, the problem of GetConsoleScreenBufferInfo doesn't happen if I run less with -X, so I guess it has something to do with the VT stuff. Can the VT be a different size than the real terminal?

@avih
Copy link
Contributor Author

avih commented Feb 13, 2025

Does this work differently in W11 vs. W10?

I think it's the same in W10. Resizing the window while less is running and then exiting, and mode con /status shows the incorrect size.

But it's not unique to less. Here's a minimal test program which has in the same behavior:

#include <windows.h>
int main() {
    HANDLE con = CreateConsoleScreenBuffer(
			GENERIC_WRITE | GENERIC_READ,
			FILE_SHARE_WRITE | FILE_SHARE_READ,
			(LPSECURITY_ATTRIBUTES) NULL,
			CONSOLE_TEXTMODE_BUFFER,
			(LPVOID) NULL);

    SetConsoleActiveScreenBuffer(con);
    Sleep(5000);
    return 42;
}

But if I remove CreateConsoleScreenBuffer and SetConsoleActiveScreenBuffer and changing the size while it sleeps, the mode con thing does show the correct size after this program exists.

I can only conclude that the size change is not registered for the main buffer while a non-main console buffer is active.

I think this has always been flaky, and if an application seem to not recognize the window size, then usually maximizing and restoring the window fixes it for the currently-running app.

Hm, the problem of GetConsoleScreenBufferInfo doesn't happen if I run less with -X, so I guess it has something to do with the VT stuff. Can the VT be a different size than the real terminal?

As above says, not the VT. The new console buffer. (-X when VT is supported only disables the new console buffer, but the mode is still set for VT the same way regardless of -X).

@avih
Copy link
Contributor Author

avih commented Feb 13, 2025

After applying the ENABLE_WRAP_AT_EOL_OUTPUT patch, I am not seeing any display issues with long lines, as long as I make sure that mode con /status is returning the correct dimensions by resizing the window outside of less. Can you provide a reproducible case?

It happens also when the mode thing shows the correct size.

Issue 1 (apparently broken by the new highlight strategy thing), happens in all 4 combos of VT-enabled and -X:

Regardless of VT mode, non-VT also has issues with long lines, both with and without -X (scroll few lines down, then few lines up, and it's missing the pre-wrap part of the lines).

Test case:

  • Open a new windows console or window terminal window.
  • Make the window narrow, say around 60 columns. Ensure mode con /status shows the correct size if you're not sure.
  • less testlines.txt this file is attached to this message. All/most of the lines are longer than the width, so they wrap normally.
  • Scroll few lines down using the down arrow. So far so good.
  • Scroll few lines up using the up arrow.

Expected result: it shows the same view of the previous lines.

Actual result: only the remainder of each line enters from the top and shows on screen. The begining of the lines enetering from the top is missing.

Issue 2 (fixed by reverting the background-fix commit):

First, disable vt: change vt_enabled = SetConsoleMode... to vt_enabled = 0 && SetConsoleMode(...).

With vt disabled, with -XS and nothing else, the inverted > marker at EOL of cropped lines is missing at the initial rendering of the content, but if scrolling down and then up then it does appear at the lines which "enter from the top" during scroll-up.

This one seems to happen to me currently only in the windows terminal:

  • Run less -XS testlines.txt. It's missing the EOL > markers.
  • If you scroll few lines down and then few lines up, then it shows the EOL markers.
  • If you run it without -X then it does show the marker from the begining.

You can also test it by compiling back-to-back commits ecf29c5 (the background fix) - don't forget to disable VT, which does have this issue, vs the commit just before it - 82add99 - don't forget to disable VT - which doesn't have this issue.

As noted, VT disabled and -X doesn't change the console mode, so we depend on whatever the initial mode happens to be. This is the 3rd Item in the first message which I wanted to fix - ensure that the console mode is set also with -X when VT is disabled.

I didn't try to fix it yet, and I don't know whether it would fix the EOL markers, but I think it might.

Regardless, I think it's undeniable that the background-fix commit also affects this, but unless you know better, I think it shouldn't (but it may, if what it thinks it knows about autowrap and ignaw values doesn't match reality).

testlines.txt

@gwsw
Copy link
Owner

gwsw commented Feb 14, 2025

The first bug (incorrect display when scrolling long lines backwards) should be fixed by 5919111. This was not Windows-specific; it happened on Linux too.

@avih
Copy link
Contributor Author

avih commented Feb 14, 2025

With vt disabled, with -XS and nothing else, the inverted > marker at EOL of cropped lines is missing at the initial rendering of the content, but if scrolling down and then up then it does appear at the lines which "enter from the top" during scroll-up.

This one seems to happen to me currently only in the windows terminal:
...

As noted, VT disabled and -X doesn't change the console mode, so we depend on whatever the initial mode happens to be. This is the 3rd Item in the first message which I wanted to fix - ensure that the console mode is set also with -X when VT is disabled.

I didn't try to fix it yet, and I don't know whether it would fix the EOL markers, but I think it might.

... the background-fix commit also affects this, but unless you know better, I think it shouldn't (but it may, if what it thinks it knows about autowrap and ignaw values doesn't match reality).

This is the exactly what happens here.

New cmd.exe window apparently normally has only ENABLE_PROCESSED_OUTPUT and ENABLE_WRAP_AT_EOL_OUTPUT bits set, which correspond to autowrap=1 and ignaw=0 in "less", which are the indeed default on windows, and -X with VT disabled doesn't change the mode, so it stays like that, and what less thinks the state is happens to match reality and it works correctly (and does show the EOL markers).

But a new cmd.exe tab in the windows terminal apparently also has the ENABLE_VIRTUAL_TERMINAL_PROCESSING bit set (confirmed, at least with WT 1.22), which should have ignaw=1 but it doesn't, and it also doesn't set the console mode with -X and TV disabled, so we still end up with autowrap=1 and ignaw=0, but now this doesn't match reality, and whatever changes the background-fix did apparently change the behavior slightly in this case.

But the issue is hopefully not in the background fix, but rather that less assumes an incorrect state of the terminal.

I'm guessing that in v668 there's no issue (at least in this case), because before the background-fix it happened to work also in this case, but it was still equally susceptible to such issues, but we were lucky and this doesn't happen in this case.

I did a quick test that after less reads the console mode on init, if it has the VT bit set then it sets ignaw=1 and this fixes the issue.

But this is due to luck, because there could be other bits set or unset which still make autowrap and ignaw not match reality.

The real solution here is what I noted at the first post - that we should set the console mode with -X when VT is disabled, and not leave it to whatever random mode we were launched in.

@avih
Copy link
Contributor Author

avih commented Feb 14, 2025

The first bug (incorrect display when scrolling long lines backwards) should be fixed by 5919111. This was not Windows-specific; it happened on Linux too.

Confirmed with this test case. Thanks.

Yes, I assumed it would happen on Linux too, but I didn't have the bandwidth to test it there too, and I didn't want to add more noise.

There's currently only one Issue I'm aware of (missing EOL marker with -XS and VT disabled), which should be fixed once we set the console mode also with -X when VT is disabled.

Shall I take it from here, including handling the missing console mode with the EOL issue, and adding the wrap-at-eol thing in VT mode and the other things I mentioned?

@avih
Copy link
Contributor Author

avih commented Feb 14, 2025

Shall I take it from here, including handling the missing console mode with the EOL issue, and adding the wrap-at-eol thing in VT mode and the other things I mentioned?

That was a question.

I don't want both of us to work on the same code.

@gwsw
Copy link
Owner

gwsw commented Feb 14, 2025

Yes, please go ahead with fixing those issues.

@gwsw
Copy link
Owner

gwsw commented Feb 14, 2025

we still end up with autowrap=1 and ignaw=0, but now this doesn't match reality, and whatever changes the background-fix did apparently change the behavior slightly in this case.

Ok, I think that makes sense. The autowrap=1 and ignaw=0 case is when less does a clear_eol after printing a line that reaches the right margin (at the end of pdone). It assumes that the terminal has wrapped the cursor to the next line, so clear_eol will clear any erroneous color on the next line. But if the terminal has actually left the cursor at the right margin, then the clear_eol will just clear the last char in the line, so it will erase the > marker.

@avih
Copy link
Contributor Author

avih commented Feb 15, 2025

Ok, I think that makes sense ...

Yeah. Your description sounds very reasonable to me. So that's another things we can now explain in even more details than my hand waving :)

Anyway, I think it should be pretty straight forward from here. Hopefully I'll get it ready in the next few days. Thanks for the quick fixes.

@avih
Copy link
Contributor Author

avih commented Feb 18, 2025

Can the win32 term init/deinit functions be called more than once during a single run?

I don't think deinit clears things up properly (in case we need to run init again), but I can't fully follow the code, so I don't know whether it should do proper cleanup, or only read console mode once on startup, etc.

Looks like get_term can be called in psignals, but I don't know whether it's called on windows...

EDIT, but that get_term in psignals does get compiled on windows, e.g. if change it to get_term(); xxx then the windows build fails. So this suggest that it may be called on windows also outside of main.

I'd appreciate some insight on this. Thanks.

@avih
Copy link
Contributor Author

avih commented Feb 18, 2025

For reference (and comments), here's how I think it should work:

  • All initial console modes (in, out) should be saved exactly once on startup, restored on exit and before executing an external app (we want an external app to see the initial console - not the one which less modified, especially if we disable line-wrap to be able to use auto_wrap=0).
  • Regardless of VT, two win32 term init modes should be supported: minimal (no_init), and full.
  • The minimal mode would set VT if supported (and generally console mode to align with our needs), and otherwise (no VT) would set the normal console mode to our needs - the last one is the missing init with -X which results in missing EOL marker when VT is disabled and running in the windows terminal.
  • The full init mode would create a new console buffer if VT is unsupported. If VT is supported, we could do one of two things: work "on top" of the non-VT init (new buffer) and set it to VT (that's the current behavior), maybe optionally also switch it to alt-screen. Or just switch it to alt-screen without a new buffer. For simplicity, it should probably work "on top" of the non-VT full init (new buffer), but let's see.
  • deinit minimal/full should be able to reverse the respective init cleanly (may need to include setting con_out and similar vars to INVALID_HANDLE_VALUE etc). Unclear yet whether this would require more state vars. But probably testing that con_out is the same as con_save is enough to identify if a new buffer is in effect, and vt_enabled can indicate what it suggests.
  • init/deinit (the minimal/full respective to no_init) may need to support being called more than once.

Thoughts?

@gwsw
Copy link
Owner

gwsw commented Feb 19, 2025

Yes, a deinit/init pair is called in several places: in psignals (when handling the terminal stop signal SIGTSTP; I don't know if there is a SIGTSTP on Windows), in pipe_data (called when executing the | command), and in lsystem (when less invokes a shell command, such as executing the ! command, running an editor with the v command, or opening an OSC8 hyperlink). It's expected that deinit() returns the terminal to a state where an external command can be run, and after that, init() restores the terminal to a state where less can use it. This should be the case regardless of whether -X is set.

I think the rest of your points look fine to me.

@avih
Copy link
Contributor Author

avih commented Feb 19, 2025

Thanks.

@gwsw
Copy link
Owner

gwsw commented Feb 28, 2025

Hi @avih, any update on this? Are you still working on it?

@avih
Copy link
Contributor Author

avih commented Feb 28, 2025

I put is aside for a bit, but yes. I want to get back. Unlike the SGR thing, there's bo real blocker here other than sitting down and making it happen with some attention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants