[CONSRV] ConDrvScrollConsoleScreenBuffer() fixes. (#2278)

- Reimplement ConDrvScrollConsoleScreenBuffer() with separate copy and
  fill helper functions and calculate rectangles in such a way as to
  never use X-Y coordinates pointing outside of the screen buffer.

- Add X-Y coordinates assertions in ConioCoordToPointer().
This commit is contained in:
Hermès Bélusca-Maïto 2020-02-22 19:18:44 +01:00
parent d46e054368
commit 05053b6f18
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0

View file

@ -143,6 +143,8 @@ TEXTMODE_BUFFER_Destroy(IN OUT PCONSOLE_SCREEN_BUFFER Buffer)
PCHAR_INFO PCHAR_INFO
ConioCoordToPointer(PTEXTMODE_SCREEN_BUFFER Buff, ULONG X, ULONG Y) ConioCoordToPointer(PTEXTMODE_SCREEN_BUFFER Buff, ULONG X, ULONG Y)
{ {
ASSERT(X < Buff->ScreenBufferSize.X);
ASSERT(Y < Buff->ScreenBufferSize.Y);
return &Buff->Buffer[((Y + Buff->VirtualY) % Buff->ScreenBufferSize.Y) * Buff->ScreenBufferSize.X + X]; return &Buff->Buffer[((Y + Buff->VirtualY) % Buff->ScreenBufferSize.Y) * Buff->ScreenBufferSize.X + X];
} }
@ -185,78 +187,166 @@ ConioComputeUpdateRect(IN PTEXTMODE_SCREEN_BUFFER Buff,
} }
/* /*
* Move from one rectangle to another. We must be careful about the order that * Copy from one rectangle to another. We must be careful about the order of
* this is done, to avoid overwriting parts of the source before they are moved. * operations, to avoid overwriting parts of the source before they are copied.
*/ */
static VOID static VOID
ConioMoveRegion(PTEXTMODE_SCREEN_BUFFER ScreenBuffer, ConioCopyRegion(
PSMALL_RECT SrcRegion, IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer,
PSMALL_RECT DstRegion, IN PSMALL_RECT SrcRegion,
PSMALL_RECT ClipRegion, IN PCOORD DstOrigin)
CHAR_INFO FillChar)
{ {
UINT Width = ConioRectWidth(SrcRegion); UINT Width, Height;
UINT Height = ConioRectHeight(SrcRegion); SHORT SY, DY;
INT SXOrg, SX, SY; SHORT YDelta;
INT DXOrg, DX, DY; PCHAR_INFO PtrSrc, PtrDst;
INT XDelta, YDelta; #if 0
SHORT SXOrg, DXOrg;
SHORT SX, DX;
SHORT XDelta;
UINT i, j; UINT i, j;
CHAR_INFO Cell; #endif
PCHAR_INFO SRow, DRow;
if (ConioIsRectEmpty(SrcRegion))
return;
#if 0
ASSERT(SrcRegion->Left >= 0 && SrcRegion->Left < ScreenBuffer->ScreenBufferSize.X);
ASSERT(SrcRegion->Right >= 0 && SrcRegion->Right < ScreenBuffer->ScreenBufferSize.X);
ASSERT(SrcRegion->Top >= 0 && SrcRegion->Top < ScreenBuffer->ScreenBufferSize.Y);
ASSERT(SrcRegion->Bottom >= 0 && SrcRegion->Bottom < ScreenBuffer->ScreenBufferSize.Y);
// ASSERT(DstOrigin->X >= 0 && DstOrigin->X < ScreenBuffer->ScreenBufferSize.X);
// ASSERT(DstOrigin->Y >= 0 && DstOrigin->Y < ScreenBuffer->ScreenBufferSize.Y);
#endif
/* If the source and destination regions are the same, just bail out */
if ((SrcRegion->Left == DstOrigin->X) && (SrcRegion->Top == DstOrigin->Y))
return;
SY = SrcRegion->Top; SY = SrcRegion->Top;
DY = DstRegion->Top; DY = DstOrigin->Y;
YDelta = 1; YDelta = 1;
if (SY < DY) if (SY < DY)
{ {
/* Moving down: work from bottom up */ /* Moving down: work from bottom up */
SY = SrcRegion->Bottom; SY = SrcRegion->Bottom;
DY = DstRegion->Bottom; DY = DstOrigin->Y + (SrcRegion->Bottom - SrcRegion->Top);
YDelta = -1; YDelta = -1;
} }
#if 0
SXOrg = SrcRegion->Left; SXOrg = SrcRegion->Left;
DXOrg = DstRegion->Left; DXOrg = DstOrigin->X;
XDelta = 1; XDelta = 1;
if (SXOrg < DXOrg) if (SXOrg < DXOrg)
{ {
/* Moving right: work from right to left */ /* Moving right: work from right to left */
SXOrg = SrcRegion->Right; SXOrg = SrcRegion->Right;
DXOrg = DstRegion->Right; DXOrg = DstOrigin->X + (SrcRegion->Right - SrcRegion->Left);
XDelta = -1; XDelta = -1;
} }
#endif
for (i = 0; i < Height; i++) /* Loop through the source region */
Width = ConioRectWidth(SrcRegion);
Height = ConioRectHeight(SrcRegion);
#if 0
for (i = 0; i < Height; ++i, SY += YDelta, DY += YDelta)
#else
for (; Height-- > 0; SY += YDelta, DY += YDelta)
#endif
{ {
SRow = ConioCoordToPointer(ScreenBuffer, 0, SY); #if 0
DRow = ConioCoordToPointer(ScreenBuffer, 0, DY);
SX = SXOrg; SX = SXOrg;
DX = DXOrg; DX = DXOrg;
// TODO: Correctly support "moving" full-width characters. PtrSrc = ConioCoordToPointer(ScreenBuffer, SX, SY);
PtrDst = ConioCoordToPointer(ScreenBuffer, DX, DY);
#else
PtrSrc = ConioCoordToPointer(ScreenBuffer, SrcRegion->Left, SY);
PtrDst = ConioCoordToPointer(ScreenBuffer, DstOrigin->X, DY);
#endif
for (j = 0; j < Width; j++) // TODO: Correctly support copying full-width characters.
// By construction the source region is supposed to contain valid
// (possibly fullwidth) characters, so for these after the copy
// we need to check the characters at the borders and adjust the
// attributes accordingly.
#if 0
for (j = 0; j < Width; ++j, SX += XDelta, DX += XDelta)
{ {
Cell = SRow[SX]; *PtrDst = *PtrSrc;
if (SX >= ClipRegion->Left && SX <= ClipRegion->Right && PtrSrc += XDelta;
SY >= ClipRegion->Top && SY <= ClipRegion->Bottom) PtrDst += XDelta;
{
SRow[SX] = FillChar;
}
if (DX >= ClipRegion->Left && DX <= ClipRegion->Right &&
DY >= ClipRegion->Top && DY <= ClipRegion->Bottom)
{
DRow[DX] = Cell;
}
SX += XDelta;
DX += XDelta;
} }
SY += YDelta; #else
DY += YDelta; /* RtlMoveMemory() takes into account for the direction of the copy */
RtlMoveMemory(PtrDst, PtrSrc, Width * sizeof(CHAR_INFO));
#endif
} }
} }
static VOID
ConioFillRegion(
IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer,
IN PSMALL_RECT Region,
IN PSMALL_RECT ExcludeRegion OPTIONAL,
IN CHAR_INFO FillChar)
{
SHORT X, Y;
PCHAR_INFO Ptr;
// BOOLEAN bFullwidth;
/* Bail out if the region to fill is empty */
if (ConioIsRectEmpty(Region))
return;
/* Sanitize the exclusion region: if it's empty, ignore the region */
if (ExcludeRegion && ConioIsRectEmpty(ExcludeRegion))
ExcludeRegion = NULL;
#if 0
ASSERT(Region->Left >= 0 && Region->Left < ScreenBuffer->ScreenBufferSize.X);
ASSERT(Region->Right >= 0 && Region->Right < ScreenBuffer->ScreenBufferSize.X);
ASSERT(Region->Top >= 0 && Region->Top < ScreenBuffer->ScreenBufferSize.Y);
ASSERT(Region->Bottom >= 0 && Region->Bottom < ScreenBuffer->ScreenBufferSize.Y);
if (ExcludeRegion)
{
ASSERT(ExcludeRegion->Left >= 0 && ExcludeRegion->Left < ScreenBuffer->ScreenBufferSize.X);
ASSERT(ExcludeRegion->Right >= 0 && ExcludeRegion->Right < ScreenBuffer->ScreenBufferSize.X);
ASSERT(ExcludeRegion->Top >= 0 && ExcludeRegion->Top < ScreenBuffer->ScreenBufferSize.Y);
ASSERT(ExcludeRegion->Bottom >= 0 && ExcludeRegion->Bottom < ScreenBuffer->ScreenBufferSize.Y);
}
#endif
// bFullwidth = (ScreenBuffer->Header.Console->IsCJK && IS_FULL_WIDTH(FillChar.Char.UnicodeChar));
/* Loop through the destination region */
for (Y = Region->Top; Y <= Region->Bottom; ++Y)
{
Ptr = ConioCoordToPointer(ScreenBuffer, Region->Left, Y);
for (X = Region->Left; X <= Region->Right; ++X)
{
// TODO: Correctly support filling with full-width characters.
if (!ExcludeRegion ||
!(X >= ExcludeRegion->Left && X <= ExcludeRegion->Right &&
Y >= ExcludeRegion->Top && Y <= ExcludeRegion->Bottom))
{
/* We are outside the excluded region, fill the destination */
*Ptr = FillChar;
// Ptr->Attributes &= ~COMMON_LVB_SBCSDBCS;
}
++Ptr;
}
}
}
// FIXME! // FIXME!
NTSTATUS NTAPI NTSTATUS NTAPI
ConDrvWriteConsoleInput(IN PCONSOLE Console, ConDrvWriteConsoleInput(IN PCONSOLE Console,
@ -1366,24 +1456,25 @@ ConDrvSetConsoleScreenBufferSize(IN PCONSOLE Console,
} }
NTSTATUS NTAPI NTSTATUS NTAPI
ConDrvScrollConsoleScreenBuffer(IN PCONSOLE Console, ConDrvScrollConsoleScreenBuffer(
IN PTEXTMODE_SCREEN_BUFFER Buffer, IN PCONSOLE Console,
IN BOOLEAN Unicode, IN PTEXTMODE_SCREEN_BUFFER Buffer,
IN PSMALL_RECT ScrollRectangle, IN BOOLEAN Unicode,
IN BOOLEAN UseClipRectangle, IN PSMALL_RECT ScrollRectangle,
IN PSMALL_RECT ClipRectangle OPTIONAL, IN BOOLEAN UseClipRectangle,
IN PCOORD DestinationOrigin, IN PSMALL_RECT ClipRectangle OPTIONAL,
IN CHAR_INFO FillChar) IN PCOORD DestinationOrigin,
IN CHAR_INFO FillChar)
{ {
COORD CapturedDestinationOrigin; COORD CapturedDestinationOrigin;
SMALL_RECT ScreenBuffer; SMALL_RECT ScreenBuffer;
SMALL_RECT CapturedClipRectangle;
SMALL_RECT SrcRegion; SMALL_RECT SrcRegion;
SMALL_RECT DstRegion; SMALL_RECT DstRegion;
SMALL_RECT UpdateRegion; SMALL_RECT UpdateRegion;
SMALL_RECT CapturedClipRectangle;
if (Console == NULL || Buffer == NULL || ScrollRectangle == NULL || if (Console == NULL || Buffer == NULL || ScrollRectangle == NULL ||
(UseClipRectangle ? ClipRectangle == NULL : FALSE) || DestinationOrigin == NULL) (UseClipRectangle && (ClipRectangle == NULL)) || DestinationOrigin == NULL)
{ {
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
} }
@ -1404,14 +1495,14 @@ ConDrvScrollConsoleScreenBuffer(IN PCONSOLE Console,
/* If the source was clipped on the left or top, adjust the destination accordingly */ /* If the source was clipped on the left or top, adjust the destination accordingly */
if (ScrollRectangle->Left < 0) if (ScrollRectangle->Left < 0)
{
CapturedDestinationOrigin.X -= ScrollRectangle->Left; CapturedDestinationOrigin.X -= ScrollRectangle->Left;
}
if (ScrollRectangle->Top < 0) if (ScrollRectangle->Top < 0)
{
CapturedDestinationOrigin.Y -= ScrollRectangle->Top; CapturedDestinationOrigin.Y -= ScrollRectangle->Top;
}
/*
* If a clip rectangle is provided, clip it to the screen buffer,
* otherwise use the latter one as the clip rectangle.
*/
if (UseClipRectangle) if (UseClipRectangle)
{ {
CapturedClipRectangle = *ClipRectangle; CapturedClipRectangle = *ClipRectangle;
@ -1425,14 +1516,44 @@ ConDrvScrollConsoleScreenBuffer(IN PCONSOLE Console,
CapturedClipRectangle = ScreenBuffer; CapturedClipRectangle = ScreenBuffer;
} }
/*
* Windows compatibility: Do nothing if the intersection of the source region
* with the clip rectangle is empty, even if the intersection of destination
* region with the clip rectangle is NOT empty and therefore it would have
* been possible to copy contents to it...
*/
if (!ConioGetIntersection(&UpdateRegion, &SrcRegion, &CapturedClipRectangle))
{
return STATUS_SUCCESS;
}
/* Initialize the destination rectangle, of same size as the source rectangle */
ConioInitRect(&DstRegion, ConioInitRect(&DstRegion,
CapturedDestinationOrigin.Y, CapturedDestinationOrigin.Y,
CapturedDestinationOrigin.X, CapturedDestinationOrigin.X,
CapturedDestinationOrigin.Y + ConioRectHeight(&SrcRegion) - 1, CapturedDestinationOrigin.Y + ConioRectHeight(&SrcRegion) - 1,
CapturedDestinationOrigin.X + ConioRectWidth(&SrcRegion ) - 1); CapturedDestinationOrigin.X + ConioRectWidth(&SrcRegion ) - 1);
if (ConioGetIntersection(&DstRegion, &DstRegion, &CapturedClipRectangle))
{
/*
* Build the region image, within the source region,
* of the destination region we should copy into.
*/
SrcRegion.Left += DstRegion.Left - CapturedDestinationOrigin.X;
SrcRegion.Top += DstRegion.Top - CapturedDestinationOrigin.Y;
SrcRegion.Right = SrcRegion.Left + (DstRegion.Right - DstRegion.Left);
SrcRegion.Bottom = SrcRegion.Top + (DstRegion.Bottom - DstRegion.Top);
/* Do the copy */
CapturedDestinationOrigin.X = DstRegion.Left;
CapturedDestinationOrigin.Y = DstRegion.Top;
ConioCopyRegion(Buffer, &SrcRegion, &CapturedDestinationOrigin);
}
if (!Unicode) if (!Unicode)
{ {
/* Conversion from the ASCII char to the UNICODE char */
WCHAR tmp; WCHAR tmp;
ConsoleOutputAnsiToUnicodeChar(Console, &tmp, &FillChar.Char.AsciiChar); ConsoleOutputAnsiToUnicodeChar(Console, &tmp, &FillChar.Char.AsciiChar);
FillChar.Char.UnicodeChar = tmp; FillChar.Char.UnicodeChar = tmp;
@ -1440,11 +1561,15 @@ ConDrvScrollConsoleScreenBuffer(IN PCONSOLE Console,
/* Sanitize the attribute */ /* Sanitize the attribute */
FillChar.Attributes &= ~COMMON_LVB_SBCSDBCS; FillChar.Attributes &= ~COMMON_LVB_SBCSDBCS;
ConioMoveRegion(Buffer, &SrcRegion, &DstRegion, &CapturedClipRectangle, FillChar); /*
* Fill the intersection (== UpdateRegion) of the source region with the
* clip rectangle, excluding the destination region.
*/
ConioFillRegion(Buffer, &UpdateRegion, &DstRegion, FillChar);
if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer) if ((PCONSOLE_SCREEN_BUFFER)Buffer == Console->ActiveBuffer)
{ {
ConioGetUnion(&UpdateRegion, &SrcRegion, &DstRegion); ConioGetUnion(&UpdateRegion, &UpdateRegion, &DstRegion);
if (ConioGetIntersection(&UpdateRegion, &UpdateRegion, &CapturedClipRectangle)) if (ConioGetIntersection(&UpdateRegion, &UpdateRegion, &CapturedClipRectangle))
{ {
/* Draw update region */ /* Draw update region */