Add support for the "blink" graphic rendition attribute (#7490)
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
This commit is contained in:
parent
206131d83a
commit
d1671a0acd
|
@ -857,6 +857,7 @@ GETAUTOHIDEBAREX
|
|||
GETCARETWIDTH
|
||||
getch
|
||||
getchar
|
||||
GETCLIENTAREAANIMATION
|
||||
GETCOMMANDHISTORY
|
||||
GETCOMMANDHISTORYLENGTH
|
||||
GETCONSOLEINPUT
|
||||
|
|
|
@ -88,16 +88,18 @@ bool TextAttribute::IsLegacy() const noexcept
|
|||
// - defaultFgColor: the default foreground color rgb value.
|
||||
// - defaultBgColor: the default background color rgb value.
|
||||
// - reverseScreenMode: true if the screen mode is reversed.
|
||||
// - blinkingIsFaint: true if blinking should be interpreted as faint.
|
||||
// Return Value:
|
||||
// - the foreground and background colors that should be displayed.
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode) const noexcept
|
||||
const bool reverseScreenMode,
|
||||
const bool blinkingIsFaint) const noexcept
|
||||
{
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgColor, IsBold());
|
||||
auto bg = _background.GetColor(colorTable, defaultBgColor);
|
||||
if (IsFaint())
|
||||
if (IsFaint() || (IsBlinking() && blinkingIsFaint))
|
||||
{
|
||||
fg = (fg >> 1) & 0x7F7F7F; // Divide foreground color components by two.
|
||||
}
|
||||
|
|
|
@ -69,7 +69,8 @@ public:
|
|||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode = false) const noexcept;
|
||||
const bool reverseScreenMode = false,
|
||||
const bool blinkingIsFaint = false) const noexcept;
|
||||
|
||||
bool IsLeadingByte() const noexcept;
|
||||
bool IsTrailingByte() const noexcept;
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
_actualFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false },
|
||||
_touchAnchor{ std::nullopt },
|
||||
_cursorTimer{},
|
||||
_blinkTimer{},
|
||||
_lastMouseClickTimestamp{},
|
||||
_lastMouseClickPos{},
|
||||
_selectionNeedsToBeCopied{ false },
|
||||
|
@ -721,6 +722,24 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
_cursorTimer = std::nullopt;
|
||||
}
|
||||
|
||||
// Set up blinking attributes
|
||||
BOOL animationsEnabled = TRUE;
|
||||
SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0);
|
||||
if (animationsEnabled && blinkTime != INFINITE)
|
||||
{
|
||||
// Create a timer
|
||||
DispatcherTimer blinkTimer;
|
||||
blinkTimer.Interval(std::chrono::milliseconds(blinkTime));
|
||||
blinkTimer.Tick({ get_weak(), &TermControl::_BlinkTimerTick });
|
||||
blinkTimer.Start();
|
||||
_blinkTimer.emplace(std::move(blinkTimer));
|
||||
}
|
||||
else
|
||||
{
|
||||
// The user has disabled blinking
|
||||
_blinkTimer = std::nullopt;
|
||||
}
|
||||
|
||||
// import value from WinUser (convert from milli-seconds to micro-seconds)
|
||||
_multiClickTimer = GetDoubleClickTime() * 1000;
|
||||
|
||||
|
@ -1777,6 +1796,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
_cursorTimer.value().Start();
|
||||
}
|
||||
|
||||
if (_blinkTimer.has_value())
|
||||
{
|
||||
_blinkTimer.value().Start();
|
||||
}
|
||||
|
||||
_UpdateSystemParameterSettings();
|
||||
}
|
||||
|
||||
|
@ -1822,6 +1846,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
_cursorTimer.value().Stop();
|
||||
_terminal->SetCursorOn(false);
|
||||
}
|
||||
|
||||
if (_blinkTimer.has_value())
|
||||
{
|
||||
_blinkTimer.value().Stop();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -2035,6 +2064,22 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
_terminal->SetCursorOn(!_terminal->IsCursorOn());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Toggle the blinking rendition state when called by the blink timer.
|
||||
// Arguments:
|
||||
// - sender: not used
|
||||
// - e: not used
|
||||
void TermControl::_BlinkTimerTick(Windows::Foundation::IInspectable const& /* sender */,
|
||||
Windows::Foundation::IInspectable const& /* e */)
|
||||
{
|
||||
if (!_closing)
|
||||
{
|
||||
auto& renderTarget = *_renderer;
|
||||
auto& blinkingState = _terminal->GetBlinkingState();
|
||||
blinkingState.ToggleBlinkingRendition(renderTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
|
||||
// Arguments:
|
||||
|
|
|
@ -201,6 +201,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
std::optional<wchar_t> _leadingSurrogate;
|
||||
|
||||
std::optional<Windows::UI::Xaml::DispatcherTimer> _cursorTimer;
|
||||
std::optional<Windows::UI::Xaml::DispatcherTimer> _blinkTimer;
|
||||
|
||||
// If this is set, then we assume we are in the middle of panning the
|
||||
// viewport via touch input.
|
||||
|
@ -251,6 +252,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
void _HyperlinkHandler(const std::wstring_view uri);
|
||||
|
||||
void _CursorTimerTick(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
|
||||
void _BlinkTimerTick(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
|
||||
void _SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition);
|
||||
void _SendInputToConnection(const winrt::hstring& wstr);
|
||||
void _SendInputToConnection(std::wstring_view wstr);
|
||||
|
|
|
@ -1036,3 +1036,8 @@ const std::optional<til::color> Terminal::GetTabColor() const noexcept
|
|||
{
|
||||
return _tabColor;
|
||||
}
|
||||
|
||||
BlinkingState& Terminal::GetBlinkingState() const noexcept
|
||||
{
|
||||
return _blinkingState;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <conattrs.hpp>
|
||||
|
||||
#include "../../buffer/out/textBuffer.hpp"
|
||||
#include "../../renderer/inc/IRenderData.hpp"
|
||||
#include "../../renderer/inc/BlinkingState.hpp"
|
||||
#include "../../terminal/parser/StateMachine.hpp"
|
||||
#include "../../terminal/input/terminalInput.hpp"
|
||||
|
||||
|
@ -187,6 +187,8 @@ public:
|
|||
|
||||
const std::optional<til::color> GetTabColor() const noexcept;
|
||||
|
||||
Microsoft::Console::Render::BlinkingState& GetBlinkingState() const noexcept;
|
||||
|
||||
#pragma region TextSelection
|
||||
// These methods are defined in TerminalSelection.cpp
|
||||
enum class SelectionExpansionMode
|
||||
|
@ -224,6 +226,7 @@ private:
|
|||
COLORREF _defaultBg;
|
||||
CursorType _defaultCursorShape;
|
||||
bool _screenReversed;
|
||||
mutable Microsoft::Console::Render::BlinkingState _blinkingState;
|
||||
|
||||
bool _snapOnInput;
|
||||
bool _altGrAliasing;
|
||||
|
|
|
@ -132,6 +132,7 @@ bool TerminalDispatch::SetGraphicsRendition(const gsl::span<const DispatchTypes:
|
|||
attr.SetItalic(false);
|
||||
break;
|
||||
case BlinkOrXterm256Index:
|
||||
case RapidBlink: // We just interpret rapid blink as an alias of blink.
|
||||
attr.SetBlinking(true);
|
||||
break;
|
||||
case Steady:
|
||||
|
|
|
@ -52,10 +52,12 @@ const TextAttribute Terminal::GetDefaultBrushColors() noexcept
|
|||
|
||||
std::pair<COLORREF, COLORREF> Terminal::GetAttributeColors(const TextAttribute& attr) const noexcept
|
||||
{
|
||||
_blinkingState.RecordBlinkingUsage(attr);
|
||||
auto colors = attr.CalculateRgbColors({ _colorTable.data(), _colorTable.size() },
|
||||
_defaultFg,
|
||||
_defaultBg,
|
||||
_screenReversed);
|
||||
_screenReversed,
|
||||
_blinkingState.IsBlinkingFaint());
|
||||
colors.first |= 0xff000000;
|
||||
// We only care about alpha for the default BG (which enables acrylic)
|
||||
// If the bg isn't the default bg color, or reverse video is enabled, make it fully opaque.
|
||||
|
|
|
@ -28,6 +28,12 @@ void CursorBlinker::UpdateSystemMetrics()
|
|||
{
|
||||
// This can be -1 in a TS session
|
||||
_uCaretBlinkTime = ServiceLocator::LocateSystemConfigurationProvider()->GetCaretBlinkTime();
|
||||
|
||||
// If animations are disabled, or the blink rate is infinite, blinking is not allowed.
|
||||
BOOL animationsEnabled = TRUE;
|
||||
SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0);
|
||||
auto& blinkingState = ServiceLocator::LocateGlobals().getConsoleInformation().GetBlinkingState();
|
||||
blinkingState.SetBlinkingAllowed(animationsEnabled && _uCaretBlinkTime != INFINITE);
|
||||
}
|
||||
|
||||
void CursorBlinker::SettingsChanged()
|
||||
|
@ -53,7 +59,8 @@ void CursorBlinker::FocusStart()
|
|||
}
|
||||
|
||||
// Routine Description:
|
||||
// - This routine is called when the timer in the console with the focus goes off. It blinks the cursor.
|
||||
// - This routine is called when the timer in the console with the focus goes off.
|
||||
// It blinks the cursor and also toggles the rendition of any blinking attributes.
|
||||
// Arguments:
|
||||
// - ScreenInfo - reference to screen info structure.
|
||||
// Return Value:
|
||||
|
@ -109,7 +116,7 @@ void CursorBlinker::TimerRoutine(SCREEN_INFORMATION& ScreenInfo)
|
|||
if (cursor.GetDelay())
|
||||
{
|
||||
cursor.SetDelay(false);
|
||||
goto DoScroll;
|
||||
goto DoBlinkingRenditionAndScroll;
|
||||
}
|
||||
|
||||
// Don't blink the cursor for remote sessions.
|
||||
|
@ -118,7 +125,7 @@ void CursorBlinker::TimerRoutine(SCREEN_INFORMATION& ScreenInfo)
|
|||
(!cursor.IsBlinkingAllowed())) &&
|
||||
cursor.IsOn())
|
||||
{
|
||||
goto DoScroll;
|
||||
goto DoBlinkingRenditionAndScroll;
|
||||
}
|
||||
|
||||
// Blink only if the cursor isn't turned off via the API
|
||||
|
@ -127,6 +134,9 @@ void CursorBlinker::TimerRoutine(SCREEN_INFORMATION& ScreenInfo)
|
|||
cursor.SetIsOn(!cursor.IsOn());
|
||||
}
|
||||
|
||||
DoBlinkingRenditionAndScroll:
|
||||
gci.GetBlinkingState().ToggleBlinkingRendition(ScreenInfo.GetRenderTarget());
|
||||
|
||||
DoScroll:
|
||||
Scrolling::s_ScrollIfNecessary(ScreenInfo);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "..\types\inc\convert.hpp"
|
||||
|
||||
using Microsoft::Console::Interactivity::ServiceLocator;
|
||||
using Microsoft::Console::Render::BlinkingState;
|
||||
using Microsoft::Console::VirtualTerminal::VtIo;
|
||||
|
||||
CONSOLE_INFORMATION::CONSOLE_INFORMATION() :
|
||||
|
@ -222,7 +223,8 @@ InputBuffer* const CONSOLE_INFORMATION::GetActiveInputBuffer() const
|
|||
// - the default foreground color of the console.
|
||||
COLORREF CONSOLE_INFORMATION::GetDefaultForeground() const noexcept
|
||||
{
|
||||
return Settings::CalculateDefaultForeground();
|
||||
const auto fg = GetDefaultForegroundColor();
|
||||
return fg != INVALID_COLOR ? fg : GetColorTableEntry(LOBYTE(GetFillAttribute()) & FG_ATTRS);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -236,7 +238,25 @@ COLORREF CONSOLE_INFORMATION::GetDefaultForeground() const noexcept
|
|||
// - the default background color of the console.
|
||||
COLORREF CONSOLE_INFORMATION::GetDefaultBackground() const noexcept
|
||||
{
|
||||
return Settings::CalculateDefaultBackground();
|
||||
const auto bg = GetDefaultBackgroundColor();
|
||||
return bg != INVALID_COLOR ? bg : GetColorTableEntry((LOBYTE(GetFillAttribute()) & BG_ATTRS) >> 4);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the colors of a particular text attribute, using our color table,
|
||||
// and our configured default attributes.
|
||||
// Arguments:
|
||||
// - attr: the TextAttribute to retrieve the foreground color of.
|
||||
// Return Value:
|
||||
// - 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);
|
||||
return attr.CalculateRgbColors(Get256ColorTable(),
|
||||
GetDefaultForeground(),
|
||||
GetDefaultBackground(),
|
||||
IsScreenReversed(),
|
||||
_blinkingState.IsBlinkingFaint());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -355,6 +375,17 @@ Microsoft::Console::CursorBlinker& CONSOLE_INFORMATION::GetCursorBlinker() noexc
|
|||
return _blinker;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - return a reference to the console's blinking state.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a reference to the console's blinking state.
|
||||
BlinkingState& CONSOLE_INFORMATION::GetBlinkingState() const noexcept
|
||||
{
|
||||
return _blinkingState;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Generates a CHAR_INFO for this output cell, using the TextAttribute
|
||||
// GetLegacyAttributes method to generate the legacy style attributes.
|
||||
|
|
|
@ -28,6 +28,7 @@ Revision History:
|
|||
#include "..\server\WaitQueue.h"
|
||||
|
||||
#include "..\host\RenderData.hpp"
|
||||
#include "..\renderer\inc\BlinkingState.hpp"
|
||||
|
||||
// clang-format off
|
||||
// Flags flags
|
||||
|
@ -125,6 +126,7 @@ public:
|
|||
|
||||
COLORREF GetDefaultForeground() const noexcept;
|
||||
COLORREF GetDefaultBackground() const noexcept;
|
||||
std::pair<COLORREF, COLORREF> LookupAttributeColors(const TextAttribute& attr) const noexcept;
|
||||
|
||||
void SetTitle(const std::wstring_view newTitle);
|
||||
void SetTitlePrefix(const std::wstring& newTitlePrefix);
|
||||
|
@ -141,6 +143,7 @@ public:
|
|||
friend class SCREEN_INFORMATION;
|
||||
friend class CommonState;
|
||||
Microsoft::Console::CursorBlinker& GetCursorBlinker() noexcept;
|
||||
Microsoft::Console::Render::BlinkingState& GetBlinkingState() const noexcept;
|
||||
|
||||
CHAR_INFO AsCharInfo(const OutputCellView& cell) const noexcept;
|
||||
|
||||
|
@ -157,6 +160,7 @@ private:
|
|||
|
||||
Microsoft::Console::VirtualTerminal::VtIo _vtIo;
|
||||
Microsoft::Console::CursorBlinker _blinker;
|
||||
mutable Microsoft::Console::Render::BlinkingState _blinkingState;
|
||||
};
|
||||
|
||||
#define ConsoleLocked() (ServiceLocator::LocateGlobals()->getConsoleInformation()->ConsoleLock.OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
|
||||
|
|
|
@ -831,51 +831,6 @@ bool Settings::GetUseDx() const noexcept
|
|||
return _fUseDx;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Return the default foreground color of the console. If the settings are
|
||||
// configured to have a default foreground color (separate from the color
|
||||
// table), this will return that value. Otherwise it will return the value
|
||||
// from the colortable corresponding to our default legacy attributes.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the default foreground color of the console.
|
||||
COLORREF Settings::CalculateDefaultForeground() const noexcept
|
||||
{
|
||||
const auto fg = GetDefaultForegroundColor();
|
||||
return fg != INVALID_COLOR ? fg : GetColorTableEntry(LOBYTE(_wFillAttribute) & FG_ATTRS);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Return the default background color of the console. If the settings are
|
||||
// configured to have a default background color (separate from the color
|
||||
// table), this will return that value. Otherwise it will return the value
|
||||
// from the colortable corresponding to our default legacy attributes.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the default background color of the console.
|
||||
COLORREF Settings::CalculateDefaultBackground() const noexcept
|
||||
{
|
||||
const auto bg = GetDefaultBackgroundColor();
|
||||
return bg != INVALID_COLOR ? bg : GetColorTableEntry((LOBYTE(_wFillAttribute) & BG_ATTRS) >> 4);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the colors of a particular text attribute, using our color table,
|
||||
// and our configured default attributes.
|
||||
// Arguments:
|
||||
// - attr: the TextAttribute to retrieve the foreground color of.
|
||||
// Return Value:
|
||||
// - The color values of the attribute's foreground and background.
|
||||
std::pair<COLORREF, COLORREF> Settings::LookupAttributeColors(const TextAttribute& attr) const noexcept
|
||||
{
|
||||
return attr.CalculateRgbColors(Get256ColorTable(),
|
||||
CalculateDefaultForeground(),
|
||||
CalculateDefaultBackground(),
|
||||
_fScreenReversed);
|
||||
}
|
||||
|
||||
bool Settings::GetCopyColor() const noexcept
|
||||
{
|
||||
return _fCopyColor;
|
||||
|
|
|
@ -185,10 +185,6 @@ public:
|
|||
bool GetUseDx() const noexcept;
|
||||
bool GetCopyColor() const noexcept;
|
||||
|
||||
COLORREF CalculateDefaultForeground() const noexcept;
|
||||
COLORREF CalculateDefaultBackground() const noexcept;
|
||||
std::pair<COLORREF, COLORREF> LookupAttributeColors(const TextAttribute& attr) const noexcept;
|
||||
|
||||
private:
|
||||
DWORD _dwHotKey;
|
||||
DWORD _dwStartupFlags;
|
||||
|
|
81
src/renderer/base/BlinkingState.cpp
Normal file
81
src/renderer/base/BlinkingState.cpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "../inc/BlinkingState.hpp"
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
||||
// Method Description:
|
||||
// - Updates the flag indicating whether cells with the blinking attribute
|
||||
// can animate on and off.
|
||||
// Arguments:
|
||||
// - blinkingAllowed: true if blinking is permitted.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void BlinkingState::SetBlinkingAllowed(const bool blinkingAllowed) noexcept
|
||||
{
|
||||
_blinkingAllowed = blinkingAllowed;
|
||||
if (!_blinkingAllowed)
|
||||
{
|
||||
_blinkingShouldBeFaint = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Makes a record of whether the given attribute has blinking enabled or
|
||||
// not, so we can determine whether the screen will need to be refreshed
|
||||
// when the blinking rendition state changes.
|
||||
// Arguments:
|
||||
// - attr: a text attribute that is expected to be rendered.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void BlinkingState::RecordBlinkingUsage(const TextAttribute& attr) noexcept
|
||||
{
|
||||
_blinkingIsInUse = _blinkingIsInUse || attr.IsBlinking();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether cells with the blinking attribute should be rendered
|
||||
// as normal or faint, based on the current position in the blinking cycle.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - True if blinking cells should be rendered as faint.
|
||||
bool BlinkingState::IsBlinkingFaint() const noexcept
|
||||
{
|
||||
return _blinkingShouldBeFaint;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Increments the position in the blinking cycle, toggling the blinking
|
||||
// rendition state on every second call, potentially triggering a redraw of
|
||||
// the given render target if there are blinking cells currently in view.
|
||||
// Arguments:
|
||||
// - renderTarget: the render target that will be redrawn.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void BlinkingState::ToggleBlinkingRendition(IRenderTarget& renderTarget) noexcept
|
||||
try
|
||||
{
|
||||
if (_blinkingAllowed)
|
||||
{
|
||||
// This method is called with the frequency of the cursor blink rate,
|
||||
// but we only want our cells to blink at half that frequency. We thus
|
||||
// have a blinking cycle that loops through four phases...
|
||||
_blinkingCycle = (_blinkingCycle + 1) % 4;
|
||||
// ... and two of those four render the blinking attributes as faint.
|
||||
_blinkingShouldBeFaint = _blinkingCycle >= 2;
|
||||
// Every two cycles (when the state changes), we need to trigger a
|
||||
// redraw, but only if there are actually blinking attributes in use.
|
||||
if (_blinkingIsInUse && _blinkingCycle % 2 == 0)
|
||||
{
|
||||
// We reset the _blinkingIsInUse flag before redrawing, so we can
|
||||
// get a fresh assessment of the current blinking attribute usage.
|
||||
_blinkingIsInUse = false;
|
||||
renderTarget.TriggerRedrawAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
|
@ -10,6 +10,7 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\BlinkingState.cpp" />
|
||||
<ClCompile Include="..\Cluster.cpp" />
|
||||
<ClCompile Include="..\FontInfo.cpp" />
|
||||
<ClCompile Include="..\FontInfoBase.cpp" />
|
||||
|
@ -22,6 +23,7 @@
|
|||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\inc\BlinkingState.hpp" />
|
||||
<ClInclude Include="..\..\inc\Cluster.hpp" />
|
||||
<ClInclude Include="..\..\inc\FontInfo.hpp" />
|
||||
<ClInclude Include="..\..\inc\FontInfoBase.hpp" />
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
<ClCompile Include="..\RenderEngineBase.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\BlinkingState.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Cluster.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -77,6 +80,9 @@
|
|||
<ClInclude Include="..\..\inc\RenderEngineBase.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\inc\BlinkingState.hpp">
|
||||
<Filter>Header Files\inc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\inc\Cluster.hpp">
|
||||
<Filter>Header Files\inc</Filter>
|
||||
</ClInclude>
|
||||
|
|
37
src/renderer/inc/BlinkingState.hpp
Normal file
37
src/renderer/inc/BlinkingState.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- BlinkingState.hpp
|
||||
|
||||
Abstract:
|
||||
- This serves as a structure holding the state of the blinking rendition.
|
||||
|
||||
- It tracks the position in the blinking cycle, which determines whether any
|
||||
blinking cells should be rendered as on or off/faint. It also records whether
|
||||
blinking attributes are actually in use or not, so we can decide whether the
|
||||
screen needs to be refreshed when the blinking cycle changes.
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IRenderTarget.hpp"
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
class BlinkingState
|
||||
{
|
||||
public:
|
||||
void SetBlinkingAllowed(const bool blinkingAllowed) noexcept;
|
||||
void RecordBlinkingUsage(const TextAttribute& attr) noexcept;
|
||||
bool IsBlinkingFaint() const noexcept;
|
||||
void ToggleBlinkingRendition(IRenderTarget& renderTarget) noexcept;
|
||||
|
||||
private:
|
||||
bool _blinkingAllowed = true;
|
||||
size_t _blinkingCycle = 0;
|
||||
bool _blinkingIsInUse = false;
|
||||
bool _blinkingShouldBeFaint = false;
|
||||
};
|
||||
}
|
|
@ -111,6 +111,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
|
|||
Italics = 3,
|
||||
Underline = 4,
|
||||
BlinkOrXterm256Index = 5, // 5 is also Blink (appears as Bold).
|
||||
RapidBlink = 6,
|
||||
Negative = 7,
|
||||
Invisible = 8,
|
||||
CrossedOut = 9,
|
||||
|
|
|
@ -141,6 +141,7 @@ bool AdaptDispatch::SetGraphicsRendition(const gsl::span<const DispatchTypes::Gr
|
|||
attr.SetItalic(false);
|
||||
break;
|
||||
case BlinkOrXterm256Index:
|
||||
case RapidBlink: // We just interpret rapid blink as an alias of blink.
|
||||
attr.SetBlinking(true);
|
||||
break;
|
||||
case Steady:
|
||||
|
|
Loading…
Reference in a new issue