-
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
Add support for XTPUSHSGR / XTPOPSGR #1978
Add support for XTPUSHSGR / XTPOPSGR #1978
Conversation
The maximum number of saved states is 10 (like xterm). Perhaps I could additionally place a (much higher) limit on the number of allowed pushes (say 10,000). With that bound, you know that you need only do 10,001 pops to be sure that you've recovered from an unbalanced state. And then we also wouldn't have to worry about the push counter wrapping around. |
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.
So I think the right way of implementing this at the moment would be to implement it not in the Terminal (with TerminalApi
and TerminalDispatch
) but instead in conhost, with adaptDispatch
. If you're able to make the changes there, then conhost should be able to "render" the changed state correctly to the terminal, without the terminal even needing to process these messages itself (though, leaving the implementation there isn't necessarily a bad idea, as the terminal will want to be able to handle these messages itself one day).
I believe that if you return false
in AdaptDispatch, conpty will just let the sequence fallthrough to the connected terminal.
So maybe finish implementing it in the Terminal first, while conhost is letting the sequences fall through, then implement again in conhost (which will probably be basically the same implementation), and make sure it keeps working.
This is not a polished PR that is ready to merge; it demonstrates a direction for which I need to get buy-off from the team before pursuing further. I'm currently working on implementing the XTPUSHSGR/XTPOPSGR control sequences (WIP PR [here](microsoft#1978), which requires saving a [stack of] text attributes, and not just the legacy attributes, but full RGB colors, etc. My first instinct was to implement the "business logic" (the stack) in the `AdaptDispatch` layer, but that will require getting the [full] text attributes from the underlying console API. This is not a *terribly* "invasive" change, but it is exposing new stuff at a layer boundary. Put another way, this means pound-including the "../buffer/out/TextAttribute.hpp" header in a few places outside of "buffer/out", and is that okay, or does that header need to be kept private and isolated? So there are a few ways this could go: 1. You folks might say "ugh, no!", and: 1. I could push (haha) the business logic of pushing and popping text attributes down another layer (so `AdaptDispatch` just forwards on the [PushGraphicsRendition](https://github.com/microsoft/terminal/blob/0af275b9cb68da14f38f05b2cdcbb35da99cb17c/src/terminal/adapter/adaptDispatchGraphics.cpp#L452) call to the lower layer, OR 2. You suggest a different, cleaner way of exposing the text attributes. OR 2. Maybe you think this general direction is fine, but maybe you have some particular requests that I do certain things differently (I totally understand being picky about stuff that cuts across API layers). What do you think?
This is not a polished PR that is ready to merge; it demonstrates a direction for which I need to get buy-off from the team before pursuing further. I'm currently working on implementing the XTPUSHSGR/XTPOPSGR control sequences (WIP PR [here](microsoft#1978), which requires saving a [stack of] text attributes, and not just the legacy attributes, but full RGB colors, etc. My first instinct was to implement the "business logic" (the stack) in the `AdaptDispatch` layer, but that will require getting the [full] text attributes from the underlying console API. This is not a *terribly* "invasive" change, but it is exposing new stuff at a layer boundary. Put another way, this means pound-including the "../buffer/out/TextAttribute.hpp" header in a few places outside of "buffer/out", and is that okay, or does that header need to be kept private and isolated? So there are a few ways this could go: 1. You folks might say "ugh, no!", and: 1. I could push (haha) the business logic of pushing and popping text attributes down another layer (so `AdaptDispatch` just forwards on the [PushGraphicsRendition](https://github.com/microsoft/terminal/blob/0af275b9cb68da14f38f05b2cdcbb35da99cb17c/src/terminal/adapter/adaptDispatchGraphics.cpp#L452) call to the lower layer, OR 2. You suggest a different, cleaner way of exposing the text attributes. OR 2. Maybe you think this general direction is fine, but maybe you have some particular requests that I do certain things differently (I totally understand being picky about stuff that cuts across API layers). What do you think? Related: PR microsoft#1978 Related: Issue microsoft#1796
The RawUI's LengthInBufferCells needs to treat VT control sequences as zero-width for the purpose of determining string length. Previously, it only handled SGR (Select Graphics Rendition) sequences (which do things like set fg color, bg color, etc.). I'm currently adding support for some new control sequences in the microsoft/terminal project: XTPUSHSGR and XTPOPSGR. Initial WIP PR [here](microsoft/terminal#1978). Summarized descriptions of XTPUSHSGR and XTPOPSGR from XTerm's [ctlseqs](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html) documentation: ``` CSI # { CSI Ps ; Ps # { Push video attributes onto stack (XTPUSHSGR), xterm. CSI # } Pop video attributes from stack (XTPOPSGR), xterm. Popping restores the video-attributes which were saved using XTPUSHSGR to their previous state. CSI # p CSI Ps ; Ps # p Push video attributes onto stack (XTPUSHSGR), xterm. This is an alias for CSI # {. CSI # q Pop video attributes from stack (XTPOPSGR), xterm. This is an alias for CSI # }. ``` The scenario enabled by these sequences is composability (see [Issue 1796](microsoft/terminal#1796)). I'd like to support these sequences in PowerShell, similarly to SGR sequences, to enable better SGR content composability.
0af275b
to
f07a68b
Compare
f07a68b
to
17c4bd6
Compare
@zadjii-msft I think this PR is ready to go. |
f61d3eb
to
76e9df9
Compare
c1e5a12
to
8f18939
Compare
29c3640
to
fa93100
Compare
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.
All minor things, but I have some questions 😄
src/buffer/out/sgrStack.hpp
Outdated
} | ||
} | ||
|
||
if (_GraphicsOptionToFlag(DispatchTypes::GraphicsOptions::SaveForegroundColor) & validParts) |
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 list of attributes may need to be extended to support strikethrough/italic/the other extended text attributes introduced in #2917.
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.
Done.
d5de9a4
to
afcf1d4
Compare
Did we end up converging on option 3? My lingering doubt has been quelled by one simple fact: this is now an xterm compatibility concern. Once I thought of it that way, it became obvious that we need to do something. |
This change adds a new pair of methods to ITermDispatch: PushGraphicsRendition and PopGraphicsRendition, and then plumbs the change through AdaptDispatch, TerminalDispatch, ITerminalApi and TerminalApi. The stack logic is encapsulated in the SgrStack class, to allow it to be reused between the two APIs (AdaptDispatch and TerminalDispatch). Like xterm, only ten levels of nesting are supported. The stack is implemented as a "ring stack": if you push when the stack is full, the bottom of the stack will be dropped to make room.
afcf1d4
to
ffcb365
Compare
@DHowett as things stand in this PR, what I actually implemented is option 1. Which, in terms of this being an xterm compat issue, seems reasonable--it matches xterm right now, and if later people come to a different consensus, then we could change it. But I'd also be happy to dike out the partial saves if it meant getting this feature in. |
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.
My concerns are mostly around comments, exceptions and line endings. This is beautiful work, and we've done it a disservice by not driving it to ground.
James can double-check me on how I feel about VTParameter's value_or
, if he wants. 😄
if (_nextPushIndex == 0) | ||
{ | ||
_nextPushIndex = gsl::narrow<int>(_storedSgrAttributes.size() - 1); | ||
} | ||
else | ||
{ | ||
_nextPushIndex--; | ||
} |
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.
nit: technically, you can do a wraparound mod math trick here:
_nextPushIndex = (size + _nextPushIndex - 1) % size
That's if concision was the goal, of course. It's not necessarily the goal!
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.
Hmmm... I waffled on this. I lean toward the more verbose, but I'd change it if y'all had a strong opinion.
Here's what I'm thinking. Option 1 is "compatible", and we can see how much use it gets in the wild. I suspect that we can move to option 3 and treat partials as fulls if we have to. Let's celebrate incremental improvement, shall we? (Does that mollify your main concerns, or at least enough to accept it for the time being, @j4james?) |
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'm fine with this pending Dustin's comments.
I think you might have the options the wrong way around? Option 3 is the partial implementation, and option 1 is essentially the full thing. I could understand starting with 3 and later moving to 1, but going in the other direction doesn't really make sense.
My dislike of this feature has only grown more intense over time, so I don't think it's worth trying to mollify me. Also, with Mintty having implemented it now too, it sort of feels like a lost battle. If you guys all like it, I'm not going to argue against it. |
I appreciate that and respect your candor. 🙂 You're right, of course, about Option 1. I had it in my head that we could do the "more compatible" thing (1) and then if it didn't hold up to scrutiny or seem to be as well-used in the wild just gank the features that were controversial (getting us to 3). |
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.
@jazzdelightsme Thanks for putting up with our literally almost two year long review cycle. I'm merging this today despite some community misgivings, but I do not believe they deeply detract from what's otherwise a solid feature. I get why we would want this, and I get why xterm wanted it and mintty probably wanted it, so I think we shouldn't stand against progress. We can always revisit our decisions -- nothing in this repository is etched in stone.
🎉 Handy links: |
This is available in conhost as of Windows Insider Preview build 21337. Thanks again! 😄 |
Summary of the Pull Request
Implement the
XTPUSHSGR
andXTPOPSGR
control sequences (see #1796).This change adds a new pair of methods to
ITermDispatch
:PushGraphicsRendition
andPopGraphicsRendition
, and then plumbs thechange through
AdaptDispatch
,TerminalDispatch
,ITerminalApi
andTerminalApi
.The stack logic is encapsulated in the
SgrStack
class, to allow it tobe reused between the two APIs (
AdaptDispatch
andTerminalDispatch
).Like xterm, only ten levels of nesting are supported.
The stack is implemented as a "ring stack": if you push when the stack
is full, the bottom of the stack will be dropped to make room.
Partial pushes (see the description of
XTPUSHSGR
in Issue #1796) areimplemented per xterm spec.
Validation Steps Performed
Tests added, plus manual verification of the feature.
Closes #1796