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 the "blink" graphic rendition attribute #7490

Merged
9 commits merged into from
Sep 21, 2020

Conversation

j4james
Copy link
Collaborator

@j4james j4james commented Aug 31, 2020

This PR adds support for the blink graphic rendition attribute. When a
character is output with this attribute set, it "blinks" at a regular
interval, by cycling its color between the normal rendition and a dimmer
shade of that color.

The majority of the blinking mechanism is encapsulated in a new
BlinkingState class, which is shared between the Terminal and Conhost
implementations. This class keeps track of the position in the blinking
cycle, which determines whether characters are rendered as normal or
faint.

In Windows Terminal, the state is stored in the Terminal class, and in
Conhost it's stored in the CONSOLE_INFORMATION class. In both cases,
the IsBlinkingFaint method is used to determine the current blinking
rendition, and that is passed on as a parameter to the
TextAttribute::CalculateRgbColors method when these classes are
looking up attribute colors.

Prior to calculating the colors, the current attribute is also passed to
the RecordBlinkingUsage method, which keeps track of whether there are
actually any blink attributes in use. This is used to determine whether
the screen needs to be refreshed when the blinking cycle toggles between
the normal and faint renditions.

The refresh itself is handled by the ToggleBlinkingRendition method,
which is triggered by a timer. In Conhost this is just piggybacking on
the existing cursor blink timer, but in Windows Terminal it needs to
have its own separate timer, since the cursor timer is reset whenever a
key is pressed, which is not something we want for attribute blinking.

Although the ToggleBlinkingRendition is called at the same rate as the
cursor blinking, we actually only want the cells to blink at half that
frequency. We thus have a counter that cycles through four phases, and
blinking is rendered as faint for two of those four. Then every two
cycles - when the state changes - a redraw is triggered, but only if
there are actually blinking attributes in use (as previously recorded).

As mentioned earlier, the blinking frequency is based on the cursor
blink rate, so that means it'll automatically be disabled if a user has
set their cursor blink rate to none. It can also be disabled by turning
off the Show animations in Windows option. In Conhost these settings
take effect immediately, but in Windows Terminal they only apply when a
new tab is opened.

This PR also adds partial support for the SGR 6 rapid blink
attribute. This is not used by DEC terminals, but was defined in the
ECMA/ANSI standards. It's not widely supported, but many terminals just
it implement it as an alias for the regular SGR 5 blink attribute, so
that's what I've done here too.

Validation Steps Performed

I've checked the Graphic rendition test pattern in Vttest, and
compared our representation of the blink attribute to that of an actual
DEC VT220 terminal as seen on YouTube. With the right color scheme
it's a reasonably close match.

Closes #7388

@ghost ghost added 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 Product-Terminal The new Windows Terminal. labels Aug 31, 2020
@j4james
Copy link
Collaborator Author

j4james commented Sep 1, 2020

For comparison with the DEC VT220 video mentioned above, here's a gif of the Terminal output with an appropriate color scheme, and with retro effects enabled:
blinking

src/host/settings.hpp Outdated Show resolved Hide resolved
@PankajBhojwani
Copy link
Contributor

Thanks for this, it looks amazing

Had a question - should we initialize our own timer if the user has disabled cursor blinking (but has enabled animations)? I'm wondering about the use case where someone does not want their cursor to blink but still wants text with this attribute to blink.

@j4james
Copy link
Collaborator Author

j4james commented Sep 1, 2020

Had a question - should we initialize our own timer if the user has disabled cursor blinking (but has enabled animations)? I'm wondering about the use case where someone does not want their cursor to blink but still wants text with this attribute to blink.

I've essentially replied to this in the comments above. But I should also point out that there have been quite a few concerns raised about the accessibility implications of this feature. So if there's any hint from the user that they don't like blinking (e.g. setting the cursor blink rate to none), I think it's probably better to be on the safe side and assume they don't want any blinking at all.

But again, I'm OK being overridden on this point if you all disagree.

@skyline75489
Copy link
Collaborator

@j4james FYI Dustin and Mike are currently out of the office right now. I'm sure they'd love to review a PR like this when they're available.

