ConPTY: Ask for the cursor position after each resize #23071

Closed
opened 2026-01-31 08:31:25 +00:00 by claunia · 8 comments
Owner

Originally created by @lhecker on GitHub (Mar 26, 2025).

Originally assigned to: @lhecker on GitHub.

Description of the new feature

ConPTY's reflow algorithm may differ from the hosting terminal and that may result in ConPTY returning wildly incorrect cursor positions via GetConsoleCursorInfo. This results in the cursor being inside the previous prompt when resizing PowerShell in VS Code.

Proposed technical implementation details

Use the existing DSC CPR facilities in VtIo but make them more flexible so that they can be called more than once.

Complications:

  • The request must block the console server
  • ...but it must not block further incoming resizes (= debounce)
Originally created by @lhecker on GitHub (Mar 26, 2025). Originally assigned to: @lhecker on GitHub. ### Description of the new feature ConPTY's reflow algorithm may differ from the hosting terminal and that may result in ConPTY returning wildly incorrect cursor positions via `GetConsoleCursorInfo`. This results in the cursor being inside the previous prompt when resizing PowerShell in VS Code. ### Proposed technical implementation details Use the existing DSC CPR facilities in `VtIo` but make them more flexible so that they can be called more than once. Complications: * The request must block the console server * ...but it must not block further incoming resizes (= debounce)
Author
Owner

@tusharsnx commented on GitHub (Sep 17, 2025):

From #19237 (Revert "ConPTY: Emit DSR CPR on resize"):

Reopens https://github.com/microsoft/terminal/issues/18725

Looks like this issue need to be reopened.

@tusharsnx commented on GitHub (Sep 17, 2025): From #19237 (Revert "ConPTY: Emit DSR CPR on resize"): > Reopens https://github.com/microsoft/terminal/issues/18725 Looks like this issue need to be reopened.
Author
Owner

@j4james commented on GitHub (Sep 18, 2025):

Just brainstorming here, but one idea I had was that we could set a kind of "dirty cursor" flag when the window is resized, without triggering the DSR-CPR query immediately. Then we only need to submit the DSR-CPR query if/when someone actually makes a GetConsoleCursorInfo call while that flag is still set. And if the cursor position is set explicitly before that happens (e.g. with a CUP sequence), we could potentially reset the flag without requiring a DSR-CPR query at all.

@j4james commented on GitHub (Sep 18, 2025): Just brainstorming here, but one idea I had was that we could set a kind of "dirty cursor" flag when the window is resized, without triggering the `DSR-CPR` query immediately. Then we only need to submit the `DSR-CPR` query if/when someone actually makes a `GetConsoleCursorInfo` call while that flag is still set. And if the cursor position is set explicitly before that happens (e.g. with a `CUP` sequence), we could potentially reset the flag without requiring a `DSR-CPR` query at all.
Author
Owner

@lhecker commented on GitHub (Nov 6, 2025):

I started implementing your idea, but I wasn't too happy with how it would release the console lock, wait for the VT input thread to set a flag, etc. - just like how we already do on startup. (...which I'm also a bit squirmish about. In our current architecture it all seems very roundabout.)

So, I went with adding a ResizePseudoConsole2 API where the new cursor position gets transmitted as part of the resize. Let me know if you have concerns with that!

@lhecker commented on GitHub (Nov 6, 2025): I started implementing your idea, but I wasn't too happy with how it would release the console lock, wait for the VT input thread to set a flag, etc. - just like how we already do on startup. (...which I'm also a bit squirmish about. In our current architecture it all seems very roundabout.) So, I went with adding a `ResizePseudoConsole2` API where the new cursor position gets transmitted as part of the resize. Let me know if you have concerns with that!
Author
Owner

@lhecker commented on GitHub (Nov 6, 2025):

I think I just realized that the biggest reason xterm.js has this issue, is simply because we stopped emitting a CUP sequence on resize. I wonder if we should start doing that again...

@lhecker commented on GitHub (Nov 6, 2025): I think I just realized that the biggest reason xterm.js has this issue, is simply because we stopped emitting a CUP sequence on resize. I wonder if we should start doing that again...
Author
Owner

@j4james commented on GitHub (Nov 6, 2025):

I may be misunderstanding the issue here, but I thought the problem with third party terminals is that their resizing algorithm might leave the buffer and cursor position in a different state to what conhost thinks it should be. So when an app uses a win32 console API to query the cursor position, it can get the wrong coordinates.

