-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Fix UIA and marks regressions due to cooked read #16105
Conversation
This comment has been minimized.
This comment has been minimized.
Tested output from Sent a build to @codeofdusk so he can test it just in case. If you find anything, feel free to throw the feedback in here 😊 |
For what it's worth, this change (and the bug it fixes) is in input in CMD/python/etc. and not output. |
src/host/readDataCooked.cpp
Outdated
#define _buffer DONT_USE_ME | ||
#define _bufferCursor DONT_USE_ME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was gonna say that you should remove these, but actually, it's kinda a neat trick to make sure we don't mess this up later. Cool!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clever! wil also added a special thing you can use to override a local.
hide_name _buffer;
except it applies inside a function scope, so it's not applicable here
I'm going on vacation for a few days, but I got a chance to chat with Bill so I want to share some notes from our chat:
Hope this helps! |
Thanks for testing this PR in my absence! And thanks for the NVDA instructions. :)
That means the issue is fixed by this PR, right? Since PowerShell has its own readline implementation... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I understand this! Which is a miracle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
8/11. Sorry, should have gotten to this sooner. Got to 5pm and ran out of time
// because the entire prompt is supposed to fit into the VT viewport, with no scrollback. If we didn't do that, | ||
// any prompt larger than the viewport will cause >1 lines to be added to scrollback, even if typing backspaces. | ||
// You can test this by removing this branch, launch Windows Terminal, and fill the entire viewport with text in cmd.exe. | ||
if (needsConPTYWorkaround) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note to self: this branch was removed. That seems good
} | ||
} | ||
else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this branch is the same
@@ -128,15 +105,11 @@ static void AdjustCursorPosition(SCREEN_INFORMATION& screenInfo, _In_ til::point | |||
LOG_IF_FAILED(screenInfo.SetViewportOrigin(false, WindowOrigin, true)); | |||
} | |||
|
|||
if (interactive) | |||
{ | |||
screenInfo.MakeCursorVisible(coordCursor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was removed, but was only ever used for
COOKED_READ_DATA::_writeChars
COOKED_READ_DATA::_handlePostCharInputLoop
if (interactive) | ||
{ | ||
break; | ||
} | ||
std::ignore = screenInfo.SendNotifyBeep(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: interesting, we didn't beep if we were interactive before. Interesting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same with the null above, obv.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay you're right this was quite repetitive even if you really have to squint a lot
|
||
// But if we simply ran out of text we just need to return the actual number of columns. | ||
it += len; | ||
if (it == end) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any chance we accidentally step over end
? (like, should we have a it>end
assert or bail or something)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
len
is either 1 or 2 depending on whether the current code point is a surrogate pair. A few lines above, len
is only set to 2 if it + 1 != end
which means that it += 2
is safe.
src/host/readDataCooked.cpp
Outdated
@@ -11,6 +11,9 @@ | |||
#include "_stream.h" | |||
#include "../interactivity/inc/ServiceLocator.hpp" | |||
|
|||
#define _buffer DONT_USE_ME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i mean, I'm not gonna block over it (cause this is a hotfix), but theoretically wouldn't the usual way of doing this be to introduce a class/struct to wrap _buffer
and then actually only expose the setters as public methods on that class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I see, _handlePostCharInputLoop
. gotcha
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could do that. I've been thinking about doing it for a bit and there's no particular reason why I picked this approach over writing a wrapper class. It was just what came to my mind first.
If this PR isn't accidentally merged in the next day, I'll try to write a wrapper class and push it. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just pushed a commit where I'm now using a wrapper class to track dirtyBeg
.
struct BufferState | ||
{ | ||
const std::wstring& Get() const noexcept; | ||
std::wstring Extract() noexcept |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this signature offer the right move guarantees? you don't need to return an rvalue ref, do you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to RVO this should be more efficient I think. It's also probably a bit safer since it cannot result in stale pointers.
// Handles any tasks that need to be completed after the read input loop finishes, | ||
// like handling doskey aliases and converting the input to non-UTF16. | ||
void COOKED_READ_DATA::_handlePostCharInputLoop(const bool isUnicode, size_t& numBytes, ULONG& controlKeyState) | ||
{ | ||
auto writer = _userBuffer; | ||
std::wstring_view input{ _buffer }; | ||
auto buffer = _buffer.Extract(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code didn't used to destroy _buffer
on all codepaths. now it does! is it OK to do so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, because this function can only be called once during the lifetime of the object instance.
The initial cooked read (= conhost readline) rewrite had two flaws: * Using viewport scrolls under ConPTY to avoid emitting newlines resulted in various bugs around marks, coloring, etc. It's still somewhat unclear why this happened, but the next issue is related and much worse. * Rewriting the input line every time causes problems with accessibility tools, as they'll re-announce unchanged parts again and again. The solution to these is to simply stop writing the unchanged parts of the prompt. To do this, code was added to measure the size of text without actually inserting them into the buffer. Since this meant that the "interactive" mode of `WriteCharsLegacy` would need to be duplicated for the new code, I instead moved those parts into `COOKED_READ_DATA`. That way we can now have the interactive transform of the prompt (= Ctrl+C -> ^C) and the two text functions (measure text & actually write text) are now agnostic to this transformation. Closes #16034 Closes #16044 ## Validation Steps Performed * A vision impaired user checked it out and it seemed fine ✅ (cherry picked from commit e1c69a9) Service-Card-Id: 90891693 Service-Version: 1.19
A late change in #16105 wrapped `_buffer` into a class to better track its dirty state, but I failed to notice that in this one instance we intentionally manipulated `_buffer` without marking it as dirty. This fixes the issue by adding a call to `MarkAsClean()`. This changeset also adds the test instructions from #15783 as a document to this repository. I've extended the list with two bugs we've found in the implementation since then. ## Validation Steps Performed * In cmd.exe, with an empty prompt in an empty directory: Pressing tab produces an audible bing and prints no text ✅
A late change in #16105 wrapped `_buffer` into a class to better track its dirty state, but I failed to notice that in this one instance we intentionally manipulated `_buffer` without marking it as dirty. This fixes the issue by adding a call to `MarkAsClean()`. This changeset also adds the test instructions from #15783 as a document to this repository. I've extended the list with two bugs we've found in the implementation since then. ## Validation Steps Performed * In cmd.exe, with an empty prompt in an empty directory: Pressing tab produces an audible bing and prints no text ✅ (cherry picked from commit 7a8dd90) Service-Card-Id: 91033502 Service-Version: 1.19
A late change in microsoft#16105 wrapped `_buffer` into a class to better track its dirty state, but I failed to notice that in this one instance we intentionally manipulated `_buffer` without marking it as dirty. This fixes the issue by adding a call to `MarkAsClean()`. This changeset also adds the test instructions from microsoft#15783 as a document to this repository. I've extended the list with two bugs we've found in the implementation since then. ## Validation Steps Performed * In cmd.exe, with an empty prompt in an empty directory: Pressing tab produces an audible bing and prints no text ✅
The initial cooked read (= conhost readline) rewrite had two flaws: * Using viewport scrolls under ConPTY to avoid emitting newlines resulted in various bugs around marks, coloring, etc. It's still somewhat unclear why this happened, but the next issue is related and much worse. * Rewriting the input line every time causes problems with accessibility tools, as they'll re-announce unchanged parts again and again. The solution to these is to simply stop writing the unchanged parts of the prompt. To do this, code was added to measure the size of text without actually inserting them into the buffer. Since this meant that the "interactive" mode of `WriteCharsLegacy` would need to be duplicated for the new code, I instead moved those parts into `COOKED_READ_DATA`. That way we can now have the interactive transform of the prompt (= Ctrl+C -> ^C) and the two text functions (measure text & actually write text) are now agnostic to this transformation. Closes microsoft#16034 Closes microsoft#16044 ## Validation Steps Performed * A vision impaired user checked it out and it seemed fine ✅
The initial cooked read (= conhost readline) rewrite had two flaws:
The solution to these is to simply stop writing the unchanged parts of the prompt. To do this, code was added to measure the size of text without actually inserting them into the buffer. Since this meant that the "interactive" mode of
WriteCharsLegacy
would need to be duplicated for the new code, I instead moved those parts intoCOOKED_READ_DATA
. That way we can now have the interactive transform of the prompt (= Ctrl+C -> ^C) and the two text functions (measure text & actually write text) are now agnostic to this transformation.Closes #16034
Closes #16044
Validation Steps Performed