src/renderer/base/BlinkingState.cpp Outdated Show resolved Hide resolved
// - The color values of the attribute's foreground and background.
std::pair<COLORREF, COLORREF> CONSOLE_INFORMATION::LookupAttributeColors(const TextAttribute& attr) const noexcept
{
_blinkingState.RecordBlinkingUsage(attr);
Copy link
Member

Choose a reason for hiding this comment

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

clever clearing it during a render cycle on-blink and using the function that resolves the color to tell whether we should keep blinking.

It took me a little bit to understand what was going on (and why I was worried that we would keep doing a full redraw every other cursor blink for all eternity), but... I like it.

It might want to be documented in a comment (sorry if it already has: I'm reading bottom-up).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, I've tried to explain this in the doc comment for RecordBlinkingUsage and then there's more detail in the internal comments of ToggleBlinkingRendition as well.

src/host/CursorBlinker.cpp Outdated Show resolved Hide resolved
src/cascadia/TerminalControl/TermControl.cpp Show resolved Hide resolved
@DHowett
Copy link
Member

DHowett commented Sep 18, 2020

Sorry to take so long to review this!

Copy link
Member

@miniksa miniksa left a comment

Choose a reason for hiding this comment

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

Excellent. Thank you, as always, James. I appreciate the choices you made here.

@DHowett DHowett added the AutoMerge Marked for automatic merge by the bot when requirements are met label Sep 21, 2020
@ghost
Copy link

ghost commented Sep 21, 2020

Hello @DHowett!

Because this pull request has the AutoMerge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit d1671a0 into microsoft:master Sep 21, 2020
DHowett pushed a commit that referenced this pull request Sep 21, 2020
This PR adds support for the _blink_ graphic rendition attribute. When a
character is output with this attribute set, it "blinks" at a regular
interval, by cycling its color between the normal rendition and a dimmer
shade of that color.

The majority of the blinking mechanism is encapsulated in a new
`BlinkingState` class, which is shared between the Terminal and Conhost
implementations. This class keeps track of the position in the blinking
cycle, which determines whether characters are rendered as normal or
faint.

In Windows Terminal, the state is stored in the `Terminal` class, and in
Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases,
the `IsBlinkingFaint` method is used to determine the current blinking
rendition, and that is passed on as a parameter to the
`TextAttribute::CalculateRgbColors` method when these classes are
looking up attribute colors.

Prior to calculating the colors, the current attribute is also passed to
the `RecordBlinkingUsage` method, which keeps track of whether there are
actually any blink attributes in use. This is used to determine whether
the screen needs to be refreshed when the blinking cycle toggles between
the normal and faint renditions.

The refresh itself is handled by the `ToggleBlinkingRendition` method,
which is triggered by a timer. In Conhost this is just piggybacking on
the existing cursor blink timer, but in Windows Terminal it needs to
have its own separate timer, since the cursor timer is reset whenever a
key is pressed, which is not something we want for attribute blinking.

Although the `ToggleBlinkingRendition` is called at the same rate as the
cursor blinking, we actually only want the cells to blink at half that
frequency. We thus have a counter that cycles through four phases, and
blinking is rendered as faint for two of those four. Then every two
cycles - when the state changes - a redraw is triggered, but only if
there are actually blinking attributes in use (as previously recorded).

As mentioned earlier, the blinking frequency is based on the cursor
blink rate, so that means it'll automatically be disabled if a user has
set their cursor blink rate to none. It can also be disabled by turning
off the _Show animations in Windows_ option. In Conhost these settings
take effect immediately, but in Windows Terminal they only apply when a
new tab is opened.

This PR also adds partial support for the `SGR 6` _rapid blink_
attribute. This is not used by DEC terminals, but was defined in the
ECMA/ANSI standards. It's not widely supported, but many terminals just
it implement it as an alias for the regular `SGR 5` blink attribute, so
that's what I've done here too.

## Validation Steps Performed

I've checked the _Graphic rendition test pattern_ in Vttest, and
compared our representation of the blink attribute to that of an actual
DEC VT220 terminal as seen on [YouTube]. With the right color scheme
it's a reasonably close match.

[YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s

Closes #7388

(cherry picked from commit d1671a0)
@ghost
Copy link

ghost commented Sep 22, 2020

🎉Windows Terminal Preview v1.4.2652.0 has been released which incorporates this pull request.:tada:

Handy links:

This pull request was closed.
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 AutoMerge Marked for automatic merge by the bot when requirements are met Issue-Task It's a feature request, but it doesn't really need a major design. Product-Conhost For issues in the Console codebase Product-Terminal The new Windows Terminal.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for the "blink" graphic rendition attribute
5 participants