-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
ReadConsoleOutputW doesn't work with surrogate pairs (the buffer layers thread) #10810
Comments
@miniksa for the response |
Okay, so here's what I said during triage that the team told me to put here:
|
Thank you for the explanation, I agree that it's a PITA from a dev perspective. The We need it to provide a terminal session look & feel inside a file manager.
Obviously, to be able to bring back that output by demand we need to read it from the console once the command finishes executing. Once we have that snapshot we can draw the UI controls (e.g. panels) on top of it and continue. Later, if the user wishes to see it, we flush the saved output back to console. Once the user executes something else we take a new snapshot. This design pattern (instant access to the output) has been around since Norton Commander / the 80s and is ridiculously convenient. Now compare it with a system that historically doesn't have any "readback" notion:
Without readback you can have either the UI or the terminal, but not both simultaneously. What can be done:As I mentioned earlier, we don't really care what
Alternatively, With this approach it would be possible not only to keep the existing functionality, but also to support all the existing and future extensions (true color, text styles, surrogates etc.) without exposing any new structures or any implementation details. |
Aha. That is interesting now to better understand the scenario. One of the troubles right now I'm having with the "passthrough" mode that I made during our fix-hack-learn week a few weeks ago is that the "popups" that occur in a "Cooked Read" (like when CMD.exe asks conhost to do the edit line on its behalf... as opposed to Powershell that does it itself with the raw reads. Press the F keys in CMD.exe after you've run a few commands to see a popup in progress). We have the exact same problem there. We want to backup a region of the screen, draw over the top of it, and then dismiss it and have what was behind it get restored. I wonder if there's anything in https://invisible-island.net/xterm/ctlseqs/ctlseqs.html that would help us here if we implemented it. When I briefly discussed this particular problem of having an "overlay" sort of layer that can be non-destructively dismissed... We were thinking it is sort of like requesting a move to an "alternate screen buffer" except one where we copy the main buffer to the alternate on entry, destructively draw over the alternate, then pop back to the main one when the "overlay" is complete. I'm not sure there is a VT sequence for "copy main into alternate while entering" but we should perhaps dig around and see if we can find anything close to this paradigm and if not, engage with some of the other terminals out there to see either how they handle this or if we can all agree on a reasonable extension. The cropping if the user resizes is also interesting as well. I'm not sure that my solution immediately covers that. |
Yes, it's the same idea as those F2/F4/F7/F9 popups. Also, // Take a snapshot of the current viewport
HANDLE WINAPI CreateConsoleScreenBuffer(
DWORD DesiredAccess, // Ignore
DWORD ShareMode, // Ignore
const SECURITY_ATTRIBUTES *Attributes, // Ignore
DWORD Flags, // CONSOLE_SNAPSHOT_BUFFER
LPVOID ScreenBufferData // Ignore
); // Write the taken snapshot into the current viewport
BOOL WINAPI SetConsoleActiveScreenBuffer(
HANDLE hConsoleOutput // A handle to a CONSOLE_SNAPSHOT_BUFFER
); // Discard the allocated snapshot
BOOL CloseHandle(
HANDLE hObject // A handle to a CONSOLE_SNAPSHOT_BUFFER
);
|
Dude. I love this. This sounds completely doable. Adding one constant to an existing flag field and not changing the method signatures at all is the level of risk I'll happily take with the existing API surface. |
FYI, there is actually a way to read the screen to some extent using VT sequences - it's just very inconvenient, and for those terminals that do support it, it's often disabled because it's considered a security risk. Essentially what you do is use the As for reading attributes, XTerm added the Obviously you can see this wouldn't be a very efficient way to read the entire screen, so I'm not sure it's a practical solution. But I thought it was worth mentioning, because if |
Yeah @j4james... I agree both on the efficiency and security points. That's why I'm excited to understand the scenario as a sort of "keep a backup for me, let me draw on top as an overlay, then restore it." I think the better route to follow is to allow for an alternate screen buffer that copies the main one on entry so it can be destructively painted then restored by returning to the main one. |
tl;dr for folks coming to the thread late:
We discussed this a bit more yesterday. The "create a screen buffer which is a copy of the current one" seemed like a good consensus. Whether that's with If that's something that might work for Far, we can try and prototype something here. |
@zadjii-msft Thanks for getting back to this. To summarise, this is what we do now:
This worked nice for decades, but now those CHAR_INFOs can't represent all the information on the screen (because of new colours and text styles) and also there are issues with surrogates like the described above, so reading and writing them back is no longer lossless. With opaque "snapshots":
Essentially it's almost the same workflow, except for the bottom right corner, describing the case when we want to display both the output and the UI simultaneously (see the screenshot above). |
I was just doing some research on the rectangular editing operations ( The key point to note is that the Copy Rectangular Area operation ( I need to do some testing on terminals that actually implement these operations to see if they work the way I think they do, but they sound promising. |
I've had a chance to test this now on a couple of different terminal emulators, and most work really well. MLTerm had issues restoring the "blank" parts of the screen, if you're tried to use an overlay immediately after startup, but once the first page had scrolled out of view it seemed to work fine. Here's a short demo from one of the terminals I tested. Terminal.Layers.mp4 |
Wow that's a GREAT solution. That sounds like the perfect way to save off a section of the buffer and restore it. I suppose some subprocess could hijack the page that you stashed buffer contents into, but meh? That seems like something that's worth at least exploring more. Seems like a way easier way to try to do overlays in a shell. |
This PR adds support for multiples pages in the VT architecture, along with new operations for moving between those pages: `NP` (Next Page), `PP` (Preceding Page), `PPA` (Page Position Absolute), `PPR` (Page Position Relative), and `PPB` (Page Position Back). There's also a new mode, `DECPCCM` (Page Cursor Coupling Mode), which determines whether or not the active page is also the visible page, and a new query sequence, `DECRQDE` (Request Displayed Extent), which can be used to query the visible page. ## References and Relevant Issues When combined with `DECCRA` (Copy Rectangular Area), which can copy between pages, you can layer content on top of existing output, and still restore the original data afterwards. So this could serve as an alternative solution to #10810. ## Detailed Description of the Pull Request / Additional comments On the original DEC terminals that supported paging, you couldn't have both paging and scrollback at the same time - only the one or the other. But modern terminals typically allow both, so we support that too. The way it works, the currently visible page will be attached to the scrollback, and any content that scrolls off the top will thus be saved. But the background pages will not have scrollback, so their content is lost if it scrolls off the top. And when the screen is resized, only the visible page will be reflowed. Background pages are not affected by a resize until they become active. At that point they just receive the traditional style of resize, where the content is clipped or padded to match the new dimensions. I'm not sure this is the best way to handle resizing, but we can always consider other approaches once people have had a chance to try it out. ## Validation Steps Performed I've added some unit tests covering the new operations, and also done a lot of manual testing. Closes #13892 Tests added/passed
…ConsoleOutput See microsoft/terminal#10810 for details
Windows Terminal version (or Windows build number)
10.0.19043.1110
Other Software
No response
Steps to reproduce
Compile and run the following code:
Expected Behavior
Actual Behavior
Observations
The problem is here:
terminal/src/host/directio.cpp
Line 848 in 3f5f37d
AsCharInfo calls Utf16ToUcs2 for the same string over and over:
terminal/src/host/consoleInformation.cpp
Line 408 in 3f5f37d
which returns UNICODE_REPLACEMENT:
terminal/src/types/convert.cpp
Line 172 in 3f5f37d
Additionally, AsCharInfo incorrectly sets COMMON_LVB_LEADING_BYTE in this case:
terminal/src/host/consoleInformation.cpp
Line 415 in 3f5f37d
I assume that the host might use
Attribute::Leading
andAttribute::Trailing
internally for its own purposes, butCOMMON_LVB_LEADING_BYTE
&COMMON_LVB_TRAILING_BYTE
have a different purpose, not applicable in this case and shouldn't leak into the public interfaces for surrogates.A possible fix
P.S. I remember that there's #8000 that should improve the situation with surrogates in general. This one is about the API issue: when a surrogate pair occupies two columns, there's really no reason to return two replacement characters if we could return the original pair. The situation with a pair that occupies one column is less obvious, but that's a different issue.
The text was updated successfully, but these errors were encountered: