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

Add support for ITU's T.416 - ODA SGR (38/48) sequence #15729

Merged
merged 13 commits into from Jul 21, 2023

Conversation

tusharsnx
Copy link
Contributor

@tusharsnx tusharsnx commented Jul 19, 2023

This PR adds support for ITU's T.416 - ODA SGR (38/48) colour sequence, which makes use of colon instead of semi-colon as a parameter separator.

  • We use semi-colons as the only parameter separator while sending SGR color sequences to a ConPTY client. This is to keep backward compatibility.
  • In response to DECRQSS query, we have decided to use colons, as the major usecase for such queries are feature detection (whether client supports ODA colours), and tracking the original separator may add too much complexity to the codebase.

Validation Steps Performed

  • Made sure that we are always sending semi-colon separated parameters regardless of whether the original sequence used colons.
  • Made sure that we are always using colons as the parameter separator in a DECRQSS response.
  • Added new tests!

Closes #15706

@microsoft-github-policy-service microsoft-github-policy-service bot added Issue-Task It's a feature request, but it doesn't really need a major design. Area-VT Virtual Terminal sequence support Product-Conhost For issues in the Console codebase labels Jul 19, 2023
src/buffer/out/TextColor.h Outdated Show resolved Hide resolved
@tusharsnx
Copy link
Contributor Author

tusharsnx commented Jul 19, 2023

I feel weird about adding new secret color types that indicate color shapes

Yeah, totally makes sense. And don't worry, this is not the final look of the PR 😄. I put it up so I get some early reviews and suggestion on how we'd like to go about implementing this.
The only reason I gave this implementation a shot was, maybe it'll better if I put the color and separator info together. But it seems it causes more confusion than any good to us 😅

I have few alternative ideas:

Separator info inside TextBuffer.

In this case, we can track the last received separator in any color sequence within TextBuffer. This should generally work as intended, though I need to check if I can implement this without any issues. As the only reason to store separator info is to be able to response back on queries with right separator, this should work fine.

Separator info inside TextAttribute.

If we keep it inside TextAttribute, maybe within _attr, which holds other attribute flags, there are two unused flags in it. We can use them to get the same effect as Index256Colon, IsRgbColon. We would need both the flags, each for Fg and Bg colour.

Let me know, which implementation sounds better to you 😀

@lhecker
Copy link
Member

lhecker commented Jul 19, 2023

I think it's fine to put the separator info into TextColor. We have COLORREF and til::color for all other, regular colors after all. 🙂

But I think I have an idea how to simplify it a bit. Instead of adding multiple new ColorTypes I believe you can add a boolean flag to the struct. To save space (we store a lot of TextAttributes after all), we can use a bitfield:

struct TextColor
{
    // ...

private:
    union
    {
        BYTE _red, _index;
    };
    BYTE _green;
    BYTE _blue;
    ColorType _meta : 7;
    BYTE _isColon : 1 = 0;
};

Now we can properly store it as if it was a boolean and simplify SetColon().
Additionally, instead of having a IsColon() function I would use a GetSeparator() one that returns a char - either ';' or ':'. This is IMO better than IsColon() because it's simply more practical: The meaning of what "separator" refers to will never change and it simplifies some code around the code base. If we ever do end up needing a IsColon() function we can just add it. With that in place we can write this:

const auto sep = color.GetSeparator();
fmt::format_to(std::back_inserter(response), FMT_COMPILE(L";{}{2}5{2}{}"), base + 8, index, sep);

(If you write {} {} {} then fmt::format will implicitly assume {0} {1} {2}. The {2} means that it will explicitly pick the 2nd (0-based index) variadic argument.)

@DHowett
Copy link
Member

DHowett commented Jul 19, 2023

(If you write {} {} {} then fmt::format will implicitly assume {0} {1} {2}. The {2} means that it will explicitly pick the 2nd (0-based index) variadic argument.)

FWIW, exposing the separator as a character will give us a false sense of equivalence.

It is not as simple as replacing ; with : in the report. The CSI sequence with subparameters is materially different:

Compatibility CSI 38 ; 2 ; Pr ; Pg ; Pb m
T.416 ODA     CSI 38 : 2 : Pi : Pr : Pg : Pb m

It just so happens that Pi (color space identifier) is empty and ignored.
So in reality, it looks like CSI 38:2::R:G:B m with the double colon.

I would swing the other way and call it IsT416ODAColor even though it is a mouthful. It encapsulates what we are actually tracking, not what it happened to look like at the time.

@lhecker
Copy link
Member

lhecker commented Jul 19, 2023

Oh I didn't notice that there are two :: in the one branch. I thought they're identical apart from the separator. In that case, please ignore the second half of my previous comment. 😅

@DHowett
Copy link
Member

DHowett commented Jul 19, 2023

I'm super cool with the bitfield version though - that's clever!

@tusharsnx
Copy link
Contributor Author

tusharsnx commented Jul 20, 2023

To save space (we store a lot of TextAttributes after all), we can use a bitfield.

ColorType _meta : 7;
BYTE _isColon : 1 = 0;

🤯 that's a TIL for me.

This is definitely a cleaner approach than mine! Thanks.

As @j4james suggested we don't need to track colour type, let's see if that works out for us. If not, we'll go ahead with bitfields approach then.

Copy link
Member

@lhecker lhecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another fantastic PR. Thanks! 🙂

src/terminal/adapter/adaptDispatchGraphics.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@j4james j4james left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a couple of suggestions, but nothing blocking. I'd be quite happy with this as it is.

src/terminal/adapter/DispatchTypes.hpp Outdated Show resolved Hide resolved
src/terminal/adapter/adaptDispatchGraphics.cpp Outdated Show resolved Hide resolved
src/terminal/adapter/adaptDispatchGraphics.cpp Outdated Show resolved Hide resolved
Copy link
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this! I'm going to give you a little bit to address open feedback before I merge this PR, but thank you so much for doing it.

My only nit -- and it's a small one -- is that I personally prefer SubParam instead of Alt. It makes it clearer when to use which one. I might even go so far as to call it _ApplyGraphicsOptionWithSubParams, _SetRgbColorsHelperFromSubParams

Up to you!

@tusharsnx
Copy link
Contributor Author

tusharsnx commented Jul 21, 2023

Pushed the requested changes.

I've also included some tests to ensure that we ignore colors if any of the following conditions are true:

  • One of the color components (Index, R, G, or B) is out of range.
  • The ColorSpaceID is not empty.

@DHowett DHowett merged commit 3de496d into microsoft:main Jul 21, 2023
12 checks passed
@DHowett
Copy link
Member

DHowett commented Jul 21, 2023

Oh, in case I didn't say it enough: HELL YES, THANK YOU. I've wanted this for EVER.

@tusharsnx tusharsnx deleted the feat-colon-truecolor branch July 22, 2023 06:12
zadjii-msft pushed a commit that referenced this pull request Aug 21, 2023
Add test for subparameter based `GraphicOptions`.

`GraphicsSingleWithSubParamTests` is added for subparameter based
`GraphicOptions`. This should've been included with #15729.

Also, while working on #15795, I realized creating and passing
subparameters for the tests is painful right now. I've added a small
util `MakeSubParamsAndRanges(...)` that eases creating subparameters and
subparameter ranges from a simple list of (lists of) subparameters.

## Validation Steps Performed
- All tests passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-VT Virtual Terminal sequence support Issue-Task It's a feature request, but it doesn't really need a major design. Product-Conhost For issues in the Console codebase
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support ITU's T.416 - ODA colour sequences
4 participants