But if there aren't any apps using console APIs, that doesn't matter. With the new conpty passthrough, VT apps should work perfectly regardless of whether conhost is in sync or not. And in that case, emitting a CUP may synchronize the cursor position, but it won't sync the buffer contents, so you'll have made things worse. The user will see the terminal's buffer, but with conhost's cursor position (which is in the wrong place).

That's why the query is the better solution. It synchronizes the cursor in the other direction, i.e. from the terminal back to conhost. And it doesn't matter as much that the conhost buffer is still out of sync, because the user can't see that (as long as there aren't any apps trying to read the buffer contents with console APIs).

@j4james commented on GitHub (Nov 6, 2025): I may be misunderstanding the issue here, but I thought the problem with third party terminals is that their resizing algorithm might leave the buffer and cursor position in a different state to what conhost thinks it should be. So when an app uses a win32 console API to query the cursor position, it can get the wrong coordinates. But if there aren't any apps using console APIs, that doesn't matter. With the new conpty passthrough, VT apps should work perfectly regardless of whether conhost is in sync or not. And in that case, emitting a `CUP` may synchronize the cursor position, but it won't sync the buffer contents, so you'll have made things worse. The user will see the terminal's buffer, but with conhost's cursor position (which is in the wrong place). That's why the query is the better solution. It synchronizes the cursor in the other direction, i.e. from the terminal back to conhost. And it doesn't matter as much that the conhost buffer is still out of sync, because the user can't see that (as long as there aren't any apps trying to read the buffer contents with console APIs).
Author
Owner

@j4james commented on GitHub (Nov 6, 2025):

It's worth noting that the cursor position problem can also arise with any escape sequence that isn't implemented by both conhost and the conpty terminal. For example, if an app tries to render an image using the iterm2 image protocol, conhost ignores it, but still passes it through to the terminal. The terminal might render the image and move the cursor down the page, but conhost will know nothing about that. So if your shell follows that up with a GetConsoleCursorInfo, it's going to place the prompt in the wrong position.

This is one of the reason I thought the "dirty cursor" flag might be a useful approach to take (assuming we could get it to work). Because if we also set that flag whenever we passed through an unrecognised VT sequence, it could potentially solve cases like this as well.

@j4james commented on GitHub (Nov 6, 2025): It's worth noting that the cursor position problem can also arise with any escape sequence that isn't implemented by both conhost and the conpty terminal. For example, if an app tries to render an image using the iterm2 image protocol, conhost ignores it, but still passes it through to the terminal. The terminal might render the image and move the cursor down the page, but conhost will know nothing about that. So if your shell follows that up with a `GetConsoleCursorInfo`, it's going to place the prompt in the wrong position. This is one of the reason I thought the "dirty cursor" flag might be a useful approach to take (assuming we could get it to work). Because if we also set that flag whenever we passed through an unrecognised VT sequence, it could potentially solve cases like this as well.
Author
Owner

@lhecker commented on GitHub (Nov 6, 2025):

I'm only squeamish about it because it may be blocking the IO thread for a while. That's ideally not something we should be doing.

That aside, I also realized another flaw with my approach: The alt & main buffer have separately reflowed cursor positions and both would need to get transmitted from the terminal to ConPTY. Using a dirty flag as you suggested avoids this issue, because the flag can easily be made per-ScreenInfo.

@lhecker commented on GitHub (Nov 6, 2025): I'm only squeamish about it because it may be blocking the IO thread for a while. That's ideally not something we should be doing. That aside, I also realized another flaw with my approach: The alt & main buffer have separately reflowed cursor positions and both would need to get transmitted from the terminal to ConPTY. Using a dirty flag as you suggested avoids this issue, because the flag can easily be made per-ScreenInfo.
Author
Owner

@j4james commented on GitHub (Nov 6, 2025):

I'm only squeamish about it because it may be blocking the IO thread for a while. That's ideally not something we should be doing.

Yeah, I completely understand your concern. I was just throwing it out as an idea, because I figured you might be able to come up with a way to make it work. And I think some kind of query is the only way we're going to get a reliable cursor position for third party terminals when using console APIs. Except maybe your in-process conpty idea, but I just can't imagine many third party terminals wanting to go to that much effort to support Windows.

@j4james commented on GitHub (Nov 6, 2025): > I'm only squeamish about it because it may be blocking the IO thread for a while. That's ideally not something we should be doing. Yeah, I completely understand your concern. I was just throwing it out as an idea, because I figured you might be able to come up with a way to make it work. And I think _some_ kind of query is the only way we're going to get a reliable cursor position for third party terminals when using console APIs. Except maybe your in-process conpty idea, but I just can't imagine many third party terminals wanting to go to that much effort to support Windows.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#23071