2019-05-03 00:29:04 +02:00
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
# include "precomp.h"
# include "_output.h"
# include "output.h"
# include "handle.h"
# include "getset.h"
# include "misc.h"
# include "../interactivity/inc/ServiceLocator.hpp"
# include "../types/inc/Viewport.hpp"
# include "../types/inc/convert.hpp"
# pragma hdrstop
2019-05-30 20:14:21 +02:00
2019-05-03 00:29:04 +02:00
using namespace Microsoft : : Console : : Types ;
2019-05-30 20:14:21 +02:00
using namespace Microsoft : : Console : : Interactivity ;
2019-05-03 00:29:04 +02:00
// This routine figures out what parameters to pass to CreateScreenBuffer based on the data from STARTUPINFO and the
// registry defaults, and then calls CreateScreenBuffer.
2019-06-11 22:27:09 +02:00
[[nodiscard]] NTSTATUS DoCreateScreenBuffer ( )
2019-05-03 00:29:04 +02:00
{
CONSOLE_INFORMATION & gci = ServiceLocator : : LocateGlobals ( ) . getConsoleInformation ( ) ;
FontInfo fiFont ( gci . GetFaceName ( ) ,
Allow FontInfo{,Base,Desired} to store a font name > 32 wch (#3107)
We now truncate the font name as it goes out to GDI APIs, in console API
servicing, and in the propsheet.
I attempted to defer truncating the font to as far up the stack as
possible, so as to make FontInfo usable for the broadest set of cases.
There were a couple questions that came up: I know that `Settings` gets
memset (memsat?) by the registry deserializer, and perhaps that's
another place for us to tackle. Right now, this pull request enables
fonts whose names are >= 32 characters _in Windows Terminal only_, but
the underpinnings are there for conhost as well. We'd need to explicitly
break at the API, or perhaps return a failure or log something to
telemetry.
* Should we log truncation at the API boundary to telemetry?
-> Later; followup filed (#3123)
* Should we fix Settings here, or later?
-> Later; followup filed (#3123)
* `TrueTypeFontList` is built out of things in winconp, the private
console header. Concern about interop structures.
-> Not used for interop, followup filed to clean it up (#3123)
* Is `unsigned int` right for codepage? For width?
-> Yes: codepage became UINT (from WORD) when we moved from Win16 to
Win32
This commit also includes a workaround for #3170. Growing
CONSOLE_INFORMATION made us lose the struct layout lottery during
release builds, and this was an expedient fix.
Closes #602.
Related to #3123.
2019-10-15 06:23:45 +02:00
gsl : : narrow_cast < unsigned char > ( gci . GetFontFamily ( ) ) ,
2019-05-03 00:29:04 +02:00
gci . GetFontWeight ( ) ,
gci . GetFontSize ( ) ,
gci . GetCodePage ( ) ) ;
// For East Asian version, we want to get the code page from the registry or shell32, so we can specify console
// codepage by console.cpl or shell32. The default codepage is OEMCP.
gci . CP = gci . GetCodePage ( ) ;
gci . OutputCP = gci . GetCodePage ( ) ;
gci . Flags | = CONSOLE_USE_PRIVATE_FLAGS ;
NTSTATUS Status = SCREEN_INFORMATION : : CreateInstance ( gci . GetWindowSize ( ) ,
fiFont ,
gci . GetScreenBufferSize ( ) ,
Improve conpty rendering of default colors in legacy apps (#6698)
Essentially what this does is map the default legacy foreground and
background attributes (typically white on black) to the `IsDefault`
color type in the `TextColor` class. As a result, we can now initialize
the buffer for "legacy" shells (like PowerShell and cmd.exe) with
default colors, instead of white on black. This fixes the startup
rendering in conpty clients, which expect an initial default background
color. It also makes these colors update appropriately when the default
palette values change.
One complication in getting this to work, is that the console permits
users to change which color indices are designated as defaults, so we
can't assume they'll always be white on black. This means that the
legacy-to-`TextAttribute` conversion will need access to those default
values.
Unfortunately the defaults are stored in the conhost `Settings` class
(the `_wFillAttribute` field), which isn't easily accessible to all the
code that needs to construct a `TextAttribute` from a legacy value. The
`OutputCellIterator` is particularly problematic, because some iterator
types need to generate a new `TextAttribute` on every iteration.
So after trying a couple of different approaches, I decided that the
least worst option would be to add a pair of static properties for the
legacy defaults in the `TextAttribute` class itself, then refresh those
values from the `Settings` class whenever the defaults changed (this
only happens on startup, or when the conhost _Properties_ dialog is
edited).
And once the `TextAttribute` class had access to those defaults, it was
fairly easy to adapt the constructor to handle the conversion of default
values to the `IsDefault` color type. I could also then simplify the
`TextAttribute::GetLegacyAttributes` method which does the reverse
mapping, and which previously required the default values to be passed
in as a parameter
VALIDATION
I had to make one small change to the `TestRoundtripExhaustive` unit
test which assumed that all legacy attributes would convert to legacy
color types, which is no longer the case, but otherwise all the existing
tests passed as is. I added a new unit test verifying that the default
legacy attributes correctly mapped to default color types, and the
default color types were mapped back to the correct legacy attributes.
I've manually confirmed that this fixed the issue raised in #5952,
namely that the conhost screen is cleared with the correct default
colors, and also that it is correctly refreshed when changing the
palette from the properties dialog. And I've combined this PR with
#6506, and confirmed that the PowerShell and the cmd shell renderings in
Windows Terminal are at least improved, if not always perfect.
This is a prerequisite for PR #6506
Closes #5952
2020-07-01 20:08:30 +02:00
TextAttribute { } ,
2019-05-03 00:29:04 +02:00
TextAttribute { gci . GetPopupFillAttribute ( ) } ,
gci . GetCursorSize ( ) ,
& gci . ScreenBuffers ) ;
// TODO: MSFT 9355013: This needs to be resolved. We increment it once with no handle to ensure it's never cleaned up
// and one always exists for the renderer (and potentially other functions.)
// It's currently a load-bearing piece of code. http://osgvsowi/9355013
if ( NT_SUCCESS ( Status ) )
{
gci . ScreenBuffers [ 0 ] . IncrementOriginalScreenBuffer ( ) ;
}
return Status ;
}
// Routine Description:
// - This routine copies a rectangular region from the screen buffer to the screen buffer.
// Arguments:
// - screenInfo - reference to screen info
// - source - rectangle in source buffer to copy
// - targetOrigin - upper left coordinates of new location rectangle
static void _CopyRectangle ( SCREEN_INFORMATION & screenInfo ,
const Viewport & source ,
const COORD targetOrigin )
{
const auto sourceOrigin = source . Origin ( ) ;
// 0. If the source and the target are the same... we have nothing to do. Leave.
if ( sourceOrigin = = targetOrigin )
{
return ;
}
// 1. If we're copying entire rows of the buffer and moving them directly up or down,
// then we can send a rotate command to the underlying buffer to just adjust the
// row locations instead of copying or moving anything.
{
const auto bufferSize = screenInfo . GetBufferSize ( ) . Dimensions ( ) ;
const auto sourceFullRows = source . Width ( ) = = bufferSize . X ;
const auto verticalCopyOnly = source . Left ( ) = = 0 & & targetOrigin . X = = 0 ;
if ( sourceFullRows & & verticalCopyOnly )
{
const auto delta = targetOrigin . Y - source . Top ( ) ;
screenInfo . GetTextBuffer ( ) . ScrollRows ( source . Top ( ) , source . Height ( ) , gsl : : narrow < SHORT > ( delta ) ) ;
return ;
}
}
// 2. We can move any other scenario in-place without copying. We just have to carefully
// choose which direction we walk through filling up the target so it doesn't accidentally
// erase the source material before it can be copied/moved to the new location.
{
const auto target = Viewport : : FromDimensions ( targetOrigin , source . Dimensions ( ) ) ;
const auto walkDirection = Viewport : : DetermineWalkDirection ( source , target ) ;
auto sourcePos = source . GetWalkOrigin ( walkDirection ) ;
auto targetPos = target . GetWalkOrigin ( walkDirection ) ;
do
{
const auto data = OutputCell ( * screenInfo . GetCellDataAt ( sourcePos ) ) ;
screenInfo . Write ( OutputCellIterator ( { & data , 1 } ) , targetPos ) ;
source . WalkInBounds ( sourcePos , walkDirection ) ;
} while ( target . WalkInBounds ( targetPos , walkDirection ) ) ;
}
}
// Routine Description:
// - This routine reads a sequence of attributes from the screen buffer.
// Arguments:
// - screenInfo - reference to screen buffer information.
// - coordRead - Screen buffer coordinate to begin reading from.
// - amountToRead - the number of elements to read
// Return Value:
// - vector of attribute data
std : : vector < WORD > ReadOutputAttributes ( const SCREEN_INFORMATION & screenInfo ,
const COORD coordRead ,
const size_t amountToRead )
{
// Short circuit. If nothing to read, leave early.
if ( amountToRead = = 0 )
{
return { } ;
}
// Short circuit, if reading out of bounds, leave early.
if ( ! screenInfo . GetBufferSize ( ) . IsInBounds ( coordRead ) )
{
return { } ;
}
// Get iterator to the position we should start reading at.
auto it = screenInfo . GetCellDataAt ( coordRead ) ;
// Count up the number of cells we've attempted to read.
ULONG amountRead = 0 ;
// Prepare the return value string.
std : : vector < WORD > retVal ;
// Reserve the number of cells. If we have >U+FFFF, it will auto-grow later and that's OK.
retVal . reserve ( amountToRead ) ;
// While we haven't read enough cells yet and the iterator is still valid (hasn't reached end of buffer)
while ( amountRead < amountToRead & & it )
{
Improve conpty rendering of default colors in legacy apps (#6698)
Essentially what this does is map the default legacy foreground and
background attributes (typically white on black) to the `IsDefault`
color type in the `TextColor` class. As a result, we can now initialize
the buffer for "legacy" shells (like PowerShell and cmd.exe) with
default colors, instead of white on black. This fixes the startup
rendering in conpty clients, which expect an initial default background
color. It also makes these colors update appropriately when the default
palette values change.
One complication in getting this to work, is that the console permits
users to change which color indices are designated as defaults, so we
can't assume they'll always be white on black. This means that the
legacy-to-`TextAttribute` conversion will need access to those default
values.
Unfortunately the defaults are stored in the conhost `Settings` class
(the `_wFillAttribute` field), which isn't easily accessible to all the
code that needs to construct a `TextAttribute` from a legacy value. The
`OutputCellIterator` is particularly problematic, because some iterator
types need to generate a new `TextAttribute` on every iteration.
So after trying a couple of different approaches, I decided that the
least worst option would be to add a pair of static properties for the
legacy defaults in the `TextAttribute` class itself, then refresh those
values from the `Settings` class whenever the defaults changed (this
only happens on startup, or when the conhost _Properties_ dialog is
edited).
And once the `TextAttribute` class had access to those defaults, it was
fairly easy to adapt the constructor to handle the conversion of default
values to the `IsDefault` color type. I could also then simplify the
`TextAttribute::GetLegacyAttributes` method which does the reverse
mapping, and which previously required the default values to be passed
in as a parameter
VALIDATION
I had to make one small change to the `TestRoundtripExhaustive` unit
test which assumed that all legacy attributes would convert to legacy
color types, which is no longer the case, but otherwise all the existing
tests passed as is. I added a new unit test verifying that the default
legacy attributes correctly mapped to default color types, and the
default color types were mapped back to the correct legacy attributes.
I've manually confirmed that this fixed the issue raised in #5952,
namely that the conhost screen is cleared with the correct default
colors, and also that it is correctly refreshed when changing the
palette from the properties dialog. And I've combined this PR with
#6506, and confirmed that the PowerShell and the cmd shell renderings in
Windows Terminal are at least improved, if not always perfect.
This is a prerequisite for PR #6506
Closes #5952
2020-07-01 20:08:30 +02:00
const auto legacyAttributes = it - > TextAttr ( ) . GetLegacyAttributes ( ) ;
Improve the legacy color conversions (#6358)
This PR provides a faster algorithm for converting 8-bit and 24-bit
colors into the 4-bit legacy values that are required by the Win32
console APIs. It also fixes areas of the code that were incorrectly
using a simple 16-color conversion that didn't handle 8-bit and 24-bit
values.
The faster conversion algorithm should be an improvement for issues #783
and #3950.
One of the main points of this PR was to fix the
`ReadConsoleOutputAttribute` API, which was using a simplified legacy
color conversion (the original `TextAttribute:GetLegacyAttributes`
method), which could only handle values from the 16-color table. RGB
values, and colors from the 256-color table, would be mapped to
completely nonsensical values. This API has now been updated to use the
more correct `Settings::GenerateLegacyAttributes` method.
But there were also a couple of other places in the code that were using
`GetLegacyAttributes` when they really had no reason to be working with
legacy attributes at all. This could result in colors being downgraded
to 4-bit values (often badly, as explained above), when the code was
already perfectly capable of displaying the full 24-bits.
This included the fill colors in the IME composer (in `ConsoleImeInfo`),
and the construction of the highlighting colors in the color
search/selection handler (`Selection::_HandleColorSelection`). I also
got rid of some legacy attribute code in the `Popup` class, which was
originally intended to update colors below the popup when the settings
changed, but actually caused more problems than it solved.
The other major goal of this PR was to improve the performance of the
`GenerateLegacyAttributes` method, since the existing implementation
could be quite slow when dealing with RGB values.
The simple cases are handled much the same as they were before. For an
`IsDefault` color, we get the default index from the
`Settings::_wFillAttribute` field. For an `IsIndex16` color, the index
can just be returned as is.
For an `IsRgb` color, the RGB components are compressed down to 8 bits
(3 red, 3 green, 2 blue), simply by dropping the least significant bits.
This 8-bit value is then used to lookup a representative 16-color value
from a hard-coded table. An `IsIndex256` color is also converted with a
lookup table, just using the existing 8-bit index.
The RGB mapping table was calculated by taking each compressed 8-bit
color, and picking a entry from the _Campbell_ palette that best
approximated that color. This was done by looking at a range of 24-bit
colors that mapped to the 8-bit value, finding the best _Campbell_ match
for each of them (using a [CIEDE2000] color difference calculation), and
then the most common match became the index that the 8-bit value would
map to.
The 256-color table was just a simpler version of this process. For each
entry in the table, we take the default RGB palette value, and find it's
closest match in the _Campbell_ palette.
Because these tables are hard-coded, the results won't adjust to changes
in the palette. However, they should still produce reasonable results
for palettes that follow the standard ANSI color range. And since
they're only a very loose approximation of the colors anyway, the exact
value really isn't that important.
That said, I have tried to make sure that if you take an RGB value for a
particular index in a reasonable color scheme, then the legacy color
mapped from that value should ideally match the same index. This will
never be possible for all color schemes, but I have tweaked a few of the
table entries to improve the results for some of the common schemes.
One other point worth making regarding the hard-coded tables: even if we
wanted to take the active palette into account, that wouldn't actually
be possible over a conpty connection, because we can't easily know what
color scheme the client application is using. At least this way the
results in conhost are guaranteed to be the same as in the Windows
Terminal.
[CIEDE2000]: https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
## Validation Steps Performed
This code still passes the `TextAttributeTests` that check the basic
`GetLegacyAttribute` behaviour and verify the all legacy attributes
roundtrip correctly. However, some of the values in the `RgbColorTests`
had to be updated, since we're now intentionally returning different
values as a result of the changes to the RGB conversion algorithm.
I haven't added additional unit tests, but I have done a lot of manual
testing to see how well the new algorithm works with a range of colors
and a variety of different color schemes. It's not perfect in every
situation, but I think it works well enough for the purpose it serves.
I've also confirmed that the issues reported in #5940 and #6247 are now
fixed by these changes.
Closes #5940
Closes #6247
2020-06-08 21:05:06 +02:00
2019-05-03 00:29:04 +02:00
// If the first thing we read is trailing, pad with a space.
// OR If the last thing we read is leading, pad with a space.
if ( ( amountRead = = 0 & & it - > DbcsAttr ( ) . IsTrailing ( ) ) | |
( amountRead = = ( amountToRead - 1 ) & & it - > DbcsAttr ( ) . IsLeading ( ) ) )
{
Improve the legacy color conversions (#6358)
This PR provides a faster algorithm for converting 8-bit and 24-bit
colors into the 4-bit legacy values that are required by the Win32
console APIs. It also fixes areas of the code that were incorrectly
using a simple 16-color conversion that didn't handle 8-bit and 24-bit
values.
The faster conversion algorithm should be an improvement for issues #783
and #3950.
One of the main points of this PR was to fix the
`ReadConsoleOutputAttribute` API, which was using a simplified legacy
color conversion (the original `TextAttribute:GetLegacyAttributes`
method), which could only handle values from the 16-color table. RGB
values, and colors from the 256-color table, would be mapped to
completely nonsensical values. This API has now been updated to use the
more correct `Settings::GenerateLegacyAttributes` method.
But there were also a couple of other places in the code that were using
`GetLegacyAttributes` when they really had no reason to be working with
legacy attributes at all. This could result in colors being downgraded
to 4-bit values (often badly, as explained above), when the code was
already perfectly capable of displaying the full 24-bits.
This included the fill colors in the IME composer (in `ConsoleImeInfo`),
and the construction of the highlighting colors in the color
search/selection handler (`Selection::_HandleColorSelection`). I also
got rid of some legacy attribute code in the `Popup` class, which was
originally intended to update colors below the popup when the settings
changed, but actually caused more problems than it solved.
The other major goal of this PR was to improve the performance of the
`GenerateLegacyAttributes` method, since the existing implementation
could be quite slow when dealing with RGB values.
The simple cases are handled much the same as they were before. For an
`IsDefault` color, we get the default index from the
`Settings::_wFillAttribute` field. For an `IsIndex16` color, the index
can just be returned as is.
For an `IsRgb` color, the RGB components are compressed down to 8 bits
(3 red, 3 green, 2 blue), simply by dropping the least significant bits.
This 8-bit value is then used to lookup a representative 16-color value
from a hard-coded table. An `IsIndex256` color is also converted with a
lookup table, just using the existing 8-bit index.
The RGB mapping table was calculated by taking each compressed 8-bit
color, and picking a entry from the _Campbell_ palette that best
approximated that color. This was done by looking at a range of 24-bit
colors that mapped to the 8-bit value, finding the best _Campbell_ match
for each of them (using a [CIEDE2000] color difference calculation), and
then the most common match became the index that the 8-bit value would
map to.
The 256-color table was just a simpler version of this process. For each
entry in the table, we take the default RGB palette value, and find it's
closest match in the _Campbell_ palette.
Because these tables are hard-coded, the results won't adjust to changes
in the palette. However, they should still produce reasonable results
for palettes that follow the standard ANSI color range. And since
they're only a very loose approximation of the colors anyway, the exact
value really isn't that important.
That said, I have tried to make sure that if you take an RGB value for a
particular index in a reasonable color scheme, then the legacy color
mapped from that value should ideally match the same index. This will
never be possible for all color schemes, but I have tweaked a few of the
table entries to improve the results for some of the common schemes.
One other point worth making regarding the hard-coded tables: even if we
wanted to take the active palette into account, that wouldn't actually
be possible over a conpty connection, because we can't easily know what
color scheme the client application is using. At least this way the
results in conhost are guaranteed to be the same as in the Windows
Terminal.
[CIEDE2000]: https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
## Validation Steps Performed
This code still passes the `TextAttributeTests` that check the basic
`GetLegacyAttribute` behaviour and verify the all legacy attributes
roundtrip correctly. However, some of the values in the `RgbColorTests`
had to be updated, since we're now intentionally returning different
values as a result of the changes to the RGB conversion algorithm.
I haven't added additional unit tests, but I have done a lot of manual
testing to see how well the new algorithm works with a range of colors
and a variety of different color schemes. It's not perfect in every
situation, but I think it works well enough for the purpose it serves.
I've also confirmed that the issues reported in #5940 and #6247 are now
fixed by these changes.
Closes #5940
Closes #6247
2020-06-08 21:05:06 +02:00
retVal . push_back ( legacyAttributes ) ;
2019-05-03 00:29:04 +02:00
}
else
{
Improve the legacy color conversions (#6358)
This PR provides a faster algorithm for converting 8-bit and 24-bit
colors into the 4-bit legacy values that are required by the Win32
console APIs. It also fixes areas of the code that were incorrectly
using a simple 16-color conversion that didn't handle 8-bit and 24-bit
values.
The faster conversion algorithm should be an improvement for issues #783
and #3950.
One of the main points of this PR was to fix the
`ReadConsoleOutputAttribute` API, which was using a simplified legacy
color conversion (the original `TextAttribute:GetLegacyAttributes`
method), which could only handle values from the 16-color table. RGB
values, and colors from the 256-color table, would be mapped to
completely nonsensical values. This API has now been updated to use the
more correct `Settings::GenerateLegacyAttributes` method.
But there were also a couple of other places in the code that were using
`GetLegacyAttributes` when they really had no reason to be working with
legacy attributes at all. This could result in colors being downgraded
to 4-bit values (often badly, as explained above), when the code was
already perfectly capable of displaying the full 24-bits.
This included the fill colors in the IME composer (in `ConsoleImeInfo`),
and the construction of the highlighting colors in the color
search/selection handler (`Selection::_HandleColorSelection`). I also
got rid of some legacy attribute code in the `Popup` class, which was
originally intended to update colors below the popup when the settings
changed, but actually caused more problems than it solved.
The other major goal of this PR was to improve the performance of the
`GenerateLegacyAttributes` method, since the existing implementation
could be quite slow when dealing with RGB values.
The simple cases are handled much the same as they were before. For an
`IsDefault` color, we get the default index from the
`Settings::_wFillAttribute` field. For an `IsIndex16` color, the index
can just be returned as is.
For an `IsRgb` color, the RGB components are compressed down to 8 bits
(3 red, 3 green, 2 blue), simply by dropping the least significant bits.
This 8-bit value is then used to lookup a representative 16-color value
from a hard-coded table. An `IsIndex256` color is also converted with a
lookup table, just using the existing 8-bit index.
The RGB mapping table was calculated by taking each compressed 8-bit
color, and picking a entry from the _Campbell_ palette that best
approximated that color. This was done by looking at a range of 24-bit
colors that mapped to the 8-bit value, finding the best _Campbell_ match
for each of them (using a [CIEDE2000] color difference calculation), and
then the most common match became the index that the 8-bit value would
map to.
The 256-color table was just a simpler version of this process. For each
entry in the table, we take the default RGB palette value, and find it's
closest match in the _Campbell_ palette.
Because these tables are hard-coded, the results won't adjust to changes
in the palette. However, they should still produce reasonable results
for palettes that follow the standard ANSI color range. And since
they're only a very loose approximation of the colors anyway, the exact
value really isn't that important.
That said, I have tried to make sure that if you take an RGB value for a
particular index in a reasonable color scheme, then the legacy color
mapped from that value should ideally match the same index. This will
never be possible for all color schemes, but I have tweaked a few of the
table entries to improve the results for some of the common schemes.
One other point worth making regarding the hard-coded tables: even if we
wanted to take the active palette into account, that wouldn't actually
be possible over a conpty connection, because we can't easily know what
color scheme the client application is using. At least this way the
results in conhost are guaranteed to be the same as in the Windows
Terminal.
[CIEDE2000]: https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
## Validation Steps Performed
This code still passes the `TextAttributeTests` that check the basic
`GetLegacyAttribute` behaviour and verify the all legacy attributes
roundtrip correctly. However, some of the values in the `RgbColorTests`
had to be updated, since we're now intentionally returning different
values as a result of the changes to the RGB conversion algorithm.
I haven't added additional unit tests, but I have done a lot of manual
testing to see how well the new algorithm works with a range of colors
and a variety of different color schemes. It's not perfect in every
situation, but I think it works well enough for the purpose it serves.
I've also confirmed that the issues reported in #5940 and #6247 are now
fixed by these changes.
Closes #5940
Closes #6247
2020-06-08 21:05:06 +02:00
retVal . push_back ( legacyAttributes | it - > DbcsAttr ( ) . GeneratePublicApiAttributeFormat ( ) ) ;
2019-05-03 00:29:04 +02:00
}
amountRead + + ;
it + + ;
}
return retVal ;
}
// Routine Description:
// - This routine reads a sequence of unicode characters from the screen buffer
// Arguments:
// - screenInfo - reference to screen buffer information.
// - coordRead - Screen buffer coordinate to begin reading from.
// - amountToRead - the number of elements to read
// Return Value:
// - wstring
std : : wstring ReadOutputStringW ( const SCREEN_INFORMATION & screenInfo ,
const COORD coordRead ,
const size_t amountToRead )
{
// Short circuit. If nothing to read, leave early.
if ( amountToRead = = 0 )
{
return { } ;
}
// Short circuit, if reading out of bounds, leave early.
if ( ! screenInfo . GetBufferSize ( ) . IsInBounds ( coordRead ) )
{
return { } ;
}
// Get iterator to the position we should start reading at.
auto it = screenInfo . GetCellDataAt ( coordRead ) ;
// Count up the number of cells we've attempted to read.
ULONG amountRead = 0 ;
// Prepare the return value string.
std : : wstring retVal ;
retVal . reserve ( amountToRead ) ; // Reserve the number of cells. If we have >U+FFFF, it will auto-grow later and that's OK.
// While we haven't read enough cells yet and the iterator is still valid (hasn't reached end of buffer)
while ( amountRead < amountToRead & & it )
{
// If the first thing we read is trailing, pad with a space.
// OR If the last thing we read is leading, pad with a space.
if ( ( amountRead = = 0 & & it - > DbcsAttr ( ) . IsTrailing ( ) ) | |
( amountRead = = ( amountToRead - 1 ) & & it - > DbcsAttr ( ) . IsLeading ( ) ) )
{
retVal + = UNICODE_SPACE ;
}
else
{
// Otherwise, add anything that isn't a trailing cell. (Trailings are duplicate copies of the leading.)
if ( ! it - > DbcsAttr ( ) . IsTrailing ( ) )
{
retVal + = it - > Chars ( ) ;
}
}
amountRead + + ;
it + + ;
}
return retVal ;
}
// Routine Description:
// - This routine reads a sequence of ascii characters from the screen buffer
// Arguments:
// - screenInfo - reference to screen buffer information.
// - coordRead - Screen buffer coordinate to begin reading from.
// - amountToRead - the number of elements to read
// Return Value:
// - string of char data
std : : string ReadOutputStringA ( const SCREEN_INFORMATION & screenInfo ,
const COORD coordRead ,
const size_t amountToRead )
{
const auto wstr = ReadOutputStringW ( screenInfo , coordRead , amountToRead ) ;
const auto & gci = ServiceLocator : : LocateGlobals ( ) . getConsoleInformation ( ) ;
return ConvertToA ( gci . OutputCP , wstr ) ;
}
void ScreenBufferSizeChange ( const COORD coordNewSize )
{
const CONSOLE_INFORMATION & gci = ServiceLocator : : LocateGlobals ( ) . getConsoleInformation ( ) ;
try
{
gci . pInputBuffer - > Write ( std : : make_unique < WindowBufferSizeEvent > ( coordNewSize ) ) ;
}
catch ( . . . )
{
LOG_HR ( wil : : ResultFromCaughtException ( ) ) ;
}
}
// Routine Description:
// - This is simply a notifier method to let accessibility and renderers know that a region of the buffer
// has been copied/moved to another location in a block fashion.
// Arguments:
// - screenInfo - The relevant screen buffer where data was moved
// - source - The viewport describing the region where data was copied from
// - fill - The viewport describing the area that was filled in with the fill character (uncovered area)
// - target - The viewport describing the region where data was copied to
static void _ScrollScreen ( SCREEN_INFORMATION & screenInfo , const Viewport & source , const Viewport & fill , const Viewport & target )
{
if ( screenInfo . IsActiveScreenBuffer ( ) )
{
2019-06-11 22:27:09 +02:00
IAccessibilityNotifier * pNotifier = ServiceLocator : : LocateAccessibilityNotifier ( ) ;
2019-05-03 00:29:04 +02:00
if ( pNotifier ! = nullptr )
{
pNotifier - > NotifyConsoleUpdateScrollEvent ( target . Origin ( ) . X - source . Left ( ) , target . Origin ( ) . Y - source . RightInclusive ( ) ) ;
}
}
// Get the render target and send it commands.
// It will figure out whether or not we're active and where the messages need to go.
auto & render = screenInfo . GetRenderTarget ( ) ;
// Redraw anything in the target area
render . TriggerRedraw ( target ) ;
// Also redraw anything that was filled.
render . TriggerRedraw ( fill ) ;
}
// Routine Description:
// - This routine is a special-purpose scroll for use by AdjustCursorPosition.
// Arguments:
// - screenInfo - reference to screen buffer info.
// Return Value:
// - true if we succeeded in scrolling the buffer, otherwise false (if we're out of memory)
bool StreamScrollRegion ( SCREEN_INFORMATION & screenInfo )
{
// Rotate the circular buffer around and wipe out the previous final line.
Correct fill attributes when scrolling and erasing (#3100)
## Summary of the Pull Request
Operations that erase areas of the screen are typically meant to do so using the current color attributes, but with the rendition attributes reset (what we refer to as meta attributes). This also includes scroll operations that have to clear the area of the screen that has scrolled into view. The only exception is the _Erase Scrollback_ operation, which needs to reset the buffer with the default attributes. This PR updates all of these cases to apply the correct attributes when scrolling and erasing.
## PR Checklist
* [x] Closes #2553
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed
* [ ] Requires documentation to be updated
* [ ] I've not really discussed this with core contributors. I'm ready to accept this work might be rejected in favor of a different grand plan.
## Detailed Description of the Pull Request / Additional comments
My initial plan was to use a special case legacy attribute value to indicate the "standard erase attribute" which could safely be passed through the legacy APIs. But this wouldn't cover the cases that required default attributes to be used. And then with the changes in PR #2668 and #2987, it became clear that our requirements could be better achieved with a couple of new private APIs that wouldn't have to depend on legacy attribute hacks at all.
To that end, I've added the `PrivateFillRegion` and `PrivateScrollRegion` APIs to the `ConGetSet` interface. These are just thin wrappers around the existing `SCREEN_INFORMATION::Write` method and the `ScrollRegion` function respectively, but with a simple boolean parameter to choose between filling with default attributes or the standard erase attributes (i.e the current colors but with meta attributes reset).
With those new APIs in place, I could then update most scroll operations to use `PrivateScrollRegion`, and most erase operations to use `PrivateFillRegion`.
The functions affected by scrolling included:
* `DoSrvPrivateReverseLineFeed` (the RI command)
* `DoSrvPrivateModifyLinesImpl` (the IL and DL commands)
* `AdaptDispatch::_InsertDeleteHelper` (the ICH and DCH commands)
* `AdaptDispatch::_ScrollMovement` (the SU and SD commands)
The functions affected by erasing included:
* `AdaptDispatch::_EraseSingleLineHelper` (the EL command, and most ED variants)
* `AdaptDispatch::EraseCharacters` (the ECH command)
While updating these erase methods, I noticed that both of them also required boundary fixes similar to those in PR #2505 (i.e. the horizontal extent of the erase operation should apply to the full width of the buffer, and not just the current viewport width), so I've addressed that at the same time.
In addition to the changes above, there were also a few special cases, the first being the line feed handling, which required updating in a number of places to use the correct erase attributes:
* `SCREEN_INFORMATION::InitializeCursorRowAttributes` - this is used to initialise the rows that pan into view when the viewport is moved down the buffer.
* `TextBuffer::IncrementCircularBuffer` - this occurs when we scroll passed the very end of the buffer, and a recycled row now needs to be reinitialised.
* `AdjustCursorPosition` - when within margin boundaries, this relies on a couple of direct calls to `ScrollRegion` which needed to be passed the correct fill attributes.
The second special case was the full screen erase sequence (`ESC 2 J`), which is handled separately from the other ED sequences. This required updating the `SCREEN_INFORMATION::VtEraseAll` method to use the standard erase attributes, and also required changes to the horizontal extent of the filled area, since it should have been clearing the full buffer width (the same issue as the other erase operations mentioned above).
Finally, there was the `AdaptDispatch::_EraseScrollback` method, which uses both scroll and fill operations, which could now be handled by the new `PrivateScrollRegion` and `PrivateFillRegion` APIs. But in this case we needed to fill with the default attributes rather than the standard erase attributes. And again this implementation needed some changes to make sure the full width of the active area was retained after the erase, similar to the horizontal boundary issues with the other erase operations.
Once all these changes were made, there were a few areas of the code that could then be simplified quite a bit. The `FillConsoleOutputCharacterW`, `FillConsoleOutputAttribute`, and `ScrollConsoleScreenBufferW` were no longer needed in the `ConGetSet` interface, so all of that code could now be removed. The `_EraseSingleLineDistanceHelper` and `_EraseAreaHelper` methods in the `AdaptDispatch` class were also no longer required and could be removed.
Then there were the hacks to handle legacy default colors in the `FillConsoleOutputAttributeImpl` and `ScrollConsoleScreenBufferWImpl` implementations. Since those hacks were only needed for VT operations, and the VT code no longer calls those methods, there was no longer a need to retain that behaviour (in fact there are probably some edge cases where that behaviour might have been considered a bug when reached via the public console APIs).
## Validation Steps Performed
For most of the scrolling operations there were already existing tests in place, and those could easily be extended to check that the meta attributes were correctly reset when filling the revealed lines of the scrolling region.
In the screen buffer tests, I made updates of that sort to the `ScrollOperations` method (handling SU, SD, IL, DL, and RI), the `InsertChars` and `DeleteChars` methods (ICH and DCH), and the `VtNewlinePastViewport` method (LF). I also added a new `VtNewlinePastEndOfBuffer` test to check the case where the line feed causes the viewport to pan past the end of the buffer.
The erase operations, however, were being covered by adapter tests, and those aren't really suited for this kind of functionality (the same sort of issue came up in PR #2505). As a result I've had to reimplement those tests as screen buffer tests.
Most of the erase operations are covered by the `EraseTests` method, except the for the scrollback erase which has a dedicated `EraseScrollbackTests` method. I've also had to replace the `HardReset` adapter test, but that was already mostly covered by the `HardResetBuffer` screen buffer test, which I've now extended slightly (it could do with some more checks, but I think that can wait for a future PR when we're fixing other RIS issues).
2019-12-11 00:14:40 +01:00
const bool inVtMode = WI_IsFlagSet ( screenInfo . OutputMode , ENABLE_VIRTUAL_TERMINAL_PROCESSING ) ;
bool fSuccess = screenInfo . GetTextBuffer ( ) . IncrementCircularBuffer ( inVtMode ) ;
2019-05-03 00:29:04 +02:00
if ( fSuccess )
{
// Trigger a graphical update if we're active.
if ( screenInfo . IsActiveScreenBuffer ( ) )
{
COORD coordDelta = { 0 } ;
coordDelta . Y = - 1 ;
2019-06-11 22:27:09 +02:00
IAccessibilityNotifier * pNotifier = ServiceLocator : : LocateAccessibilityNotifier ( ) ;
2019-05-03 00:29:04 +02:00
if ( pNotifier )
{
// Notify accessibility that a scroll has occurred.
pNotifier - > NotifyConsoleUpdateScrollEvent ( coordDelta . X , coordDelta . Y ) ;
}
if ( ServiceLocator : : LocateGlobals ( ) . pRender ! = nullptr )
{
ServiceLocator : : LocateGlobals ( ) . pRender - > TriggerScroll ( & coordDelta ) ;
}
}
}
return fSuccess ;
}
// Routine Description:
// - This routine copies ScrollRectangle to DestinationOrigin then fills in ScrollRectangle with Fill.
// - The scroll region is copied to a third buffer, the scroll region is filled, then the original contents of the scroll region are copied to the destination.
// Arguments:
// - screenInfo - reference to screen buffer info.
// - scrollRectGiven - Region to copy/move (source and size)
// - clipRectGiven - Optional clip region to contain buffer change effects
// - destinationOriginGiven - Upper left corner of target region.
// - fillCharGiven - Character to fill source region with.
// - fillAttrsGiven - Attribute to fill source region with.
// NOTE: Throws exceptions
void ScrollRegion ( SCREEN_INFORMATION & screenInfo ,
const SMALL_RECT scrollRectGiven ,
const std : : optional < SMALL_RECT > clipRectGiven ,
const COORD destinationOriginGiven ,
const wchar_t fillCharGiven ,
const TextAttribute fillAttrsGiven )
{
// ------ 1. PREP SOURCE ------
// Set up the source viewport.
auto source = Viewport : : FromInclusive ( scrollRectGiven ) ;
const auto originalSourceOrigin = source . Origin ( ) ;
// Alright, let's make sure that our source fits inside the buffer.
const auto buffer = screenInfo . GetBufferSize ( ) ;
source = Viewport : : Intersect ( source , buffer ) ;
// If the source is no longer valid, then there's nowhere we can copy from
// and also nowhere we can fill. We're done. Return early.
if ( ! source . IsValid ( ) )
{
return ;
}
// ------ 2. PREP CLIP ------
// Now figure out our clipping area. If we have clipping specified, it will limit
// the area that can be affected (targeted or filling) throughout this operation.
// If there was no clip rect, we'll clip to the entire buffer size.
auto clip = Viewport : : FromInclusive ( clipRectGiven . value_or ( buffer . ToInclusive ( ) ) ) ;
// OK, make sure that the clip rectangle also fits inside the buffer
clip = Viewport : : Intersect ( buffer , clip ) ;
// ------ 3. PREP FILL ------
// Then think about fill. We will fill in any area of the source that we copied from
// with the fill character as long as it falls inside the clip region (the area
// that is allowed to be affected).
auto fill = Viewport : : Intersect ( clip , source ) ;
// If fill is no longer valid, then there is no area that we're allowed to write to
// within the buffer. So we can just exit early.
if ( ! fill . IsValid ( ) )
{
return ;
}
// Determine the cell we will use to fill in any revealed/uncovered space.
// We generally use exactly what was given to us.
OutputCellIterator fillData ( fillCharGiven , fillAttrsGiven ) ;
// However, if the character is null and we were given a null attribute (represented as legacy 0),
// then we'll just fill with spaces and whatever the buffer's default colors are.
Improve the legacy color conversions (#6358)
This PR provides a faster algorithm for converting 8-bit and 24-bit
colors into the 4-bit legacy values that are required by the Win32
console APIs. It also fixes areas of the code that were incorrectly
using a simple 16-color conversion that didn't handle 8-bit and 24-bit
values.
The faster conversion algorithm should be an improvement for issues #783
and #3950.
One of the main points of this PR was to fix the
`ReadConsoleOutputAttribute` API, which was using a simplified legacy
color conversion (the original `TextAttribute:GetLegacyAttributes`
method), which could only handle values from the 16-color table. RGB
values, and colors from the 256-color table, would be mapped to
completely nonsensical values. This API has now been updated to use the
more correct `Settings::GenerateLegacyAttributes` method.
But there were also a couple of other places in the code that were using
`GetLegacyAttributes` when they really had no reason to be working with
legacy attributes at all. This could result in colors being downgraded
to 4-bit values (often badly, as explained above), when the code was
already perfectly capable of displaying the full 24-bits.
This included the fill colors in the IME composer (in `ConsoleImeInfo`),
and the construction of the highlighting colors in the color
search/selection handler (`Selection::_HandleColorSelection`). I also
got rid of some legacy attribute code in the `Popup` class, which was
originally intended to update colors below the popup when the settings
changed, but actually caused more problems than it solved.
The other major goal of this PR was to improve the performance of the
`GenerateLegacyAttributes` method, since the existing implementation
could be quite slow when dealing with RGB values.
The simple cases are handled much the same as they were before. For an
`IsDefault` color, we get the default index from the
`Settings::_wFillAttribute` field. For an `IsIndex16` color, the index
can just be returned as is.
For an `IsRgb` color, the RGB components are compressed down to 8 bits
(3 red, 3 green, 2 blue), simply by dropping the least significant bits.
This 8-bit value is then used to lookup a representative 16-color value
from a hard-coded table. An `IsIndex256` color is also converted with a
lookup table, just using the existing 8-bit index.
The RGB mapping table was calculated by taking each compressed 8-bit
color, and picking a entry from the _Campbell_ palette that best
approximated that color. This was done by looking at a range of 24-bit
colors that mapped to the 8-bit value, finding the best _Campbell_ match
for each of them (using a [CIEDE2000] color difference calculation), and
then the most common match became the index that the 8-bit value would
map to.
The 256-color table was just a simpler version of this process. For each
entry in the table, we take the default RGB palette value, and find it's
closest match in the _Campbell_ palette.
Because these tables are hard-coded, the results won't adjust to changes
in the palette. However, they should still produce reasonable results
for palettes that follow the standard ANSI color range. And since
they're only a very loose approximation of the colors anyway, the exact
value really isn't that important.
That said, I have tried to make sure that if you take an RGB value for a
particular index in a reasonable color scheme, then the legacy color
mapped from that value should ideally match the same index. This will
never be possible for all color schemes, but I have tweaked a few of the
table entries to improve the results for some of the common schemes.
One other point worth making regarding the hard-coded tables: even if we
wanted to take the active palette into account, that wouldn't actually
be possible over a conpty connection, because we can't easily know what
color scheme the client application is using. At least this way the
results in conhost are guaranteed to be the same as in the Windows
Terminal.
[CIEDE2000]: https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
## Validation Steps Performed
This code still passes the `TextAttributeTests` that check the basic
`GetLegacyAttribute` behaviour and verify the all legacy attributes
roundtrip correctly. However, some of the values in the `RgbColorTests`
had to be updated, since we're now intentionally returning different
values as a result of the changes to the RGB conversion algorithm.
I haven't added additional unit tests, but I have done a lot of manual
testing to see how well the new algorithm works with a range of colors
and a variety of different color schemes. It's not perfect in every
situation, but I think it works well enough for the purpose it serves.
I've also confirmed that the issues reported in #5940 and #6247 are now
fixed by these changes.
Closes #5940
Closes #6247
2020-06-08 21:05:06 +02:00
if ( fillCharGiven = = UNICODE_NULL & & fillAttrsGiven = = TextAttribute { 0 } )
2019-05-03 00:29:04 +02:00
{
fillData = OutputCellIterator ( UNICODE_SPACE , screenInfo . GetAttributes ( ) ) ;
}
// ------ 4. PREP TARGET ------
// Now it's time to think about the target. We're only given the origin of the target
// because it is assumed that it will have the same relative dimensions as the original source.
auto targetOrigin = destinationOriginGiven ;
// However, if we got to this point, we may have clipped the source because some part of it
// fell outside of the buffer.
// Apply any delta between the original source rectangle's origin and its current position to
// the target origin.
{
auto currentSourceOrigin = source . Origin ( ) ;
targetOrigin . X + = currentSourceOrigin . X - originalSourceOrigin . X ;
targetOrigin . Y + = currentSourceOrigin . Y - originalSourceOrigin . Y ;
}
// And now the target viewport is the same size as the source viewport but at the different position.
auto target = Viewport : : FromDimensions ( targetOrigin , source . Dimensions ( ) ) ;
// However, this might mean that the target is falling outside of the region we're allowed to edit
// (the clip area). So we need to reduce the target to only inside the clip.
// But backup the original target origin first, because we need to know how it has changed.
const auto originalTargetOrigin = target . Origin ( ) ;
target = Viewport : : Intersect ( clip , target ) ;
// OK, if the target became smaller than before, we need to also adjust the source accordingly
// so we don't waste time loading up/copying things that have no place to go within the target.
{
const auto currentTargetOrigin = target . Origin ( ) ;
auto sourceOrigin = source . Origin ( ) ;
sourceOrigin . X + = currentTargetOrigin . X - originalTargetOrigin . X ;
sourceOrigin . Y + = currentTargetOrigin . Y - originalTargetOrigin . Y ;
source = Viewport : : FromDimensions ( sourceOrigin , target . Dimensions ( ) ) ;
}
// ------ 5. COPY ------
// If the target region is valid, let's do this.
if ( target . IsValid ( ) )
{
// Perform the copy from the source to the target.
_CopyRectangle ( screenInfo , source , target . Origin ( ) ) ;
// Notify the renderer and accessibility as to what moved and where.
_ScrollScreen ( screenInfo , source , fill , target ) ;
}
// ------ 6. FILL ------
// Now fill in anything that wasn't already touched by the copy above.
// Fill as a single viewport represents the entire region we were allowed to
// write into. But since we already copied, filling the whole thing might
// overwrite what we just placed at the target.
// So use the special subtraction function to get the viewports that fall
// within the fill area but outside of the target area.
const auto remaining = Viewport : : Subtract ( fill , target ) ;
// Apply the fill data to each of the viewports we're given here.
for ( size_t i = 0 ; i < remaining . size ( ) ; i + + )
{
const auto & view = remaining . at ( i ) ;
screenInfo . WriteRect ( fillData , view ) ;
Add support for double-width/double-height lines in conhost (#8664)
This PR adds support for the VT line rendition attributes, which allow
for double-width and double-height line renditions. These renditions are
enabled with the `DECDWL` (double-width line) and `DECDHL`
(double-height line) escape sequences. Both reset to the default
rendition with the `DECSWL` (single-width line) escape sequence. For now
this functionality is only supported by the GDI renderer in conhost.
There are a lot of changes, so this is just a general overview of the
main areas affected.
Previously it was safe to assume that the screen had a fixed width, at
least for a given point in time. But now we need to deal with the
possibility of different lines have different widths, so all the
functions that are constrained by the right border (text wrapping,
cursor movement operations, and sequences like `EL` and `ICH`) now need
to lookup the width of the active line in order to behave correctly.
Similarly it used to be safe to assume that buffer and screen
coordinates were the same thing, but that is no longer true. Lots of
places now need to translate back and forth between coordinate systems
dependent on the line rendition. This includes clipboard handling, the
conhost color selection and search, accessibility location tracking and
screen reading, IME editor positioning, "snapping" the viewport, and of
course all the rendering calculations.
For the rendering itself, I've had to introduce a new
`PrepareLineTransform` method that the render engines can use to setup
the necessary transform matrix for a given line rendition. This is also
now used to handle the horizontal viewport offset, since that could no
longer be achieved just by changing the target coordinates (on a double
width line, the viewport offset may be halfway through a character).
I've also had to change the renderer's existing `InvalidateCursor`
method to take a `SMALL_RECT` rather than a `COORD`, to allow for the
cursor being a variable width. Technically this was already a problem,
because the cursor could occupy two screen cells when over a
double-width character, but now it can be anything between one and four
screen cells (e.g. a double-width character on the double-width line).
In terms of architectural changes, there is now a new `lineRendition`
field in the `ROW` class that keeps track of the line rendition for each
row, and several new methods in the `ROW` and `TextBuffer` classes for
manipulating that state. This includes a few helper methods for handling
the various issues discussed above, e.g. position clamping and
translating between coordinate systems.
## Validation Steps Performed
I've manually confirmed all the double-width and double-height tests in
_Vttest_ are now working as expected, and the _VT100 Torture Test_ now
renders correctly (at least the line rendition aspects). I've also got
my own test scripts that check many of the line rendition boundary cases
and have confirmed that those are now passing.
I've manually tested as many areas of the conhost UI that I could think
of, that might be affected by line rendition, including things like
searching, selection, copying, and color highlighting. For
accessibility, I've confirmed that the _Magnifier_ and _Narrator_
correctly handle double-width lines. And I've also tested the Japanese
IME, which while not perfect, is at least useable.
Closes #7865
2021-02-18 06:44:50 +01:00
// If we're scrolling an area that encompasses the full buffer width,
// then the filled rows should also have their line rendition reset.
if ( view . Width ( ) = = buffer . Width ( ) & & destinationOriginGiven . X = = 0 )
{
screenInfo . GetTextBuffer ( ) . ResetLineRenditionRange ( view . Top ( ) , view . BottomExclusive ( ) ) ;
}
2019-05-03 00:29:04 +02:00
}
}
void SetActiveScreenBuffer ( SCREEN_INFORMATION & screenInfo )
{
CONSOLE_INFORMATION & gci = ServiceLocator : : LocateGlobals ( ) . getConsoleInformation ( ) ;
gci . pCurrentScreenBuffer = & screenInfo ;
2020-01-30 21:14:16 +01:00
// initialize cursor GH#4102 - Typically, the cursor is set to on by the
// cursor blinker. Unfortunately, in conpty mode, there is no cursor
// blinker. So, in conpty mode, we need to leave the cursor on always. The
// cursor can still be set to hidden, and whether the cursor should be
// blinking will still be passed through to the terminal, but internally,
// the cursor should always be on.
//
// In particular, some applications make use of a calling
// `SetConsoleScreenBuffer` and `SetCursorPosition` without printing any
// text in between these calls. If we initialize the cursor to Off in conpty
// mode, then the cursor will remain off until they print text. This can
// lead to alignment problems in the terminal, because we won't move the
// terminal's cursor in this _exact_ scenario.
screenInfo . GetTextBuffer ( ) . GetCursor ( ) . SetIsOn ( gci . IsInVtIoMode ( ) ) ;
2019-05-03 00:29:04 +02:00
// set font
screenInfo . RefreshFontWithRenderer ( ) ;
// Empty input buffer.
gci . pInputBuffer - > FlushAllButKeys ( ) ;
// Set window size.
screenInfo . PostUpdateWindowSize ( ) ;
gci . ConsoleIme . RefreshAreaAttributes ( ) ;
// Write data to screen.
WriteToScreen ( screenInfo , screenInfo . GetViewport ( ) ) ;
}
// TODO: MSFT 9450717 This should join the ProcessList class when CtrlEvents become moved into the server. https://osgvsowi/9450717
void CloseConsoleProcessState ( )
{
2019-11-08 22:44:52 +01:00
const CONSOLE_INFORMATION & gci = ServiceLocator : : LocateGlobals ( ) . getConsoleInformation ( ) ;
2019-05-03 00:29:04 +02:00
// If there are no connected processes, sending control events is pointless as there's no one do send them to. In
// this case we'll just exit conhost.
// N.B. We can get into this state when a process has a reference to the console but hasn't connected. For example,
// when it's created suspended and never resumed.
if ( gci . ProcessHandleList . IsEmpty ( ) )
{
ServiceLocator : : RundownAndExit ( STATUS_SUCCESS ) ;
}
HandleCtrlEvent ( CTRL_CLOSE_EVENT ) ;
2019-11-08 22:44:52 +01:00
// Jiggle the handle: (see MSFT:19419231)
// When we call this function, we'll only actually close the console once
// we're totally unlocked. If our caller has the console locked, great,
2020-02-10 21:40:01 +01:00
// we'll dispatch the ctrl event once they unlock. However, if they're
2019-11-08 22:44:52 +01:00
// not running under lock (eg PtySignalInputThread::_GetData), then the
// ctrl event will never actually get dispatched.
// So, lock and unlock here, to make sure the ctrl event gets handled.
LockConsole ( ) ;
auto Unlock = wil : : scope_exit ( [ & ] { UnlockConsole ( ) ; } ) ;
2019-05-03 00:29:04 +02:00
}