Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6386 lines
203 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
output.c
Abstract:
This file implements the video buffer management.
Author:
Therese Stowell (thereses) 6-Nov-1990
Revision History:
Notes:
ScreenBuffer data structure overview:
each screen buffer has an array of ROW structures. each ROW structure
contains the data for one row of text. the data stored for one row of
text is a character array and an attribute array. the character array
is allocated the full length of the row from the heap, regardless of the
non-space length. we also maintain the non-space length. the character
array is initialized to spaces. the attribute
array is run length encoded (i.e 5 BLUE, 3 RED). if there is only one
attribute for the whole row (the normal case), it is stored in the ATTR_ROW
structure. otherwise the attr string is allocated from the heap.
ROW - CHAR_ROW - CHAR string
\ \ length of char string
\
ATTR_ROW - ATTR_PAIR string
\ length of attr pair string
ROW
ROW
ROW
ScreenInfo->Rows points to the ROW array. ScreenInfo->Rows[0] is not
necessarily the top row. ScreenInfo->BufferInfo.TextInfo.FirstRow contains the index of
the top row. That means scrolling (if scrolling entire screen)
merely involves changing the FirstRow index,
filling in the last row, and updating the screen.
--*/
#include "precomp.h"
#pragma hdrstop
//#define THERESES_DEBUG 1
//#define PROFILE_GDI
#ifdef PROFILE_GDI
LONG ScrollDCCount=0;
LONG ExtTextOutCount=0;
LONG TextColor=1;
#define SCROLLDC_CALL ScrollDCCount+=1
#define TEXTOUT_CALL ExtTextOutCount+=1
#define TEXTCOLOR_CALL TextColor+=1
#else
#define SCROLLDC_CALL
#define TEXTOUT_CALL
#define TEXTCOLOR_CALL
#endif
#define ITEM_MAX_SIZE 256
// BUGBUG get the real include file from progman
typedef struct _PMIconData {
DWORD dwResSize;
DWORD dwVer;
BYTE iResource; // icon resource
} PMICONDATA, *LPPMICONDATA;
extern UINT ProgmanHandleMessage;
//
// Screen dimensions
//
RECT ConsoleWorkArea;
int ConsoleFullScreenX = 0;
int ConsoleFullScreenY = 0;
int MinimumWidthX = 0;
SHORT VerticalScrollSize = 0;
SHORT HorizontalScrollSize = 0;
SHORT VerticalClientToWindow = 0;
SHORT HorizontalClientToWindow = 0;
PCHAR_INFO ScrollBuffer = 0;
ULONG ScrollBufferSize = 0;
CRITICAL_SECTION ScrollBufferLock;
// this value keeps track of the number of existing console windows.
// if a window is created when this value is zero, the Face Names
// must be reenumerated because no WM_FONTCHANGE message was processed
// if there's no window.
LONG gnConsoleWindows=0;
BOOL gfInitSystemMetrics = FALSE;
BOOL UsePolyTextOut;
HRGN ghrgnScroll = NULL;
LPRGNDATA gprgnData = NULL;
ULONG gucWheelScrollLines;
#define GRGNDATASIZE (sizeof(RGNDATAHEADER) + (6 * sizeof(RECTL)))
#define CharSizeOf(x) (sizeof(x) / sizeof(*x))
typedef struct _DROPFILES {
DWORD pFiles; // offset of file list
POINT pt; // drop point (client coords)
BOOL fNC; // is it on NonClient area
// and pt is in screen coords
BOOL fWide; // WIDE character switch
} DROPFILES, FAR * LPDROPFILES;
#define LockScrollBuffer() RtlEnterCriticalSection(&ScrollBufferLock)
#define UnlockScrollBuffer() RtlLeaveCriticalSection(&ScrollBufferLock)
VOID FreeConsoleBitmap(IN PSCREEN_INFORMATION ScreenInfo);
VOID
ScrollIfNecessary(
IN PCONSOLE_INFORMATION Console,
IN PSCREEN_INFORMATION ScreenInfo
);
VOID
ProcessResizeWindow(
IN PSCREEN_INFORMATION ScreenInfo,
IN PCONSOLE_INFORMATION Console,
IN LPWINDOWPOS WindowPos
);
NTSTATUS
AllocateScrollBuffer(
DWORD Size
);
VOID FreeScrollBuffer ( VOID );
VOID
InternalUpdateScrollBars(
IN PSCREEN_INFORMATION ScreenInfo
);
BOOL
PolyTextOutCandidate(
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT Region
);
VOID
ConsolePolyTextOut(
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT Region
);
VOID
InitializeSystemMetrics( VOID )
{
RECT WindowSize;
gfInitSystemMetrics = FALSE;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &gucWheelScrollLines, FALSE);
SystemParametersInfo(SPI_GETWORKAREA, 0, &ConsoleWorkArea, FALSE);
ConsoleFullScreenX = GetSystemMetrics(SM_CXFULLSCREEN);
ConsoleFullScreenY = GetSystemMetrics(SM_CYFULLSCREEN);
VerticalScrollSize = GetSystemMetrics(SM_CXVSCROLL);
HorizontalScrollSize = GetSystemMetrics(SM_CYHSCROLL);
WindowSize.left = WindowSize.top = 0;
WindowSize.right = WindowSize.bottom = 50;
AdjustWindowRectEx(&WindowSize,
CONSOLE_WINDOW_FLAGS,
FALSE,
CONSOLE_WINDOW_EX_FLAGS
);
VerticalClientToWindow = WindowSize.right-WindowSize.left-50;
HorizontalClientToWindow = WindowSize.bottom-WindowSize.top-50;
}
VOID
UpdateScreenSizes(
IN PSCREEN_INFORMATION ScreenInfo,
IN COORD dwScreenBufferSize
)
{
//
// If the system metrics have changed or there aren't any console
// windows around, reinitialize the global valeus.
//
if (gfInitSystemMetrics || gnConsoleWindows == 0) {
InitializeSystemMetrics();
}
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
COORD FontSize = SCR_FONTSIZE(ScreenInfo);
ScreenInfo->MinX = ((MinimumWidthX - VerticalClientToWindow + FontSize.X - 1) / FontSize.X);
ScreenInfo->MaximumWindowSize.X = min(ConsoleFullScreenX/FontSize.X, dwScreenBufferSize.X);
ScreenInfo->MaximumWindowSize.X = max(ScreenInfo->MaximumWindowSize.X, ScreenInfo->MinX);
ScreenInfo->MaximumWindowSize.Y = min(ConsoleFullScreenY/FontSize.Y, dwScreenBufferSize.Y);
ScreenInfo->MaxWindow.X = ScreenInfo->MaximumWindowSize.X*FontSize.X + VerticalClientToWindow;
ScreenInfo->MaxWindow.Y = ScreenInfo->MaximumWindowSize.Y*FontSize.Y + HorizontalClientToWindow;
} else {
ScreenInfo->MinX = MinimumWidthX - VerticalClientToWindow;
ScreenInfo->MaximumWindowSize.X = min(ConsoleFullScreenX, dwScreenBufferSize.X);
ScreenInfo->MaximumWindowSize.Y = min(ConsoleFullScreenY, dwScreenBufferSize.Y);
ScreenInfo->MaxWindow.X = ScreenInfo->MaximumWindowSize.X + VerticalClientToWindow;
ScreenInfo->MaxWindow.Y = ScreenInfo->MaximumWindowSize.Y + HorizontalClientToWindow;
}
}
VOID
InitializeScreenInfo( VOID )
{
HDC hDC;
InitializeMouseButtons();
MinimumWidthX = GetSystemMetrics(SM_CXMIN);
InitializeSystemMetrics();
hDC = CreateDCW(L"DISPLAY",NULL,NULL,NULL);
if (hDC != NULL) {
UsePolyTextOut = GetDeviceCaps(hDC,TEXTCAPS) & TC_SCROLLBLT;
DeleteDC(hDC);
}
}
NTSTATUS
DoCreateScreenBuffer(
IN PCONSOLE_INFORMATION Console,
IN PCONSOLE_INFO ConsoleInfo,
IN LPWSTR ConsoleTitle
)
/*++
this routine figures out what parameters to pass to CreateScreenBuffer,
based on the data from STARTUPINFO and the defaults in win.ini,
then calls CreateScreenBuffer.
--*/
{
CHAR_INFO Fill,PopupFill;
COORD dwScreenBufferSize, dwWindowSize;
NTSTATUS Status;
int FontIndexWant;
CONSOLE_REGISTRY_INFO RegInfo;
if (ConsoleInfo->dwStartupFlags & STARTF_USESHOWWINDOW) {
Console->wShowWindow = ConsoleInfo->wShowWindow;
} else {
Console->wShowWindow = SW_SHOWNORMAL;
}
if (ConsoleInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) {
#if 0
{
INT i;
DbgPrint("[Link Server Properties for %ws]\n", ConsoleTitle );
DbgPrint(" wFillAttribute = 0x%04X\n", ConsoleInfo->wFillAttribute );
DbgPrint(" wPopupFillAttribute = 0x%04X\n", ConsoleInfo->wPopupFillAttribute );
DbgPrint(" dwScreenBufferSize = (%d , %d)\n", ConsoleInfo->dwScreenBufferSize.X, ConsoleInfo->dwScreenBufferSize.Y );
DbgPrint(" dwWindowSize = (%d , %d)\n", ConsoleInfo->dwWindowSize.X, ConsoleInfo->dwWindowSize.Y );
DbgPrint(" dwWindowOrigin = (%d , %d)\n", ConsoleInfo->dwWindowOrigin.X, ConsoleInfo->dwWindowOrigin.Y );
DbgPrint(" nFont = %d\n", ConsoleInfo->nFont );
DbgPrint(" nInputBufferSize = %d\n", ConsoleInfo->nInputBufferSize );
DbgPrint(" dwFontSize = (%d , %d)\n", ConsoleInfo->dwFontSize.X, ConsoleInfo->dwFontSize.Y );
DbgPrint(" uFontFamily = 0x%08X\n", ConsoleInfo->uFontFamily );
DbgPrint(" uFontWeight = 0x%08X\n", ConsoleInfo->uFontWeight );
DbgPrint(" FaceName = %ws\n", ConsoleInfo->FaceName );
DbgPrint(" uCursorSize = %d\n", ConsoleInfo->uCursorSize );
DbgPrint(" bFullScreen = %s\n", ConsoleInfo->bFullScreen ? "TRUE" : "FALSE" );
DbgPrint(" bQuickEdit = %s\n", ConsoleInfo->bQuickEdit ? "TRUE" : "FALSE" );
DbgPrint(" bInsertMode = %s\n", ConsoleInfo->bInsertMode ? "TRUE" : "FALSE" );
DbgPrint(" bAutoPosition = %s\n", ConsoleInfo->bAutoPosition ? "TRUE" : "FALSE" );
DbgPrint(" uHistoryBufferSize = %d\n", ConsoleInfo->uHistoryBufferSize );
DbgPrint(" uNumHistoryBuffers = %d\n", ConsoleInfo->uNumberOfHistoryBuffers );
DbgPrint(" bHistoryNoDup = %s\n", ConsoleInfo->bHistoryNoDup ? "TRUE" : "FALSE" );
DbgPrint(" ColorTable = [" );
i=0;
while( i < 16 )
{
DbgPrint("\n ");
DbgPrint("0x%08X ", ConsoleInfo->ColorTable[i++]);
DbgPrint("0x%08X ", ConsoleInfo->ColorTable[i++]);
DbgPrint("0x%08X ", ConsoleInfo->ColorTable[i++]);
DbgPrint("0x%08X ", ConsoleInfo->ColorTable[i++]);
}
DbgPrint( "]\n\n" );
}
#endif
//
// Get values from consoleinfo (which was initialized through link)
//
Fill.Attributes = ConsoleInfo->wFillAttribute;
Fill.Char.UnicodeChar = (WCHAR)' ';
PopupFill.Attributes = ConsoleInfo->wPopupFillAttribute;
PopupFill.Char.UnicodeChar = (WCHAR)' ';
dwScreenBufferSize.X = ConsoleInfo->dwScreenBufferSize.X;
dwScreenBufferSize.Y = ConsoleInfo->dwScreenBufferSize.Y;
//
// Grab font
//
FontIndexWant = FindCreateFont(ConsoleInfo->uFontFamily,
ConsoleInfo->FaceName,
ConsoleInfo->dwFontSize,
ConsoleInfo->uFontWeight);
//
// grab window size information
//
dwWindowSize.X = ConsoleInfo->dwWindowSize.X;
dwWindowSize.Y = ConsoleInfo->dwWindowSize.Y;
if (dwScreenBufferSize.X < dwWindowSize.X)
dwScreenBufferSize.X = dwWindowSize.X;
if (dwScreenBufferSize.Y < dwWindowSize.Y)
dwScreenBufferSize.Y = dwWindowSize.Y;
Console->dwWindowOriginX = ConsoleInfo->dwWindowOrigin.X;
Console->dwWindowOriginY = ConsoleInfo->dwWindowOrigin.Y;
if (ConsoleInfo->bAutoPosition) {
Console->Flags |= CONSOLE_AUTO_POSITION;
Console->dwWindowOriginX = CW_USEDEFAULT;
}
#ifdef i386
if (FullScreenInitialized) {
if (ConsoleInfo->bFullScreen) {
Console->FullScreenFlags = CONSOLE_FULLSCREEN;
}
}
#endif
if (ConsoleInfo->bQuickEdit) {
Console->Flags |= CONSOLE_QUICK_EDIT_MODE;
}
Console->Flags |= CONSOLE_USE_PRIVATE_FLAGS;
Console->InsertMode = ConsoleInfo->bInsertMode;
Console->CommandHistorySize = (SHORT)ConsoleInfo->uHistoryBufferSize;
Console->MaxCommandHistories = (SHORT)ConsoleInfo->uNumberOfHistoryBuffers;
if (ConsoleInfo->bHistoryNoDup) {
Console->Flags |= CONSOLE_HISTORY_NODUP;
} else {
Console->Flags &= ~CONSOLE_HISTORY_NODUP;
}
RtlCopyMemory(Console->ColorTable, ConsoleInfo->ColorTable, sizeof( Console->ColorTable ));
Status = CreateScreenBuffer(&Console->ScreenBuffers,
dwWindowSize,
FontIndexWant,
dwScreenBufferSize,
&Fill,
&PopupFill,
Console,
CONSOLE_TEXTMODE_BUFFER,
NULL,
NULL,
NULL,
ConsoleInfo->uCursorSize
);
if (NT_SUCCESS(Status)) {
wcscpy(Console->ScreenBuffers->BufferInfo.TextInfo.FaceName,
ConsoleInfo->FaceName);
}
} else {
//
// read values from the registry
//
RegInfo = DefaultRegInfo;
GetRegistryValues(ConsoleTitle, &RegInfo);
//
// if screen fill specified in STARTUP info, use it. otherwise
// see if screen fill saved in registry. if so, use that value.
//
if (ConsoleInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE) {
Fill.Attributes = ConsoleInfo->wFillAttribute;
Fill.Char.UnicodeChar = (WCHAR)' ';
} else {
Fill = RegInfo.ScreenFill;
}
PopupFill = RegInfo.PopupFill;
//
// if screen buffer size specified in STARTUP info, use it. otherwise
// see if screen buffer size saved in registry. if so, use that value.
//
if (ConsoleInfo->dwStartupFlags & STARTF_USECOUNTCHARS) {
dwScreenBufferSize.X = ConsoleInfo->dwScreenBufferSize.X;
dwScreenBufferSize.Y = ConsoleInfo->dwScreenBufferSize.Y;
} else {
dwScreenBufferSize = RegInfo.ScreenBufferSize;
if (Console->Flags & CONSOLE_NO_WINDOW) {
dwScreenBufferSize.X = min(dwScreenBufferSize.X, 80);
dwScreenBufferSize.Y = min(dwScreenBufferSize.Y, 25);
}
}
if (dwScreenBufferSize.X == 0)
dwScreenBufferSize.X = 1;
if (dwScreenBufferSize.Y == 0)
dwScreenBufferSize.Y = 1;
DBGPRINT(("dwScreenBufferSize = (%d,%d)\n",
dwScreenBufferSize.X, dwScreenBufferSize.Y));
//
// see if font size saved in registry. if so, try to match it
// to one of the fonts.
//
FontIndexWant = FindCreateFont(RegInfo.FontFamily,
RegInfo.FaceName,
RegInfo.FontSize,
RegInfo.FontWeight);
//
// if window size specified in STARTUP info, use it. otherwise
// see if window size saved in registry. if so, use that value.
//
if (ConsoleInfo->dwStartupFlags & STARTF_USESIZE) {
dwWindowSize.X = ConsoleInfo->dwWindowSize.X /
FontInfo[FontIndexWant].Size.X;
dwWindowSize.Y = ConsoleInfo->dwWindowSize.Y /
FontInfo[FontIndexWant].Size.Y;
} else {
dwWindowSize = RegInfo.WindowSize;
if (Console->Flags & CONSOLE_NO_WINDOW) {
dwWindowSize.X = min(dwWindowSize.X, 80);
dwWindowSize.Y = min(dwWindowSize.Y, 25);
}
}
if (dwWindowSize.X == 0)
dwWindowSize.X = 1;
if (dwWindowSize.Y == 0)
dwWindowSize.Y = 1;
if (dwScreenBufferSize.X < dwWindowSize.X)
dwScreenBufferSize.X = dwWindowSize.X;
if (dwScreenBufferSize.Y < dwWindowSize.Y)
dwScreenBufferSize.Y = dwWindowSize.Y;
if (ConsoleInfo->dwStartupFlags & STARTF_USEPOSITION) {
Console->dwWindowOriginX = ConsoleInfo->dwWindowOrigin.X;
Console->dwWindowOriginY = ConsoleInfo->dwWindowOrigin.Y;
} else {
Console->dwWindowOriginX = RegInfo.WindowPosX;
Console->dwWindowOriginY = RegInfo.WindowPosY;
}
if (Console->dwWindowOriginX == CW_USEDEFAULT) {
Console->Flags |= CONSOLE_AUTO_POSITION;
}
#ifdef i386
if (FullScreenInitialized) {
if (ConsoleInfo->dwStartupFlags & STARTF_RUNFULLSCREEN) {
Console->FullScreenFlags = CONSOLE_FULLSCREEN;
} else if (RegInfo.FullScreen) {
Console->FullScreenFlags = CONSOLE_FULLSCREEN;
}
}
#endif
if (RegInfo.QuickEdit) {
Console->Flags |= CONSOLE_QUICK_EDIT_MODE;
}
Console->Flags |= CONSOLE_USE_PRIVATE_FLAGS;
Console->InsertMode = RegInfo.InsertMode;
Console->CommandHistorySize = (SHORT)RegInfo.HistoryBufferSize;
Console->MaxCommandHistories = (SHORT)RegInfo.NumberOfHistoryBuffers;
if (RegInfo.HistoryNoDup) {
Console->Flags |= CONSOLE_HISTORY_NODUP;
} else {
Console->Flags &= ~CONSOLE_HISTORY_NODUP;
}
RtlCopyMemory(Console->ColorTable, RegInfo.ColorTable, sizeof( Console->ColorTable ));
#if 0
{
DbgPrint("[Registry Server Properties for %ws]\n", ConsoleTitle );
DbgPrint(" wFillAttribute = 0x%04X\n", Fill );
DbgPrint(" wPopupFillAttribute = 0x%04X\n", PopupFill );
DbgPrint(" dwScreenBufferSize = (%d , %d)\n", dwScreenBufferSize.X, dwScreenBufferSize.Y );
DbgPrint(" dwWindowSize = (%d , %d)\n", dwWindowSize.X, dwWindowSize.Y );
}
#endif
Status = CreateScreenBuffer(&Console->ScreenBuffers,
dwWindowSize,
FontIndexWant,
dwScreenBufferSize,
&Fill,
&PopupFill,
Console,
CONSOLE_TEXTMODE_BUFFER,
NULL,
NULL,
NULL,
RegInfo.CursorSize
);
if (NT_SUCCESS(Status)) {
wcscpy(Console->ScreenBuffers->BufferInfo.TextInfo.FaceName,
RegInfo.FaceName);
}
}
return Status;
}
NTSTATUS
CreateScreenBuffer(
OUT PSCREEN_INFORMATION *ScreenInformation,
IN COORD dwWindowSize,
IN DWORD nFont,
IN COORD dwScreenBufferSize,
IN PCHAR_INFO Fill,
IN PCHAR_INFO PopupFill,
IN PCONSOLE_INFORMATION Console,
IN DWORD Flags,
IN PCONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo OPTIONAL,
OUT PVOID *lpBitmap OPTIONAL,
OUT HANDLE *hMutex OPTIONAL,
IN UINT CursorSize
)
/*++
Routine Description:
This routine allocates and initializes the data associated with a screen
buffer. It also creates a window.
Arguments:
ScreenInformation - the new screen buffer.
dwWindowSize - the initial size of screen buffer's window (in rows/columns)
nFont - the initial font to generate text with.
dwScreenBufferSize - the initial size of the screen buffer (in rows/columns).
Return Value:
--*/
{
LONG i,j;
PSCREEN_INFORMATION ScreenInfo;
NTSTATUS Status;
PWCHAR TextRowPtr;
COLORREF rgbBk;
DBGPRINT(("CreateScreenBuffer(\n"
" OUT PSCREEN_INFORMATION = %lx\n"
" dwWindowSize = (%d,%d)\n"
" nFont = %x\n"
" dwScreenBufferSize = (%d,%d)\n"
" Fill\n"
" PopupFill\n",
ScreenInformation,
dwWindowSize.X, dwWindowSize.Y,
nFont,
dwScreenBufferSize.X, dwScreenBufferSize.Y
// Fill,
// PopupFill
));
DBGPRINT((" PCONSOLE_INFORMATION = %lx\n"
" Flags = %lx\n"
" GraphicsBufferInfo\n"
" lpBitmap\n"
" *hMutex\n"
" ConsoleTitle \"%ls\"\n",
Console,
Flags,
// GraphicsBufferInfo,
// lpBitmap,
// hMutex,
Console->Title));
/*
* CONSIDER (adams): Allocate and zero memory, so
* initialization is only of non-zero members.
*/
ScreenInfo = (PSCREEN_INFORMATION)HeapAlloc(pConHeap,MAKE_TAG( SCREEN_TAG ),sizeof(SCREEN_INFORMATION));
if (ScreenInfo == NULL) {
return STATUS_NO_MEMORY;
}
if ((ScreenInfo->Flags = Flags) & CONSOLE_TEXTMODE_BUFFER) {
SCR_FONTSIZE(ScreenInfo) = FontInfo[nFont].Size;
SCR_FONTNUMBER(ScreenInfo) = nFont;
SCR_FAMILY(ScreenInfo) = FontInfo[nFont].Family;
SCR_FONTWEIGHT(ScreenInfo) = FontInfo[nFont].Weight;
wcscpy(SCR_FACENAME(ScreenInfo), FontInfo[nFont].FaceName);
DBGFONTS(("DoCreateScreenBuffer sets FontSize(%d,%d), FontNumber=%x, Family=%x\n",
SCR_FONTSIZE(ScreenInfo).X,
SCR_FONTSIZE(ScreenInfo).Y,
SCR_FONTNUMBER(ScreenInfo),
SCR_FAMILY(ScreenInfo)));
if (TM_IS_TT_FONT(FontInfo[nFont].Family)) {
ScreenInfo->Flags &= ~CONSOLE_OEMFONT_DISPLAY;
} else {
ScreenInfo->Flags |= CONSOLE_OEMFONT_DISPLAY;
}
UpdateScreenSizes(ScreenInfo, dwScreenBufferSize);
dwScreenBufferSize.X = max(dwScreenBufferSize.X, ScreenInfo->MinX);
dwWindowSize.X = max(dwWindowSize.X, ScreenInfo->MinX);
ScreenInfo->BufferInfo.TextInfo.ModeIndex = (ULONG)-1;
#ifdef i386
if (Console->FullScreenFlags & CONSOLE_FULLSCREEN) {
COORD WindowSize;
ScreenInfo->BufferInfo.TextInfo.WindowedWindowSize = dwWindowSize;
ScreenInfo->BufferInfo.TextInfo.WindowedScreenSize = dwScreenBufferSize;
ScreenInfo->BufferInfo.TextInfo.ModeIndex = MatchWindowSize(dwWindowSize,&WindowSize);
}
#endif
ScreenInfo->BufferInfo.TextInfo.FirstRow = 0;
ScreenInfo->BufferInfo.TextInfo.Rows = (PROW)HeapAlloc(pConHeap,MAKE_TAG( SCREEN_TAG ),dwScreenBufferSize.Y * sizeof(ROW));
if (ScreenInfo->BufferInfo.TextInfo.Rows == NULL) {
HeapFree(pConHeap,0,ScreenInfo);
return STATUS_NO_MEMORY;
}
ScreenInfo->BufferInfo.TextInfo.TextRows = (PWCHAR)HeapAlloc(pConHeap,MAKE_TAG( SCREEN_TAG ),dwScreenBufferSize.X*dwScreenBufferSize.Y*sizeof(WCHAR));
if (ScreenInfo->BufferInfo.TextInfo.TextRows == NULL) {
HeapFree(pConHeap,0,ScreenInfo->BufferInfo.TextInfo.Rows);
HeapFree(pConHeap,0,ScreenInfo);
return STATUS_NO_MEMORY;
}
for (i=0,TextRowPtr=ScreenInfo->BufferInfo.TextInfo.TextRows;
i<dwScreenBufferSize.Y;
i++,TextRowPtr+=dwScreenBufferSize.X) {
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Left = dwScreenBufferSize.X;
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.OldLeft = INVALID_OLD_LENGTH;
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Right = 0;
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.OldRight = INVALID_OLD_LENGTH;
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Chars = TextRowPtr;
for (j=0;j<dwScreenBufferSize.X;j++) {
TextRowPtr[j] = (WCHAR)' ';
}
ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Length = 1;
ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.AttrPair.Length = dwScreenBufferSize.X;
ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.AttrPair.Attr = Fill->Attributes;
ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs = &ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.AttrPair;
}
ScreenInfo->BufferInfo.TextInfo.CursorSize = CursorSize;
ScreenInfo->BufferInfo.TextInfo.CursorPosition.X = 0;
ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y = 0;
ScreenInfo->BufferInfo.TextInfo.CursorVisible = TRUE;
ScreenInfo->BufferInfo.TextInfo.CursorOn = FALSE;
ScreenInfo->BufferInfo.TextInfo.CursorYSize = (WORD)CURSOR_SIZE_IN_PIXELS(SCR_FONTSIZE(ScreenInfo).Y,ScreenInfo->BufferInfo.TextInfo.CursorSize);
ScreenInfo->BufferInfo.TextInfo.UpdatingScreen = 0;
ScreenInfo->BufferInfo.TextInfo.DoubleCursor = FALSE;
ScreenInfo->BufferInfo.TextInfo.DelayCursor = FALSE;
ScreenInfo->BufferInfo.TextInfo.Flags = SINGLE_ATTRIBUTES_PER_LINE;
ScreenInfo->ScreenBufferSize = dwScreenBufferSize;
ScreenInfo->Window.Left = 0;
ScreenInfo->Window.Top = 0;
ScreenInfo->Window.Right = dwWindowSize.X - 1;
ScreenInfo->Window.Bottom = dwWindowSize.Y - 1;
if (ScreenInfo->Window.Right >= ScreenInfo->MaximumWindowSize.X) {
ScreenInfo->Window.Right = ScreenInfo->MaximumWindowSize.X-1;
dwWindowSize.X = CONSOLE_WINDOW_SIZE_X(ScreenInfo);
}
if (ScreenInfo->Window.Bottom >= ScreenInfo->MaximumWindowSize.Y) {
ScreenInfo->Window.Bottom = ScreenInfo->MaximumWindowSize.Y-1;
dwWindowSize.Y = CONSOLE_WINDOW_SIZE_Y(ScreenInfo);
}
ScreenInfo->WindowMaximizedX = (dwWindowSize.X == dwScreenBufferSize.X);
ScreenInfo->WindowMaximizedY = (dwWindowSize.Y == dwScreenBufferSize.Y);
}
else {
Status = CreateConsoleBitmap(GraphicsBufferInfo,
ScreenInfo,
lpBitmap,
hMutex
);
if (!NT_SUCCESS(Status)) {
HeapFree(pConHeap,0,ScreenInfo);
return Status;
}
UpdateScreenSizes(ScreenInfo, ScreenInfo->ScreenBufferSize);
ScreenInfo->WindowMaximizedX = TRUE;
ScreenInfo->WindowMaximizedY = TRUE;
}
ScreenInfo->WindowMaximized = FALSE;
ScreenInfo->Console = Console;
ScreenInfo->RefCount = 0;
ScreenInfo->ShareAccess.OpenCount = 0;
ScreenInfo->ShareAccess.Readers = 0;
ScreenInfo->ShareAccess.Writers = 0;
ScreenInfo->ShareAccess.SharedRead = 0;
ScreenInfo->ShareAccess.SharedWrite = 0;
ScreenInfo->CursorHandle = LoadCursor(NULL, IDC_ARROW);
ScreenInfo->CursorDisplayCount = 0;
ScreenInfo->CommandIdLow = (UINT)-1;
ScreenInfo->CommandIdHigh = (UINT)-1;
ScreenInfo->dwUsage = SYSPAL_STATIC;
ScreenInfo->hPalette = NULL;
ScreenInfo->OutputMode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
ScreenInfo->ResizingWindow = 0;
ScreenInfo->Next = NULL;
ScreenInfo->Attributes = Fill->Attributes;
ScreenInfo->PopupAttributes = PopupFill->Attributes;
rgbBk = ConvertAttrToRGB(Console, LOBYTE(ScreenInfo->Attributes >> 4));
ScreenInfo->hBackground = CreateSolidBrush(rgbBk);
ScreenInfo->WheelDelta = 0;
*ScreenInformation = ScreenInfo;
DBGOUTPUT(("SCREEN at %lx\n", ScreenInfo));
return STATUS_SUCCESS;
}
NTSTATUS
ConsoleSetForegroundWindow(
IN PCONSOLE_INFORMATION Console
)
{
HWND hWnd = Console->hWnd;
HANDLE ConsoleHandle = Console->ConsoleHandle;
UnlockConsole(Console);
SetForegroundWindow(hWnd);
return RevalidateConsole(ConsoleHandle, &Console);
}
NTSTATUS
CreateWindowsWindow(
IN PCONSOLE_INFORMATION Console,
IN HANDLE ClientProcessHandle
)
{
PSCREEN_INFORMATION ScreenInfo;
RECT WindowSize;
DWORD Style;
THREAD_BASIC_INFORMATION ThreadInfo;
HWND hWnd;
ScreenInfo = Console->ScreenBuffers;
//
// figure out how big to make the window, given the desired client area
// size. window is always created in textmode.
//
ASSERT(ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER);
WindowSize.left = 0;
WindowSize.top = 0;
WindowSize.right = CONSOLE_WINDOW_SIZE_X(ScreenInfo)*SCR_FONTSIZE(ScreenInfo).X + VerticalClientToWindow;
WindowSize.bottom = CONSOLE_WINDOW_SIZE_Y(ScreenInfo)*SCR_FONTSIZE(ScreenInfo).Y + HorizontalClientToWindow;
Style = CONSOLE_WINDOW_FLAGS & ~WS_VISIBLE;
if (!ScreenInfo->WindowMaximizedX) {
WindowSize.bottom += HorizontalScrollSize;
} else {
Style &= ~WS_HSCROLL;
}
if (!ScreenInfo->WindowMaximizedY) {
WindowSize.right += VerticalScrollSize;
} else {
Style &= ~WS_VSCROLL;
}
#ifdef THERESES_DEBUG
DbgPrint("creating window with char size %d %d\n",CONSOLE_WINDOW_SIZE_X(ScreenInfo),CONSOLE_WINDOW_SIZE_Y(ScreenInfo));
DbgPrint(" pixel size %d %d\n",WindowSize.right,WindowSize.bottom);
#endif
//
// create the window.
//
Console->WindowRect.left = Console->dwWindowOriginX;
Console->WindowRect.top = Console->dwWindowOriginY;
Console->WindowRect.right = WindowSize.right-WindowSize.left + Console->dwWindowOriginX;
Console->WindowRect.bottom = WindowSize.bottom-WindowSize.top + Console->dwWindowOriginY;
hWnd = CreateWindowEx(CONSOLE_WINDOW_EX_FLAGS,
CONSOLE_WINDOW_CLASS,
Console->Title,
Style,
Console->dwWindowOriginX,
Console->dwWindowOriginY,
WindowSize.right-WindowSize.left,
WindowSize.bottom-WindowSize.top,
NULL,
NULL,
ghInstance,
NULL);
if (hWnd == NULL) {
NtSetEvent(Console->InitEvents[INITIALIZATION_FAILED],NULL);
return STATUS_NO_MEMORY;
}
Console->hWnd = hWnd;
SetWindowLong(Console->hWnd, GWL_USERDATA, (LONG)Console);
//
// Stuff the client id into the window so USER can find it.
//
if (NT_SUCCESS(NtQueryInformationThread(Console->ClientThreadHandle,
ThreadBasicInformation, &ThreadInfo,
sizeof(ThreadInfo), NULL))) {
SetConsolePid(Console->hWnd, (LONG)ThreadInfo.ClientId.UniqueProcess);
SetConsoleTid(Console->hWnd, (LONG)ThreadInfo.ClientId.UniqueThread);
}
//
// Get the dc.
//
Console->hDC = GetDC(Console->hWnd);
if (Console->hDC == NULL) {
NtSetEvent(Console->InitEvents[INITIALIZATION_FAILED],NULL);
DestroyWindow(Console->hWnd);
Console->hWnd = NULL;
return STATUS_NO_MEMORY;
}
Console->hMenu = GetSystemMenu(Console->hWnd,FALSE);
//
// modify system menu to our liking.
//
InitSystemMenu(Console);
ScreenInfo->CursorHandle = ghNormalCursor;
gnConsoleWindows++;
Console->InputThreadInfo->WindowCount++;
//
// Set up the hot key for this window
//
if ((Console->dwHotKey != 0) && !(Console->Flags & CONSOLE_NO_WINDOW)) {
SendMessage(Console->hWnd, WM_SETHOTKEY, Console->dwHotKey, 0L);
}
//
// create icon
//
if (Console->iIconId) {
// We have no icon, try and get one from progman.
PostMessage(HWND_BROADCAST,
ProgmanHandleMessage,
(DWORD)Console->hWnd,
1);
}
if (Console->hIcon == NULL) {
Console->hIcon = ghDefaultIcon;
} else if (Console->hIcon != ghDefaultIcon) {
SendMessage(Console->hWnd, WM_SETICON, ICON_BIG, (LONG)Console->hIcon);
}
SetBkMode(Console->hDC,OPAQUE);
SetFont(ScreenInfo);
SetScreenColors(ScreenInfo, ScreenInfo->Attributes,
ScreenInfo->PopupAttributes, FALSE);
if (Console->Flags & CONSOLE_NO_WINDOW) {
ShowWindow(Console->hWnd, SW_HIDE);
#ifdef i386
} else if (Console->FullScreenFlags != 0) {
if (Console->wShowWindow == SW_SHOWMINNOACTIVE) {
ShowWindow(Console->hWnd, Console->wShowWindow);
Console->FullScreenFlags = 0;
Console->Flags |= CONSOLE_IS_ICONIC;
} else {
ConvertToFullScreen(Console);
if (!NT_SUCCESS(ConsoleSetForegroundWindow(Console))) {
return STATUS_INVALID_HANDLE;
}
ChangeDispSettings(Console, Console->hWnd,CDS_FULLSCREEN);
}
#endif
} else {
if (Console->wShowWindow != SW_SHOWNOACTIVATE &&
Console->wShowWindow != SW_SHOWMINNOACTIVE &&
Console->wShowWindow != SW_HIDE) {
if (!NT_SUCCESS(ConsoleSetForegroundWindow(Console))) {
return STATUS_INVALID_HANDLE;
}
} else if (Console->wShowWindow == SW_SHOWMINNOACTIVE) {
Console->Flags |= CONSOLE_IS_ICONIC;
}
ShowWindow(Console->hWnd, Console->wShowWindow);
}
//UpdateWindow(Console->hWnd);
InternalUpdateScrollBars(ScreenInfo);
if (!(Console->Flags & CONSOLE_IS_ICONIC) &&
(Console->FullScreenFlags == 0) ) {
GetWindowRect(Console->hWnd,&Console->WindowRect);
}
//
// If this is an autoposition window, make sure it doesn't descend
// below the tray
//
if (Console->Flags & CONSOLE_AUTO_POSITION) {
LONG x = Console->WindowRect.left;
LONG y = Console->WindowRect.top;
if (Console->WindowRect.right > ConsoleWorkArea.right) {
x -= Console->WindowRect.right - ConsoleWorkArea.right;
x = max(x, ConsoleWorkArea.left);
}
if (Console->WindowRect.bottom > ConsoleWorkArea.bottom) {
y -= Console->WindowRect.bottom - ConsoleWorkArea.bottom;
y = max(y, ConsoleWorkArea.left);
}
if (x != Console->WindowRect.left || y != Console->WindowRect.top) {
SetWindowPos(Console->hWnd, NULL, x, y, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
}
NtSetEvent(Console->InitEvents[INITIALIZATION_SUCCEEDED],NULL);
return STATUS_SUCCESS;
}
NTSTATUS
FreeScreenBuffer(
IN PSCREEN_INFORMATION ScreenInfo
)
/*++
Routine Description:
This routine frees the memory associated with a screen buffer.
Arguments:
ScreenInfo - screen buffer data to free.
Return Value:
Note: console handle table lock must be held when calling this routine
--*/
{
SHORT i;
PCONSOLE_INFORMATION Console = ScreenInfo->Console;
//
// If the DC is still around, make sure that the background brush
// is not selected.
//
if (Console->hDC != NULL && ScreenInfo->hBackground != NULL) {
if (GetCurrentObject(Console->hDC, OBJ_BRUSH) == ScreenInfo->hBackground) {
SelectObject(Console->hDC, GetStockObject(BLACK_BRUSH));
}
DeleteObject(ScreenInfo->hBackground);
}
ASSERT(ScreenInfo->RefCount == 0);
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
for (i=0;i<ScreenInfo->ScreenBufferSize.Y;i++) {
if (ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Length > 1) {
HeapFree(pConHeap,0,ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs);
}
}
HeapFree(pConHeap,0,ScreenInfo->BufferInfo.TextInfo.TextRows);
HeapFree(pConHeap,0,ScreenInfo->BufferInfo.TextInfo.Rows);
} else {
if (ScreenInfo->hPalette != NULL) {
if (GetCurrentObject(Console->hDC, OBJ_PAL) == ScreenInfo->hPalette) {
SelectPalette(Console->hDC, Console->hSysPalette, FALSE);
}
DeleteObject(ScreenInfo->hPalette);
}
FreeConsoleBitmap(ScreenInfo);
}
HeapFree(pConHeap,0,ScreenInfo);
return STATUS_SUCCESS;
}
VOID
FindAttrIndex(
IN PATTR_PAIR String,
IN SHORT Index,
OUT PATTR_PAIR *IndexedAttr,
OUT PSHORT CountOfAttr
)
/*++
Routine Description:
This routine finds the nth attribute in a string.
Arguments:
String - attribute string
Index - which attribute to find
IndexedAttr - pointer to attribute within string
CountOfAttr - on output, contains corrected length of indexed attr.
for example, if the attribute string was { 5, BLUE } and the requested
index was 3, CountOfAttr would be 2.
Return Value:
none.
--*/
{
SHORT i;
for (i=0;i<Index;) {
i += String->Length;
String++;
}
if (i>Index) {
String--;
*CountOfAttr = i-Index;
}
else {
*CountOfAttr = String->Length;
}
*IndexedAttr = String;
}
NTSTATUS
MergeAttrStrings(
IN PATTR_PAIR Source,
IN WORD SourceLength,
IN PATTR_PAIR Merge,
IN WORD MergeLength,
OUT PATTR_PAIR *Target,
OUT LPWORD TargetLength,
IN SHORT StartIndex,
IN SHORT EndIndex,
IN PROW Row,
IN PSCREEN_INFORMATION ScreenInfo
)
/*++
Routine Description:
This routine merges two run-length encoded attribute strings into
a third.
for example, if the source string was { 4, BLUE }, the merge string
was { 2, RED }, and the StartIndex and EndIndex were 1 and 2,
respectively, the target string would be { 1, BLUE, 2, RED, 1, BLUE }
and the target length would be 3.
Arguments:
Source - pointer to source attribute string
SourceLength - length of source. for example, the length of
{ 4, BLUE } is 1.
Merge - pointer to attribute string to insert into source
MergeLength - length of merge
Target - where to store pointer to resulting attribute string
TargetLength - where to store length of resulting attribute string
StartIndex - index into Source at which to insert Merge String.
EndIndex - index into Source at which to stop insertion of Merge String
Return Value:
none.
--*/
{
PATTR_PAIR SrcAttr,TargetAttr,SrcEnd;
PATTR_PAIR NewString;
SHORT i;
#if THERESES_DEBUG2
#if DBG
WORD AllocLength;
AllocLength = MergeLength + SourceLength + 1;
#endif
#endif
//
// if just changing the attr for the whole row
//
if (MergeLength == 1 && Row->AttrRow.Length == 1) {
if (Row->AttrRow.Attrs->Attr == Merge->Attr) {
*TargetLength = 1;
*Target = &Row->AttrRow.AttrPair;
return STATUS_SUCCESS;
}
if (StartIndex == 0 && EndIndex == (SHORT)(ScreenInfo->ScreenBufferSize.X-1)) {
NewString = &Row->AttrRow.AttrPair;
NewString->Attr = Merge->Attr;
*TargetLength = 1;
*Target = NewString;
return STATUS_SUCCESS;
}
}
NewString = (PATTR_PAIR) HeapAlloc(pConHeap,MAKE_TAG( SCREEN_TAG ),(SourceLength+MergeLength+1)*sizeof(ATTR_PAIR));
if (NewString == NULL) {
return STATUS_NO_MEMORY;
}
//
// copy the source string, up to the start index.
//
SrcAttr = Source;
SrcEnd = Source + SourceLength;
TargetAttr = NewString;
i=0;
if (StartIndex != 0) {
while (i<StartIndex) {
i += SrcAttr->Length;
*TargetAttr++ = *SrcAttr++;
}
//
// back up to the last pair copied, in case the attribute in the first
// pair in the merge string matches. also, adjust TargetAttr->Length
// based on i, the attribute
// counter, back to the StartIndex. i will be larger than the
// StartIndex in the case where the last attribute pair copied had
// a length greater than the number needed to reach StartIndex.
//
TargetAttr--;
if (i>StartIndex) {
TargetAttr->Length -= i-StartIndex;
}
if (Merge->Attr == TargetAttr->Attr) {
TargetAttr->Length += Merge->Length;
MergeLength-=1;
Merge++;
}
TargetAttr++;
}
//
// copy the merge string.
//
RtlCopyMemory(TargetAttr,Merge,MergeLength*sizeof(ATTR_PAIR));
TargetAttr += MergeLength;
//
// figure out where to resume copying the source string.
//
while (i<=EndIndex) {
ASSERT(SrcAttr != SrcEnd);
i += SrcAttr->Length;
SrcAttr++;
}
//
// if not done, copy the rest of the source
//
if (SrcAttr != SrcEnd || i!=(SHORT)(EndIndex+1)) {
//
// see if we've gone past the right attribute. if so, back up and
// copy the attribute and the correct length.
//
TargetAttr--;
if (i>(SHORT)(EndIndex+1)) {
SrcAttr--;
if (TargetAttr->Attr == SrcAttr->Attr) {
TargetAttr->Length += i-(EndIndex+1);
} else {
TargetAttr++;
TargetAttr->Attr = SrcAttr->Attr;
TargetAttr->Length = (SHORT)(i-(EndIndex+1));
}
SrcAttr++;
}
//
// see if we can merge the source and target.
//
else if (TargetAttr->Attr == SrcAttr->Attr) {
TargetAttr->Length += SrcAttr->Length;
i += SrcAttr->Length;
SrcAttr++;
}
TargetAttr++;
//
// copy the rest of the source
//
if (SrcAttr < SrcEnd) {
RtlCopyMemory(TargetAttr,SrcAttr,(SrcEnd-SrcAttr)*sizeof(ATTR_PAIR));
TargetAttr += SrcEnd - SrcAttr;
}
}
*TargetLength = (WORD)(TargetAttr - NewString);
#if THERESES_DEBUG2
#if DBG
{ SHORT j;
WORD i;
PATTR_PAIR NewAttr;
PULONG Foo;
j=0;
NewAttr = NewString;
for (i=0;i<*TargetLength;i++) {
j+=NewAttr->Length;
NewAttr++;
}
ASSERT (j == ScreenInfo->ScreenBufferSize.X);
if (j != ScreenInfo->ScreenBufferSize.X) {
DbgPrint("new length is %d\n",*TargetLength);
DbgPrint("address of new attr string is %lx\n",NewString);
}
ASSERT (*TargetLength <= AllocLength);
Foo = (PULONG)(NewString-4);
ASSERT (*Foo & 1);
Foo = (PULONG)(NewString-3);
ASSERT (*Foo >= (*TargetLength * sizeof(ATTR_PAIR)) + 16);
}
#endif
#endif
*Target = NewString;
if (*TargetLength == 1) {
*Target = &Row->AttrRow.AttrPair;
**Target = *NewString;
HeapFree(pConHeap,0,NewString);
}
return STATUS_SUCCESS;
}
VOID
ResetTextFlags(
IN PSCREEN_INFORMATION ScreenInfo,
IN SHORT StartY,
IN SHORT EndY
)
/*
this routine updates the text flags
#define SINGLE_ATTRIBUTES_PER_LINE 2 // only one attribute per line
*/
{
SHORT RowIndex;
PROW Row;
SHORT i;
//
// first see whether we wrote any lines with multiple attributes. if
// we did, set the flags and bail out. also, remember if any of the
// lines we wrote had attributes different from other lines.
//
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+StartY) % ScreenInfo->ScreenBufferSize.Y;
for (i=StartY;i<=EndY;i++) {
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
if (Row->AttrRow.Length != 1) {
ScreenInfo->BufferInfo.TextInfo.Flags &= ~SINGLE_ATTRIBUTES_PER_LINE;
return;
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
}
// all of the written lines have the same attribute.
if (ScreenInfo->BufferInfo.TextInfo.Flags & SINGLE_ATTRIBUTES_PER_LINE) {
return;
}
if (StartY == 0 && EndY == (ScreenInfo->ScreenBufferSize.Y-1)) {
ScreenInfo->BufferInfo.TextInfo.Flags |= SINGLE_ATTRIBUTES_PER_LINE;
return;
}
RowIndex = ScreenInfo->BufferInfo.TextInfo.FirstRow;
for (i=0;i<StartY;i++) {
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
if (Row->AttrRow.Length != 1) {
return;
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
}
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+EndY+1) % ScreenInfo->ScreenBufferSize.Y;
for (i=EndY+1;i<ScreenInfo->ScreenBufferSize.Y;i++) {
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
if (Row->AttrRow.Length != 1) {
return;
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
}
ScreenInfo->BufferInfo.TextInfo.Flags |= SINGLE_ATTRIBUTES_PER_LINE;
}
VOID
StreamWriteToScreenBuffer(
IN PWCHAR String,
IN SHORT StringLength,
IN PSCREEN_INFORMATION ScreenInfo
)
{
SHORT RowIndex;
PROW Row;
PWCHAR Char;
COORD TargetPoint;
DBGOUTPUT(("StreamWriteToScreenBuffer\n"));
ScreenInfo->BufferInfo.TextInfo.Flags |= TEXT_VALID_HINT;
TargetPoint = ScreenInfo->BufferInfo.TextInfo.CursorPosition;
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+TargetPoint.Y) % ScreenInfo->ScreenBufferSize.Y;
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
DBGOUTPUT(("RowIndex = %lx, Row = %lx, TargetPoint = (%d,%d)\n",
RowIndex, Row, TargetPoint.X, TargetPoint.Y));
//
// copy chars
//
RtlCopyMemory(&Row->CharRow.Chars[TargetPoint.X],String,StringLength*sizeof(WCHAR));
// recalculate first and last non-space char
Row->CharRow.OldLeft = Row->CharRow.Left;
if (TargetPoint.X < Row->CharRow.Left) {
PWCHAR LastChar = &Row->CharRow.Chars[ScreenInfo->ScreenBufferSize.X];
for (Char=&Row->CharRow.Chars[TargetPoint.X];Char < LastChar && *Char==(WCHAR)' ';Char++)
;
Row->CharRow.Left = Char-Row->CharRow.Chars;
}
Row->CharRow.OldRight = Row->CharRow.Right;
if ((TargetPoint.X+StringLength) >= Row->CharRow.Right) {
PWCHAR FirstChar = Row->CharRow.Chars;
for (Char=&Row->CharRow.Chars[TargetPoint.X+StringLength-1];*Char==(WCHAR)' ' && Char >= FirstChar;Char--)
;
Row->CharRow.Right = (SHORT)(Char+1-FirstChar);
}
//
// see if attr string is different. if so, allocate a new
// attr buffer and merge the two strings.
//
if (Row->AttrRow.Length != 1 ||
Row->AttrRow.Attrs->Attr != ScreenInfo->Attributes) {
PATTR_PAIR NewAttrs;
WORD NewAttrsLength;
ATTR_PAIR Attrs;
Attrs.Length = StringLength;
Attrs.Attr = ScreenInfo->Attributes;
if (!NT_SUCCESS(MergeAttrStrings(Row->AttrRow.Attrs,
Row->AttrRow.Length,
&Attrs,
1,
&NewAttrs,
&NewAttrsLength,
TargetPoint.X,
(SHORT)(TargetPoint.X+StringLength-1),
Row,
ScreenInfo
))) {
return;
}
if (Row->AttrRow.Length > 1) {
HeapFree(pConHeap,0,Row->AttrRow.Attrs);
}
else {
ASSERT(Row->AttrRow.Attrs == &Row->AttrRow.AttrPair);
}
Row->AttrRow.Attrs = NewAttrs;
Row->AttrRow.Length = NewAttrsLength;
Row->CharRow.OldLeft = INVALID_OLD_LENGTH;
Row->CharRow.OldRight = INVALID_OLD_LENGTH;
}
ResetTextFlags(ScreenInfo,TargetPoint.Y,TargetPoint.Y);
}
#define CHAR_OF_PCI(p) (((PCHAR_INFO)(p))->Char.AsciiChar)
#define WCHAR_OF_PCI(p) (((PCHAR_INFO)(p))->Char.UnicodeChar)
#define ATTR_OF_PCI(p) (((PCHAR_INFO)(p))->Attributes)
#define SIZEOF_CI_CELL sizeof(CHAR_INFO)
#define CHAR_OF_VGA(p) (p[0])
#define ATTR_OF_VGA(p) (p[1])
#ifdef i386
#define SIZEOF_VGA_CELL 2
#else // risc
#define SIZEOF_VGA_CELL 4
#endif
VOID
WriteRectToScreenBuffer(
PBYTE Source,
COORD SourceSize,
PSMALL_RECT SourceRect,
PSCREEN_INFORMATION ScreenInfo,
COORD TargetPoint,
IN UINT Codepage
)
/*++
Routine Description:
This routine copies a rectangular region to the screen buffer.
no clipping is done.
The source should contain Unicode or UnicodeOem chars.
Arguments:
Source - pointer to source buffer (a real VGA buffer or CHAR_INFO[])
SourceSize - dimensions of source buffer
SourceRect - rectangle in source buffer to copy
ScreenInfo - pointer to screen info
TargetPoint - upper left coordinates of target rectangle
Codepage - codepage to translate real VGA buffer from,
0xFFFFFFF if Source is CHAR_INFO[] (not requiring translation)
Return Value:
none.
--*/
{
PBYTE SourcePtr;
SHORT i,j;
SHORT XSize,YSize;
BOOLEAN WholeSource;
SHORT RowIndex;
PROW Row;
PWCHAR Char;
ATTR_PAIR Attrs[80];
PATTR_PAIR AttrBuf;
PATTR_PAIR Attr;
SHORT AttrLength;
BOOL bVGABuffer;
ULONG ulCellSize;
DBGOUTPUT(("WriteRectToScreenBuffer\n"));
ScreenInfo->BufferInfo.TextInfo.Flags |= TEXT_VALID_HINT;
XSize = (SHORT)(SourceRect->Right - SourceRect->Left + 1);
YSize = (SHORT)(SourceRect->Bottom - SourceRect->Top + 1);
AttrBuf = Attrs;
if (XSize > 80) {
AttrBuf = (PATTR_PAIR)HeapAlloc(pConHeap,MAKE_TAG( TMP_TAG ),XSize * sizeof(ATTR_PAIR));
if (AttrBuf == NULL)
return;
}
bVGABuffer = (Codepage != 0xFFFFFFFF);
if (bVGABuffer) {
ulCellSize = SIZEOF_VGA_CELL;
} else {
ulCellSize = SIZEOF_CI_CELL;
}
SourcePtr = Source;
WholeSource = FALSE;
if (XSize == SourceSize.X) {
ASSERT (SourceRect->Left == 0);
if (SourceRect->Top != 0) {
SourcePtr += SCREEN_BUFFER_POINTER(SourceRect->Left,
SourceRect->Top,
SourceSize.X,
ulCellSize);
}
WholeSource = TRUE;
}
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+TargetPoint.Y) % ScreenInfo->ScreenBufferSize.Y;
for (i=0;i<YSize;i++) {
if (!WholeSource) {
SourcePtr = Source + SCREEN_BUFFER_POINTER(SourceRect->Left,
SourceRect->Top+i,
SourceSize.X,
ulCellSize);
}
//
// copy the chars and attrs into their respective arrays
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
Char = &Row->CharRow.Chars[TargetPoint.X];
Attr = AttrBuf;
Attr->Length = 0;
AttrLength = 1;
/*
* Two version of the following loop to keep it fast:
* one for VGA buffers, one for CHAR_INFO buffers.
*/
if (bVGABuffer) {
Attr->Attr = ATTR_OF_VGA(SourcePtr);
for (j = SourceRect->Left;
j <= SourceRect->Right;
j++, SourcePtr += SIZEOF_VGA_CELL) {
*Char++ = CharToWcharGlyph(Codepage, CHAR_OF_VGA(SourcePtr));
if (Attr->Attr == ATTR_OF_VGA(SourcePtr)) {
Attr->Length += 1;
}
else {
Attr++;
Attr->Length = 1;
Attr->Attr = ATTR_OF_VGA(SourcePtr);
AttrLength += 1;
}
}
} else {
Attr->Attr = ATTR_OF_PCI(SourcePtr);
for (j = SourceRect->Left;
j <= SourceRect->Right;
j++, SourcePtr += SIZEOF_CI_CELL) {
*Char++ = WCHAR_OF_PCI(SourcePtr);
if (Attr->Attr == ATTR_OF_PCI(SourcePtr)) {
Attr->Length += 1;
}
else {
Attr++;
Attr->Length = 1;
Attr->Attr = ATTR_OF_PCI(SourcePtr);
AttrLength += 1;
}
}
}
// recalculate first and last non-space char
Row->CharRow.OldLeft = Row->CharRow.Left;
if (TargetPoint.X < Row->CharRow.Left) {
PWCHAR LastChar = &Row->CharRow.Chars[ScreenInfo->ScreenBufferSize.X];
for (Char=&Row->CharRow.Chars[TargetPoint.X];Char < LastChar && *Char==(WCHAR)' ';Char++)
;
Row->CharRow.Left = Char-Row->CharRow.Chars;
}
Row->CharRow.OldRight = Row->CharRow.Right;
if ((TargetPoint.X+XSize) >= Row->CharRow.Right) {
SHORT LastNonSpace;
PWCHAR FirstChar = Row->CharRow.Chars;
LastNonSpace = (SHORT)(TargetPoint.X+XSize-1);
for (Char=&Row->CharRow.Chars[(TargetPoint.X+XSize-1)];*Char==(WCHAR)' ' && Char >= FirstChar;Char--)
LastNonSpace--;
//
// if the attributes change after the last non-space, make the
// index of the last attribute change + 1 the length. otherwise
// make the length one more than the last non-space.
//
Row->CharRow.Right = (SHORT)(LastNonSpace+1);
}
//
// see if attr string is different. if so, allocate a new
// attr buffer and merge the two strings.
//
if (AttrLength != Row->AttrRow.Length ||
memcmp(Row->AttrRow.Attrs,AttrBuf,AttrLength*sizeof(*Attr))) {
PATTR_PAIR NewAttrs;
WORD NewAttrsLength;
if (!NT_SUCCESS(MergeAttrStrings(Row->AttrRow.Attrs,
Row->AttrRow.Length,
AttrBuf,
AttrLength,
&NewAttrs,
&NewAttrsLength,
TargetPoint.X,
(SHORT)(TargetPoint.X+XSize-1),
Row,
ScreenInfo
))) {
if (XSize > 80) {
HeapFree(pConHeap,0,AttrBuf);
}
ResetTextFlags(ScreenInfo,TargetPoint.Y,(SHORT)(TargetPoint.Y+YSize-1));
return;
}
if (Row->AttrRow.Length > 1) {
HeapFree(pConHeap,0,Row->AttrRow.Attrs);
}
else {
ASSERT(Row->AttrRow.Attrs == &Row->AttrRow.AttrPair);
}
Row->AttrRow.Attrs = NewAttrs;
Row->AttrRow.Length = NewAttrsLength;
Row->CharRow.OldLeft = INVALID_OLD_LENGTH;
Row->CharRow.OldRight = INVALID_OLD_LENGTH;
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
}
ResetTextFlags(ScreenInfo,TargetPoint.Y,(SHORT)(TargetPoint.Y+YSize-1));
if (XSize > 80) {
HeapFree(pConHeap,0,AttrBuf);
}
}
VOID
ReadRectFromScreenBuffer(
IN PSCREEN_INFORMATION ScreenInfo,
IN COORD SourcePoint,
IN PCHAR_INFO Target,
IN COORD TargetSize,
IN PSMALL_RECT TargetRect
)
/*++
Routine Description:
This routine copies a rectangular region from the screen buffer.
no clipping is done.
Arguments:
ScreenInfo - pointer to screen info
SourcePoint - upper left coordinates of source rectangle
Target - pointer to target buffer
TargetSize - dimensions of target buffer
TargetRect - rectangle in source buffer to copy
Return Value:
none.
--*/
{
PCHAR_INFO TargetPtr;
SHORT i,j,k;
SHORT XSize,YSize;
BOOLEAN WholeTarget;
SHORT RowIndex;
PROW Row;
PWCHAR Char;
PATTR_PAIR Attr;
SHORT CountOfAttr;
DBGOUTPUT(("ReadRectFromScreenBuffer\n"));
XSize = (SHORT)(TargetRect->Right - TargetRect->Left + 1);
YSize = (SHORT)(TargetRect->Bottom - TargetRect->Top + 1);
TargetPtr = Target;
WholeTarget = FALSE;
if (XSize == TargetSize.X) {
ASSERT (TargetRect->Left == 0);
if (TargetRect->Top != 0) {
TargetPtr = (PCHAR_INFO)
((ULONG)Target + SCREEN_BUFFER_POINTER(TargetRect->Left,
TargetRect->Top,
TargetSize.X,
sizeof(CHAR_INFO)));
}
WholeTarget = TRUE;
}
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+SourcePoint.Y) % ScreenInfo->ScreenBufferSize.Y;
for (i=0;i<YSize;i++) {
if (!WholeTarget) {
TargetPtr = (PCHAR_INFO)
((ULONG)Target + SCREEN_BUFFER_POINTER(TargetRect->Left,
TargetRect->Top+i,
TargetSize.X,
sizeof(CHAR_INFO)));
}
//
// copy the chars and attrs from their respective arrays
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
Char = &Row->CharRow.Chars[SourcePoint.X];
FindAttrIndex(Row->AttrRow.Attrs,
SourcePoint.X,
&Attr,
&CountOfAttr
);
k=0;
for (j=0;j<XSize;TargetPtr++) {
TargetPtr->Char.UnicodeChar = *Char++;
TargetPtr->Attributes = Attr->Attr;
j+=1;
if (++k==CountOfAttr && j<XSize) {
Attr++;
k=0;
CountOfAttr = Attr->Length;
}
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
}
}
VOID
CopyRectangle(
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT SourceRect,
IN COORD TargetPoint
)
/*++
Routine Description:
This routine copies a rectangular region from the screen buffer to
the screen buffer. no clipping is done.
Arguments:
ScreenInfo - pointer to screen info
SourceRect - rectangle in source buffer to copy
TargetPoint - upper left coordinates of new location rectangle
Return Value:
none.
--*/
{
SMALL_RECT Target;
COORD SourcePoint;
COORD Size;
DBGOUTPUT(("CopyRectangle\n"));
LockScrollBuffer();
SourcePoint.X = SourceRect->Left;
SourcePoint.Y = SourceRect->Top;
Target.Left = 0;
Target.Top = 0;
Target.Right = Size.X = SourceRect->Right - SourceRect->Left;
Target.Bottom = Size.Y = SourceRect->Bottom - SourceRect->Top;
Size.X++;
Size.Y++;
if (ScrollBufferSize < (Size.X * Size.Y * sizeof(CHAR_INFO))) {
FreeScrollBuffer();
if (!NT_SUCCESS(AllocateScrollBuffer(Size.X * Size.Y * sizeof(CHAR_INFO)))) {
UnlockScrollBuffer();
return;
}
}
ReadRectFromScreenBuffer(ScreenInfo,
SourcePoint,
ScrollBuffer,
Size,
&Target
);
WriteRectToScreenBuffer((PBYTE)ScrollBuffer,
Size,
&Target,
ScreenInfo,
TargetPoint,
0xFFFFFFFF // ScrollBuffer won't need conversion
);
UnlockScrollBuffer();
}
NTSTATUS
ReadScreenBuffer(
IN PSCREEN_INFORMATION ScreenInformation,
OUT PCHAR_INFO Buffer,
IN OUT PSMALL_RECT ReadRegion
)
/*++
Routine Description:
This routine reads a rectangular region from the screen buffer.
The region is first clipped.
Arguments:
ScreenInformation - Screen buffer to read from.
Buffer - Buffer to read into.
ReadRegion - Region to read.
Return Value:
--*/
{
COORD TargetSize;
COORD TargetPoint,SourcePoint;
SMALL_RECT Target;
DBGOUTPUT(("ReadScreenBuffer\n"));
//
// calculate dimensions of caller's buffer. have to do this calculation
// before clipping.
//
TargetSize.X = (SHORT)(ReadRegion->Right - ReadRegion->Left + 1);
TargetSize.Y = (SHORT)(ReadRegion->Bottom - ReadRegion->Top + 1);
if (TargetSize.X <= 0 || TargetSize.Y <= 0) {
return STATUS_SUCCESS;
}
// do clipping.
if (ReadRegion->Right > (SHORT)(ScreenInformation->ScreenBufferSize.X-1)) {
ReadRegion->Right = (SHORT)(ScreenInformation->ScreenBufferSize.X-1);
}
if (ReadRegion->Bottom > (SHORT)(ScreenInformation->ScreenBufferSize.Y-1)) {
ReadRegion->Bottom = (SHORT)(ScreenInformation->ScreenBufferSize.Y-1);
}
if (ReadRegion->Left < 0) {
TargetPoint.X = -ReadRegion->Left;
ReadRegion->Left = 0;
}
else {
TargetPoint.X = 0;
}
if (ReadRegion->Top < 0) {
TargetPoint.Y = -ReadRegion->Top;
ReadRegion->Top = 0;
}
else {
TargetPoint.Y = 0;
}
SourcePoint.X = ReadRegion->Left;
SourcePoint.Y = ReadRegion->Top;
Target.Left = TargetPoint.X;
Target.Top = TargetPoint.Y;
Target.Right = TargetPoint.X + (ReadRegion->Right - ReadRegion->Left);
Target.Bottom = TargetPoint.Y + (ReadRegion->Bottom - ReadRegion->Top);
ReadRectFromScreenBuffer(ScreenInformation,
SourcePoint,
Buffer,
TargetSize,
&Target
);
return STATUS_SUCCESS;
}
NTSTATUS
WriteScreenBuffer(
IN PSCREEN_INFORMATION ScreenInformation,
IN PCHAR_INFO Buffer,
IN OUT PSMALL_RECT WriteRegion
)
/*++
Routine Description:
This routine write a rectangular region to the screen buffer.
The region is first clipped.
The region should contain Unicode or UnicodeOem chars.
Arguments:
ScreenInformation - Screen buffer to write to.
Buffer - Buffer to write from.
ReadRegion - Region to write.
Return Value:
--*/
{
COORD SourceSize;
COORD TargetPoint;
SMALL_RECT SourceRect;
DBGOUTPUT(("WriteScreenBuffer\n"));
//
// calculate dimensions of caller's buffer. have to do this calculation
// before clipping.
//
SourceSize.X = (SHORT)(WriteRegion->Right - WriteRegion->Left + 1);
SourceSize.Y = (SHORT)(WriteRegion->Bottom - WriteRegion->Top + 1);
if (SourceSize.X <= 0 || SourceSize.Y <= 0) {
return STATUS_SUCCESS;
}
// do clipping.
if (WriteRegion->Left >= ScreenInformation->ScreenBufferSize.X ||
WriteRegion->Top >= ScreenInformation->ScreenBufferSize.Y) {
return STATUS_SUCCESS;
}
if (WriteRegion->Right > (SHORT)(ScreenInformation->ScreenBufferSize.X-1))
WriteRegion->Right = (SHORT)(ScreenInformation->ScreenBufferSize.X-1);
SourceRect.Right = WriteRegion->Right - WriteRegion->Left;
if (WriteRegion->Bottom > (SHORT)(ScreenInformation->ScreenBufferSize.Y-1))
WriteRegion->Bottom = (SHORT)(ScreenInformation->ScreenBufferSize.Y-1);
SourceRect.Bottom = WriteRegion->Bottom - WriteRegion->Top;
if (WriteRegion->Left < 0) {
SourceRect.Left = -WriteRegion->Left;
WriteRegion->Left = 0;
}
else {
SourceRect.Left = 0;
}
if (WriteRegion->Top < 0) {
SourceRect.Top = -WriteRegion->Top;
WriteRegion->Top = 0;
}
else {
SourceRect.Top = 0;
}
TargetPoint.X = WriteRegion->Left;
TargetPoint.Y = WriteRegion->Top;
WriteRectToScreenBuffer((PBYTE)Buffer,
SourceSize,
&SourceRect,
ScreenInformation,
TargetPoint,
0xFFFFFFFF
);
return STATUS_SUCCESS;
}
VOID
WriteRegionToScreen(
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT Region
)
{
COORD Window;
int i,j;
PATTR_PAIR Attr;
RECT TextRect;
SHORT RowIndex;
SHORT CountOfAttr;
PROW Row;
BOOL OneLine, SimpleWrite; // one line && one attribute per line
PCONSOLE_INFORMATION Console = ScreenInfo->Console;
DBGOUTPUT(("WriteRegionToScreen\n"));
if (Console->FullScreenFlags == 0) {
//
// if we have a selection, turn it off.
//
InvertSelection(Console, TRUE);
ASSERT(ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER);
if (PolyTextOutCandidate(ScreenInfo,Region)) {
ConsolePolyTextOut(ScreenInfo,Region);
} else {
try { // capture TextOut exceptions for low memory
Window.Y = Region->Top - ScreenInfo->Window.Top;
Window.X = Region->Left - ScreenInfo->Window.Left;
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+Region->Top) % ScreenInfo->ScreenBufferSize.Y;
OneLine = (Region->Top==Region->Bottom);
for (i=Region->Top;i<=Region->Bottom;i++,Window.Y++) {
//
// copy the chars and attrs from their respective arrays
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
if (Row->AttrRow.Length == 1) {
Attr = Row->AttrRow.Attrs;
CountOfAttr = ScreenInfo->ScreenBufferSize.X;
SimpleWrite = TRUE;
} else {
SimpleWrite = FALSE;
FindAttrIndex(Row->AttrRow.Attrs,
Region->Left,
&Attr,
&CountOfAttr
);
}
if (Console->LastAttributes != Attr->Attr) {
TEXTCOLOR_CALL;
SetTextColor(Console->hDC, ConvertAttrToRGB(Console, LOBYTE(Attr->Attr)));
SetBkColor(Console->hDC, ConvertAttrToRGB(Console, LOBYTE(Attr->Attr >> 4)));
Console->LastAttributes = Attr->Attr;
}
TextRect.top = Window.Y*SCR_FONTSIZE(ScreenInfo).Y;
TextRect.bottom = TextRect.top + SCR_FONTSIZE(ScreenInfo).Y;
for (j=Region->Left;j<=Region->Right;) {
SHORT NumberOfChars;
int TextLeft;
SHORT LeftChar,RightChar;
if (CountOfAttr > (SHORT)(Region->Right - j + 1)) {
CountOfAttr = (SHORT)(Region->Right - j + 1);
}
//
// make the bounding rect smaller, if we can. the TEXT_VALID_HINT
// flag gets set each time we write to the screen buffer. it gets
// turned off any time we get asked to redraw the screen
// and we don't know exactly what needs to be redrawn
// (i.e. paint messages).
//
// we have the left and right bounds of the text on the
// line. the opaqueing rectangle and the number of
// chars get set according to those values.
//
// if there's more than one attr per line (!SimpleWrite)
// we bail on the opaqueing rect.
//
if (ScreenInfo->BufferInfo.TextInfo.Flags & TEXT_VALID_HINT && SimpleWrite) {
if (Row->CharRow.OldLeft != INVALID_OLD_LENGTH) {
TextRect.left = (max(min(Row->CharRow.Left,Row->CharRow.OldLeft),j)-ScreenInfo->Window.Left) *
SCR_FONTSIZE(ScreenInfo).X;
} else {
TextRect.left = Window.X*SCR_FONTSIZE(ScreenInfo).X;
}
if (Row->CharRow.OldRight != INVALID_OLD_LENGTH) {
TextRect.right = (min(max(Row->CharRow.Right,Row->CharRow.OldRight),j+CountOfAttr)-ScreenInfo->Window.Left) *
SCR_FONTSIZE(ScreenInfo).X;
} else {
TextRect.right = TextRect.left + CountOfAttr*SCR_FONTSIZE(ScreenInfo).X;
}
LeftChar = max(Row->CharRow.Left,j);
RightChar = min(Row->CharRow.Right,j+CountOfAttr);
NumberOfChars = RightChar - LeftChar;
TextLeft = (LeftChar-ScreenInfo->Window.Left)*SCR_FONTSIZE(ScreenInfo).X;
} else {
LeftChar = j;
TextRect.left = Window.X*SCR_FONTSIZE(ScreenInfo).X;
TextRect.right = TextRect.left + CountOfAttr*SCR_FONTSIZE(ScreenInfo).X;
NumberOfChars = (Row->CharRow.Right > (SHORT)(j + CountOfAttr)) ? (CountOfAttr) : (SHORT)(Row->CharRow.Right-j);
TextLeft = TextRect.left;
}
if (NumberOfChars < 0)
NumberOfChars = 0;
TEXTOUT_CALL;
ExtTextOutW(Console->hDC,
TextLeft,
TextRect.top,
ETO_OPAQUE,
&TextRect,
&Row->CharRow.Chars[LeftChar],
NumberOfChars,
NULL
);
if (OneLine && SimpleWrite) {
break;
}
j+=CountOfAttr;
if (j <= Region->Right) {
Window.X += CountOfAttr;
Attr++;
SetTextColor(Console->hDC, ConvertAttrToRGB(Console, LOBYTE(Attr->Attr)));
SetBkColor(Console->hDC, ConvertAttrToRGB(Console, LOBYTE(Attr->Attr >> 4)));
Console->LastAttributes = Attr->Attr;
CountOfAttr = Attr->Length;
}
}
Window.X = Region->Left - ScreenInfo->Window.Left;
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
}
GdiFlush();
} except( EXCEPTION_EXECUTE_HANDLER ) {
KdPrint(("CONSRV: ExtTextOut raised exception\n"));
// LATER IanJa : typically leaves critsect (GDI's?) locked
}
}
//
// if we have a selection, turn it on.
//
InvertSelection(Console, FALSE);
}
#ifdef i386
else if (Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
WriteRegionToScreenHW(ScreenInfo,Region);
}
#endif
}
VOID
WriteToScreen(
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT Region
)
/*++
Routine Description:
This routine writes a screen buffer region to the screen.
Arguments:
ScreenInfo - Pointer to screen buffer information.
Region - Region to write in screen buffer coordinates. Region is
inclusive
Return Value:
none.
--*/
{
SMALL_RECT ClippedRegion;
DBGOUTPUT(("WriteToScreen\n"));
//
// update to screen, if we're not iconic. we're marked as
// iconic if we're fullscreen, so check for fullscreen.
//
if (!ACTIVE_SCREEN_BUFFER(ScreenInfo) ||
(ScreenInfo->Console->Flags & CONSOLE_IS_ICONIC && ScreenInfo->Console->FullScreenFlags == 0)) {
return;
}
// clip region
ClippedRegion.Left = max(Region->Left, ScreenInfo->Window.Left);
ClippedRegion.Top = max(Region->Top, ScreenInfo->Window.Top);
ClippedRegion.Right = min(Region->Right, ScreenInfo->Window.Right);
ClippedRegion.Bottom = min(Region->Bottom, ScreenInfo->Window.Bottom);
if (ClippedRegion.Right < ClippedRegion.Left ||
ClippedRegion.Bottom < ClippedRegion.Top) {
return;
}
if (ScreenInfo->Flags & CONSOLE_GRAPHICS_BUFFER) {
if (ScreenInfo->Console->FullScreenFlags == 0) {
WriteRegionToScreenBitMap(ScreenInfo, &ClippedRegion);
}
} else {
ConsoleHideCursor(ScreenInfo);
WriteRegionToScreen(ScreenInfo, &ClippedRegion);
ConsoleShowCursor(ScreenInfo);
}
}
NTSTATUS
ReadOutputString(
IN PSCREEN_INFORMATION ScreenInfo,
OUT PVOID Buffer,
IN COORD ReadCoord,
IN ULONG StringType,
IN OUT PULONG NumRecords // this value is valid even for error cases
)
/*++
Routine Description:
This routine reads a string of characters or attributes from the
screen buffer.
Arguments:
ScreenInfo - Pointer to screen buffer information.
Buffer - Buffer to read into.
ReadCoord - Screen buffer coordinate to begin reading from.
StringType
CONSOLE_ASCII - read a string of ascii characters.
CONSOLE_UNICODE - read a string of unicode characters.
CONSOLE_ATTRIBUTE - read a string of attributes.
NumRecords - On input, the size of the buffer in elements. On output,
the number of elements read.
Return Value:
--*/
{
ULONG NumRead;
SHORT X,Y;
SHORT RowIndex;
SHORT CountOfAttr;
PATTR_PAIR Attr;
PROW Row;
PWCHAR Char;
SHORT j,k;
PWCHAR TransBuffer,BufPtr;
DBGOUTPUT(("ReadOutputString\n"));
if (*NumRecords == 0)
return STATUS_SUCCESS;
NumRead = 0;
X=ReadCoord.X;
Y=ReadCoord.Y;
if (X>=ScreenInfo->ScreenBufferSize.X ||
X<0 ||
Y>=ScreenInfo->ScreenBufferSize.Y ||
Y<0) {
*NumRecords = 0;
return STATUS_SUCCESS;
}
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+ReadCoord.Y) % ScreenInfo->ScreenBufferSize.Y;
if (StringType == CONSOLE_ASCII) {
TransBuffer = (PWCHAR)HeapAlloc(pConHeap,MAKE_TAG( TMP_TAG ),*NumRecords * sizeof(WCHAR));
if (TransBuffer == NULL) {
return STATUS_NO_MEMORY;
}
BufPtr = TransBuffer;
} else {
BufPtr = Buffer;
}
if (StringType == CONSOLE_ASCII ||
StringType == CONSOLE_UNICODE) {
while (NumRead < *NumRecords) {
//
// copy the chars from its array
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
Char = &Row->CharRow.Chars[X];
if ((ULONG)(ScreenInfo->ScreenBufferSize.X - X) > (*NumRecords - NumRead)) {
RtlCopyMemory(BufPtr,Char,(*NumRecords - NumRead) * sizeof(WCHAR));
NumRead += *NumRecords - NumRead;
break;
}
RtlCopyMemory(BufPtr,Char,(ScreenInfo->ScreenBufferSize.X - X) * sizeof(WCHAR));
BufPtr = (PVOID)((ULONG)BufPtr + ((ScreenInfo->ScreenBufferSize.X - X) * sizeof(WCHAR)));
NumRead += ScreenInfo->ScreenBufferSize.X - X;
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
X = 0;
Y++;
if (Y>=ScreenInfo->ScreenBufferSize.Y) {
break;
}
}
} else if (StringType == CONSOLE_ATTRIBUTE) {
PWORD TargetPtr=BufPtr;
while (NumRead < *NumRecords) {
//
// copy the attrs from its array
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
FindAttrIndex(Row->AttrRow.Attrs,
X,
&Attr,
&CountOfAttr
);
k=0;
for (j=X;j<ScreenInfo->ScreenBufferSize.X;TargetPtr++) {
*TargetPtr = Attr->Attr;
NumRead++;
j+=1;
if (++k==CountOfAttr && j<ScreenInfo->ScreenBufferSize.X) {
Attr++;
k=0;
CountOfAttr = Attr->Length;
}
if (NumRead == *NumRecords) {
return STATUS_SUCCESS;
}
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
X = 0;
Y++;
if (Y>=ScreenInfo->ScreenBufferSize.Y) {
break;
}
}
} else {
*NumRecords = 0;
return STATUS_INVALID_PARAMETER;
}
if (StringType == CONSOLE_ASCII) {
UINT Codepage;
if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
Codepage = WINDOWSCP;
} else {
Codepage = ScreenInfo->Console->OutputCP;
}
if (NumRead == 1) {
*((PBYTE)Buffer) = WcharToChar(Codepage, *TransBuffer);
} else {
ConvertOutputToOem(Codepage, TransBuffer, NumRead, Buffer, NumRead);
}
HeapFree(pConHeap,0,TransBuffer);
} else if (StringType == CONSOLE_UNICODE &&
(ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
/*
* Buffer contains false Unicode (UnicodeOem) only in Windowed
* RasterFont mode, so in this case, convert it to real Unicode.
*/
FalseUnicodeToRealUnicode(Buffer,
NumRead,
ScreenInfo->Console->OutputCP
);
}
*NumRecords = NumRead;
return STATUS_SUCCESS;
}
NTSTATUS
WriteOutputString(
IN PSCREEN_INFORMATION ScreenInfo,
IN PVOID Buffer,
IN COORD WriteCoord,
IN ULONG StringType,
IN OUT PULONG NumRecords // this value is valid even for error cases
)
/*++
Routine Description:
This routine writes a string of characters or attributes to the
screen buffer.
Arguments:
ScreenInfo - Pointer to screen buffer information.
Buffer - Buffer to write from.
WriteCoord - Screen buffer coordinate to begin writing to.
StringType
CONSOLE_ASCII - write a string of ascii characters.
CONSOLE_UNICODE - write a string of unicode characters.
CONSOLE_ATTRIBUTE - write a string of attributes.
NumRecords - On input, the number of elements to write. On output,
the number of elements written.
Return Value:
--*/
{
ULONG NumWritten;
SHORT X,Y,LeftX;
SMALL_RECT WriteRegion;
PROW Row;
PWCHAR Char;
SHORT RowIndex;
SHORT j;
PWCHAR TransBuffer;
WCHAR SingleChar;
UINT Codepage;
DBGOUTPUT(("WriteOutputString\n"));
if (*NumRecords == 0)
return STATUS_SUCCESS;
NumWritten = 0;
X=WriteCoord.X;
Y=WriteCoord.Y;
if (X>=ScreenInfo->ScreenBufferSize.X ||
X<0 ||
Y>=ScreenInfo->ScreenBufferSize.Y ||
Y<0) {
*NumRecords = 0;
return STATUS_SUCCESS;
}
ScreenInfo->BufferInfo.TextInfo.Flags |= TEXT_VALID_HINT;
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+WriteCoord.Y) % ScreenInfo->ScreenBufferSize.Y;
if (StringType == CONSOLE_ASCII) {
if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
Codepage = WINDOWSCP;
} else {
Codepage = ScreenInfo->Console->OutputCP;
}
if (*NumRecords == 1) {
TransBuffer = NULL;
SingleChar = CharToWcharGlyph(Codepage, *((char *)Buffer));
Buffer = &SingleChar;
} else {
TransBuffer = (PWCHAR)HeapAlloc(pConHeap,MAKE_TAG( TMP_TAG ),*NumRecords * sizeof(WCHAR));
if (TransBuffer == NULL) {
return STATUS_NO_MEMORY;
}
ConvertOutputToUnicode(Codepage, Buffer, *NumRecords,
TransBuffer, *NumRecords);
Buffer = TransBuffer;
}
} else if (StringType == CONSOLE_UNICODE &&
(ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
RealUnicodeToFalseUnicode(Buffer,
*NumRecords,
ScreenInfo->Console->OutputCP
);
}
if (StringType == CONSOLE_UNICODE ||
StringType == CONSOLE_ASCII) {
while (TRUE) {
LeftX = X;
//
// copy the chars into their arrays
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
Char = &Row->CharRow.Chars[X];
if ((ULONG)(ScreenInfo->ScreenBufferSize.X - X) >= (*NumRecords - NumWritten)) {
RtlCopyMemory(Char,Buffer,(*NumRecords - NumWritten) * sizeof(WCHAR));
X=(SHORT)(X+*NumRecords - NumWritten-1);
NumWritten = *NumRecords;
}
else {
RtlCopyMemory(Char,Buffer,(ScreenInfo->ScreenBufferSize.X - X) * sizeof(WCHAR));
Buffer = (PVOID)((ULONG)Buffer + ((ScreenInfo->ScreenBufferSize.X - X) * sizeof(WCHAR)));
NumWritten += ScreenInfo->ScreenBufferSize.X - X;
X = (SHORT)(ScreenInfo->ScreenBufferSize.X-1);
}
// recalculate first and last non-space char
Row->CharRow.OldLeft = Row->CharRow.Left;
if (LeftX < Row->CharRow.Left) {
PWCHAR LastChar = &Row->CharRow.Chars[ScreenInfo->ScreenBufferSize.X];
for (Char=&Row->CharRow.Chars[LeftX];Char < LastChar && *Char==(WCHAR)' ';Char++)
;
Row->CharRow.Left = Char-Row->CharRow.Chars;
}
Row->CharRow.OldRight = Row->CharRow.Right;
if ((X+1) >= Row->CharRow.Right) {
WORD LastNonSpace;
PWCHAR FirstChar = Row->CharRow.Chars;
LastNonSpace = X;
for (Char=&Row->CharRow.Chars[X];*Char==(WCHAR)' ' && Char >= FirstChar;Char--)
LastNonSpace--;
Row->CharRow.Right = (SHORT)(LastNonSpace+1);
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
if (NumWritten < *NumRecords) {
X = 0;
Y++;
if (Y>=ScreenInfo->ScreenBufferSize.Y) {
break;
}
} else {
break;
}
}
} else if (StringType == CONSOLE_ATTRIBUTE) {
PWORD SourcePtr=Buffer;
PATTR_PAIR AttrBuf;
ATTR_PAIR Attrs[80];
PATTR_PAIR Attr;
SHORT AttrLength;
AttrBuf = Attrs;
if (ScreenInfo->ScreenBufferSize.X > 80) {
AttrBuf = (PATTR_PAIR)HeapAlloc(pConHeap,MAKE_TAG( TMP_TAG ),ScreenInfo->ScreenBufferSize.X * sizeof(ATTR_PAIR));
if (AttrBuf == NULL)
return STATUS_NO_MEMORY;
}
while (TRUE) {
//
// copy the attrs into the screen buffer arrays
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
Attr = AttrBuf;
Attr->Length = 0;
Attr->Attr = *SourcePtr;
AttrLength = 1;
for (j=X;j<ScreenInfo->ScreenBufferSize.X;j++,SourcePtr++) {
if (Attr->Attr == *SourcePtr) {
Attr->Length += 1;
}
else {
Attr++;
Attr->Length = 1;
Attr->Attr = *SourcePtr;
AttrLength += 1;
}
NumWritten++;
X++;
if (NumWritten == *NumRecords) {
break;
}
}
X--;
// recalculate last non-space char
//
// see if attr string is different. if so, allocate a new
// attr buffer and merge the two strings.
//
if (AttrLength != Row->AttrRow.Length ||
memcmp(Row->AttrRow.Attrs,AttrBuf,AttrLength*sizeof(*Attr))) {
PATTR_PAIR NewAttrs;
WORD NewAttrsLength;
if (!NT_SUCCESS(MergeAttrStrings(Row->AttrRow.Attrs,
Row->AttrRow.Length,
AttrBuf,
AttrLength,
&NewAttrs,
&NewAttrsLength,
(SHORT)((Y == WriteCoord.Y) ? WriteCoord.X : 0),
X,
Row,
ScreenInfo
))) {
if (ScreenInfo->ScreenBufferSize.X > 80) {
HeapFree(pConHeap,0,AttrBuf);
}
ResetTextFlags(ScreenInfo,WriteCoord.Y,Y);
return STATUS_NO_MEMORY;
}
if (Row->AttrRow.Length > 1) {
HeapFree(pConHeap,0,Row->AttrRow.Attrs);
}
else {
ASSERT(Row->AttrRow.Attrs == &Row->AttrRow.AttrPair);
}
Row->AttrRow.Attrs = NewAttrs;
Row->AttrRow.Length = NewAttrsLength;
Row->CharRow.OldLeft = INVALID_OLD_LENGTH;
Row->CharRow.OldRight = INVALID_OLD_LENGTH;
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
if (NumWritten < *NumRecords) {
X = 0;
Y++;
if (Y>=ScreenInfo->ScreenBufferSize.Y) {
break;
}
} else {
break;
}
}
ResetTextFlags(ScreenInfo,WriteCoord.Y,Y);
if (ScreenInfo->ScreenBufferSize.X > 80) {
HeapFree(pConHeap,0,AttrBuf);
}
} else {
*NumRecords = 0;
return STATUS_INVALID_PARAMETER;
}
if ((StringType == CONSOLE_ASCII) && (TransBuffer != NULL)) {
HeapFree(pConHeap,0,TransBuffer);
}
//
// determine write region. if we're still on the same line we started
// on, left X is the X we started with and right X is the one we're on
// now. otherwise, left X is 0 and right X is the rightmost column of
// the screen buffer.
//
// then update the screen.
//
WriteRegion.Top = WriteCoord.Y;
WriteRegion.Bottom = Y;
if (Y != WriteCoord.Y) {
WriteRegion.Left = 0;
WriteRegion.Right = (SHORT)(ScreenInfo->ScreenBufferSize.X-1);
}
else {
WriteRegion.Left = WriteCoord.X;
WriteRegion.Right = X;
}
WriteToScreen(ScreenInfo,&WriteRegion);
*NumRecords = NumWritten;
return STATUS_SUCCESS;
}
NTSTATUS
FillOutput(
IN PSCREEN_INFORMATION ScreenInfo,
IN WORD Element,
IN COORD WriteCoord,
IN ULONG ElementType,
IN OUT PULONG Length // this value is valid even for error cases
)
/*++
Routine Description:
This routine fills the screen buffer with the specified character or
attribute.
Arguments:
ScreenInfo - Pointer to screen buffer information.
Element - Element to write.
WriteCoord - Screen buffer coordinate to begin writing to.
ElementType
CONSOLE_ASCII - element is an ascii character.
CONSOLE_UNICODE - element is a unicode character.
CONSOLE_ATTRIBUTE - element is an attribute.
Length - On input, the number of elements to write. On output,
the number of elements written.
Return Value:
--*/
{
ULONG NumWritten;
SHORT X,Y,LeftX;
SMALL_RECT WriteRegion;
PROW Row;
PWCHAR Char;
SHORT RowIndex;
SHORT j;
DBGOUTPUT(("FillOutput\n"));
if (*Length == 0)
return STATUS_SUCCESS;
NumWritten = 0;
X=WriteCoord.X;
Y=WriteCoord.Y;
if (X>=ScreenInfo->ScreenBufferSize.X ||
X<0 ||
Y>=ScreenInfo->ScreenBufferSize.Y ||
Y<0) {
*Length = 0;
return STATUS_SUCCESS;
}
ScreenInfo->BufferInfo.TextInfo.Flags |= TEXT_VALID_HINT;
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+WriteCoord.Y) % ScreenInfo->ScreenBufferSize.Y;
if (ElementType == CONSOLE_ASCII) {
UINT Codepage;
if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
((ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN) == 0)) {
Codepage = WINDOWSCP;
} else {
Codepage = ScreenInfo->Console->OutputCP;
}
Element = CharToWchar(Codepage, (CHAR)Element);
} else if (ElementType == CONSOLE_UNICODE &&
(ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
RealUnicodeToFalseUnicode(&Element,
1,
ScreenInfo->Console->OutputCP
);
}
if (ElementType == CONSOLE_ASCII ||
ElementType == CONSOLE_UNICODE) {
while (TRUE) {
//
// copy the chars into their arrays
//
LeftX = X;
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
Char = &Row->CharRow.Chars[X];
if ((ULONG)(ScreenInfo->ScreenBufferSize.X - X) >= (*Length - NumWritten)) {
for (j=0;j<(SHORT)(*Length - NumWritten);j++) {
*Char++ = (WCHAR)Element;
}
X=(SHORT)(X+*Length - NumWritten - 1);
NumWritten = *Length;
}
else {
for (j=0;j<ScreenInfo->ScreenBufferSize.X - X;j++) {
*Char++ = (WCHAR)Element;
}
NumWritten += ScreenInfo->ScreenBufferSize.X - X;
X = (SHORT)(ScreenInfo->ScreenBufferSize.X-1);
}
// recalculate first and last non-space char
Row->CharRow.OldLeft = Row->CharRow.Left;
if (LeftX < Row->CharRow.Left) {
if (Element == UNICODE_SPACE) {
Row->CharRow.Left = X+1;
} else {
Row->CharRow.Left = LeftX;
}
}
Row->CharRow.OldRight = Row->CharRow.Right;
if ((X+1) >= Row->CharRow.Right) {
if (Element == UNICODE_SPACE) {
Row->CharRow.Right = LeftX;
} else {
Row->CharRow.Right = X+1;
}
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
if (NumWritten < *Length) {
X = 0;
Y++;
if (Y>=ScreenInfo->ScreenBufferSize.Y) {
break;
}
} else {
break;
}
}
} else if (ElementType == CONSOLE_ATTRIBUTE) {
ATTR_PAIR Attr;
while (TRUE) {
//
// copy the attrs into the screen buffer arrays
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
if ((ULONG)(ScreenInfo->ScreenBufferSize.X - X) >= (*Length - NumWritten)) {
X=(SHORT)(X+*Length - NumWritten - 1);
NumWritten = *Length;
}
else {
NumWritten += ScreenInfo->ScreenBufferSize.X - X;
X = (SHORT)(ScreenInfo->ScreenBufferSize.X-1);
}
// recalculate last non-space char
//
// merge the two attribute strings.
//
Attr.Length = (SHORT)((Y == WriteCoord.Y) ? (X-WriteCoord.X+1) : (X+1));
Attr.Attr = Element;
if (1 != Row->AttrRow.Length ||
memcmp(Row->AttrRow.Attrs,&Attr,sizeof(Attr))) {
PATTR_PAIR NewAttrs;
WORD NewAttrsLength;
if (!NT_SUCCESS(MergeAttrStrings(Row->AttrRow.Attrs,
Row->AttrRow.Length,
&Attr,
1,
&NewAttrs,
&NewAttrsLength,
(SHORT)(X-Attr.Length+1),
X,
Row,
ScreenInfo
))) {
ResetTextFlags(ScreenInfo,WriteCoord.Y,Y);
return STATUS_NO_MEMORY;
}
if (Row->AttrRow.Length > 1) {
HeapFree(pConHeap,0,Row->AttrRow.Attrs);
}
else {
ASSERT(Row->AttrRow.Attrs == &Row->AttrRow.AttrPair);
}
Row->AttrRow.Attrs = NewAttrs;
Row->AttrRow.Length = NewAttrsLength;
Row->CharRow.OldLeft = INVALID_OLD_LENGTH;
Row->CharRow.OldRight = INVALID_OLD_LENGTH;
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
if (NumWritten < *Length) {
X = 0;
Y++;
if (Y>=ScreenInfo->ScreenBufferSize.Y) {
break;
}
} else {
break;
}
}
ResetTextFlags(ScreenInfo,WriteCoord.Y,Y);
} else {
*Length = 0;
return STATUS_INVALID_PARAMETER;
}
//
// determine write region. if we're still on the same line we started
// on, left X is the X we started with and right X is the one we're on
// now. otherwise, left X is 0 and right X is the rightmost column of
// the screen buffer.
//
// then update the screen.
//
WriteRegion.Top = WriteCoord.Y;
WriteRegion.Bottom = Y;
if (Y != WriteCoord.Y) {
WriteRegion.Left = 0;
WriteRegion.Right = (SHORT)(ScreenInfo->ScreenBufferSize.X-1);
}
else {
WriteRegion.Left = WriteCoord.X;
WriteRegion.Right = X;
}
WriteToScreen(ScreenInfo,&WriteRegion);
*Length = NumWritten;
return STATUS_SUCCESS;
}
NTSTATUS
GetScreenBufferInformation(
IN PSCREEN_INFORMATION ScreenInfo,
OUT PCOORD Size,
OUT PCOORD CursorPosition,
OUT PCOORD ScrollPosition,
OUT PWORD Attributes,
OUT PCOORD CurrentWindowSize,
OUT PCOORD MaximumWindowSize
)
/*++
Routine Description:
This routine returns data about the screen buffer.
Arguments:
ScreenInfo - Pointer to screen buffer information.
Size - Pointer to location in which to store screen buffer size.
CursorPosition - Pointer to location in which to store the cursor position.
ScrollPosition - Pointer to location in which to store the scroll position.
Attributes - Pointer to location in which to store the default attributes.
CurrentWindowSize - Pointer to location in which to store current window size.
MaximumWindowSize - Pointer to location in which to store maximum window size.
Return Value:
--*/
{
//
// Make sure our max screen sizes reflect reality
//
UpdateScreenSizes(ScreenInfo, ScreenInfo->ScreenBufferSize);
*Size = ScreenInfo->ScreenBufferSize;
*CursorPosition = ScreenInfo->BufferInfo.TextInfo.CursorPosition;
ScrollPosition->X = ScreenInfo->Window.Left;
ScrollPosition->Y = ScreenInfo->Window.Top;
*Attributes = ScreenInfo->Attributes;
CurrentWindowSize->X = (SHORT)CONSOLE_WINDOW_SIZE_X(ScreenInfo);
CurrentWindowSize->Y = (SHORT)CONSOLE_WINDOW_SIZE_Y(ScreenInfo);
if (ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
MaximumWindowSize->X = min(80,ScreenInfo->ScreenBufferSize.X);
MaximumWindowSize->Y = min(50,ScreenInfo->ScreenBufferSize.Y);
} else {
*MaximumWindowSize = ScreenInfo->MaximumWindowSize;
}
return STATUS_SUCCESS;
}
VOID
FillRectangle(
IN PCHAR_INFO Fill,
IN OUT PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT TargetRect
)
/*++
Routine Description:
This routine fills a rectangular region in the screen
buffer. no clipping is done.
Arguments:
Fill - pointer to element to copy to each element in target rect
ScreenInfo - pointer to screen info
TargetRect - rectangle in screen buffer to fill
Return Value:
--*/
{
SHORT i,j;
SHORT XSize;
SHORT RowIndex;
PROW Row;
PWCHAR Char;
ATTR_PAIR Attr;
DBGOUTPUT(("FillRectangle\n"));
XSize = (SHORT)(TargetRect->Right - TargetRect->Left + 1);
ScreenInfo->BufferInfo.TextInfo.Flags |= TEXT_VALID_HINT;
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+TargetRect->Top) % ScreenInfo->ScreenBufferSize.Y;
for (i=TargetRect->Top;i<=TargetRect->Bottom;i++) {
//
// copy the chars and attrs into their respective arrays
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
Char = &Row->CharRow.Chars[TargetRect->Left];
for (j=0;j<XSize;j++) {
*Char++ = Fill->Char.UnicodeChar;
}
// recalculate first and last non-space char
Row->CharRow.OldLeft = Row->CharRow.Left;
if (TargetRect->Left < Row->CharRow.Left) {
if (Fill->Char.UnicodeChar == UNICODE_SPACE) {
Row->CharRow.Left = (SHORT)(TargetRect->Right+1);
}
else {
Row->CharRow.Left = (SHORT)(TargetRect->Left);
}
}
Row->CharRow.OldRight = Row->CharRow.Right;
if (TargetRect->Right >= Row->CharRow.Right) {
if (Fill->Char.UnicodeChar == UNICODE_SPACE) {
Row->CharRow.Right = (SHORT)(TargetRect->Left);
}
else {
Row->CharRow.Right = (SHORT)(TargetRect->Right+1);
}
}
Attr.Length = XSize;
Attr.Attr = Fill->Attributes;
//
// merge the two attribute strings.
//
if (1 != Row->AttrRow.Length ||
memcmp(Row->AttrRow.Attrs,&Attr,sizeof(Attr))) {
PATTR_PAIR NewAttrs;
WORD NewAttrsLength;
if (!NT_SUCCESS(MergeAttrStrings(Row->AttrRow.Attrs,
Row->AttrRow.Length,
&Attr,
1,
&NewAttrs,
&NewAttrsLength,
TargetRect->Left,
TargetRect->Right,
Row,
ScreenInfo
))) {
ResetTextFlags(ScreenInfo,TargetRect->Top,TargetRect->Bottom);
return;
}
if (Row->AttrRow.Length > 1) {
HeapFree(pConHeap,0,Row->AttrRow.Attrs);
}
else {
ASSERT(Row->AttrRow.Attrs == &Row->AttrRow.AttrPair);
}
Row->AttrRow.Attrs = NewAttrs;
Row->AttrRow.Length = NewAttrsLength;
Row->CharRow.OldLeft = INVALID_OLD_LENGTH;
Row->CharRow.OldRight = INVALID_OLD_LENGTH;
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
}
ResetTextFlags(ScreenInfo,TargetRect->Top,TargetRect->Bottom);
}
VOID
UpdateScrollBars(
IN PSCREEN_INFORMATION ScreenInfo
)
{
if (!ACTIVE_SCREEN_BUFFER(ScreenInfo)) {
return;
}
if (ScreenInfo->Console->Flags & CONSOLE_UPDATING_SCROLL_BARS)
return;
ScreenInfo->Console->Flags |= CONSOLE_UPDATING_SCROLL_BARS;
PostMessage(ScreenInfo->Console->hWnd,
CM_UPDATE_SCROLL_BARS,
(DWORD)ScreenInfo,
0
);
}
VOID
InternalUpdateScrollBars(
IN PSCREEN_INFORMATION ScreenInfo
)
{
SCROLLINFO si;
ScreenInfo->Console->Flags &= ~CONSOLE_UPDATING_SCROLL_BARS;
if (!ACTIVE_SCREEN_BUFFER(ScreenInfo)) {
return;
}
ScreenInfo->ResizingWindow++;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
si.nPage = CONSOLE_WINDOW_SIZE_Y(ScreenInfo);
si.nMin = 0;
si.nMax = ScreenInfo->ScreenBufferSize.Y - 1;
si.nPos = ScreenInfo->Window.Top;
SetScrollInfo(ScreenInfo->Console->hWnd, SB_VERT, &si, TRUE);
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
si.nPage = CONSOLE_WINDOW_SIZE_X(ScreenInfo);
si.nMin = 0;
si.nMax = ScreenInfo->ScreenBufferSize.X - 1;
si.nPos = ScreenInfo->Window.Left;
SetScrollInfo(ScreenInfo->Console->hWnd, SB_HORZ, &si, TRUE);
ScreenInfo->ResizingWindow--;
}
VOID
ScreenBufferSizeChange(
IN PSCREEN_INFORMATION ScreenInfo,
IN COORD NewSize
)
{
INPUT_RECORD InputEvent;
InputEvent.EventType = WINDOW_BUFFER_SIZE_EVENT;
InputEvent.Event.WindowBufferSizeEvent.dwSize = NewSize;
WriteInputBuffer(ScreenInfo->Console,
&ScreenInfo->Console->InputBuffer,
&InputEvent,
1
);
}
NTSTATUS
ResizeScreenBuffer(
IN PSCREEN_INFORMATION ScreenInfo,
IN COORD NewScreenSize,
IN BOOL DoScrollBarUpdate
)
/*++
Routine Description:
This routine resizes the screen buffer.
Arguments:
ScreenInfo - pointer to screen buffer info.
NewScreenSize - new size of screen.
Return Value:
--*/
{
SHORT i,j;
BOOL WindowMaximizedX,WindowMaximizedY;
SHORT LimitX,LimitY;
PWCHAR TextRows,TextRowPtr;
BOOL UpdateWindow;
SHORT TopRow,TopRowIndex; // new top row of screen buffer
COORD CursorPosition;
//
// Don't allow resize of graphics apps
//
if (!(ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER)) {
return STATUS_UNSUCCESSFUL;
}
TextRows = (PWCHAR)HeapAlloc(pConHeap,MAKE_TAG( SCREEN_TAG ),NewScreenSize.X*NewScreenSize.Y*sizeof(WCHAR));
if (TextRows == NULL) {
return STATUS_NO_MEMORY;
}
LimitX = (NewScreenSize.X < ScreenInfo->ScreenBufferSize.X) ?
NewScreenSize.X : ScreenInfo->ScreenBufferSize.X;
LimitY = (NewScreenSize.Y < ScreenInfo->ScreenBufferSize.Y) ?
NewScreenSize.Y : ScreenInfo->ScreenBufferSize.Y;
TopRow = 0;
if (NewScreenSize.Y <= ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y) {
TopRow += ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y - NewScreenSize.Y + 1;
}
TopRowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+TopRow) % ScreenInfo->ScreenBufferSize.Y;
if (NewScreenSize.Y != ScreenInfo->ScreenBufferSize.Y) {
PROW Temp;
SHORT NumToCopy,NumToCopy2;
//
// resize ROWs array. first alloc a new ROWs array. then copy the old
// one over, resetting the FirstRow.
//
//
Temp = (PROW)HeapAlloc(pConHeap,MAKE_TAG( SCREEN_TAG ),NewScreenSize.Y*sizeof(ROW));
if (Temp == NULL) {
HeapFree(pConHeap,0,TextRows);
return STATUS_NO_MEMORY;
}
NumToCopy = ScreenInfo->ScreenBufferSize.Y-TopRowIndex;
if (NumToCopy > NewScreenSize.Y)
NumToCopy = NewScreenSize.Y;
RtlCopyMemory(Temp,&ScreenInfo->BufferInfo.TextInfo.Rows[TopRowIndex],NumToCopy*sizeof(ROW));
if (TopRowIndex!=0 && NumToCopy != NewScreenSize.Y) {
NumToCopy2 = TopRowIndex;
if (NumToCopy2 > (NewScreenSize.Y-NumToCopy))
NumToCopy2 = NewScreenSize.Y-NumToCopy;
RtlCopyMemory(&Temp[NumToCopy],
ScreenInfo->BufferInfo.TextInfo.Rows,
NumToCopy2*sizeof(ROW)
);
}
for (i=0;i<LimitY;i++) {
if (Temp[i].AttrRow.Length == 1) {
Temp[i].AttrRow.Attrs = &Temp[i].AttrRow.AttrPair;
}
}
//
// if the new screen buffer has fewer rows than the existing one,
// free the extra rows. if the new screen buffer has more rows
// than the existing one, allocate new rows.
//
if (NewScreenSize.Y < ScreenInfo->ScreenBufferSize.Y) {
i = (TopRowIndex+NewScreenSize.Y) % ScreenInfo->ScreenBufferSize.Y;
for (j=NewScreenSize.Y;j<ScreenInfo->ScreenBufferSize.Y;j++) {
if (ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Length > 1) {
HeapFree(pConHeap,0,ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs);
}
if (++i == ScreenInfo->ScreenBufferSize.Y) {
i = 0;
}
}
} else if (NewScreenSize.Y > ScreenInfo->ScreenBufferSize.Y) {
for (i=ScreenInfo->ScreenBufferSize.Y;i<NewScreenSize.Y;i++) {
Temp[i].AttrRow.Length = 1;
Temp[i].AttrRow.AttrPair.Length = NewScreenSize.X;
Temp[i].AttrRow.AttrPair.Attr = ScreenInfo->Attributes;
Temp[i].AttrRow.Attrs = &Temp[i].AttrRow.AttrPair;
}
}
ScreenInfo->BufferInfo.TextInfo.FirstRow = 0;
HeapFree(pConHeap,0,ScreenInfo->BufferInfo.TextInfo.Rows);
ScreenInfo->BufferInfo.TextInfo.Rows = Temp;
}
//
// Realloc each row. any horizontal growth results in the last
// attribute in a row getting extended.
//
for (i=0,TextRowPtr=TextRows;i<LimitY;i++,TextRowPtr+=NewScreenSize.X) {
RtlCopyMemory(TextRowPtr,
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Chars,
LimitX*sizeof(WCHAR));
for (j=ScreenInfo->ScreenBufferSize.X;j<NewScreenSize.X;j++) {
TextRowPtr[j] = (WCHAR)' ';
}
if (ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Right > NewScreenSize.X) {
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.OldRight = INVALID_OLD_LENGTH;
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Right = NewScreenSize.X;
}
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Chars = TextRowPtr;
}
for (;i<NewScreenSize.Y;i++,TextRowPtr+=NewScreenSize.X) {
for (j=0;j<NewScreenSize.X;j++) {
TextRowPtr[j] = (WCHAR)' ';
}
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Chars = TextRowPtr;
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.OldLeft = INVALID_OLD_LENGTH;
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.OldRight = INVALID_OLD_LENGTH;
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Left = NewScreenSize.X;
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Right = 0;
}
HeapFree(pConHeap,0,ScreenInfo->BufferInfo.TextInfo.TextRows);
ScreenInfo->BufferInfo.TextInfo.TextRows = TextRows;
if (NewScreenSize.X != ScreenInfo->ScreenBufferSize.X) {
for (i=0;i<LimitY;i++) {
PATTR_PAIR IndexedAttr;
SHORT CountOfAttr;
if (NewScreenSize.X > ScreenInfo->ScreenBufferSize.X) {
FindAttrIndex(ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs,
(SHORT)(ScreenInfo->ScreenBufferSize.X-1),
&IndexedAttr,
&CountOfAttr
);
ASSERT (IndexedAttr <=
&ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs[ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Length-1]);
IndexedAttr->Length += NewScreenSize.X - ScreenInfo->ScreenBufferSize.X;
}
else {
FindAttrIndex(ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs,
(SHORT)(NewScreenSize.X-1),
&IndexedAttr,
&CountOfAttr
);
IndexedAttr->Length -= CountOfAttr-1;
if (ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Length != 1) {
ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Length = (SHORT)(IndexedAttr - ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs + 1);
if (ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Length != 1) {
ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs = (PATTR_PAIR)HeapReAlloc(pConHeap,MAKE_TAG( SCREEN_TAG ),ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs,
ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Length * sizeof(ATTR_PAIR));
}
else {
ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.AttrPair = *IndexedAttr;
HeapFree(pConHeap,0,ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs);
ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs = &ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.AttrPair;
}
}
}
}
}
//
// if the screen buffer is resized smaller than the saved
// window size, shrink the saved window size.
//
#ifdef i386
if (ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN) {
if (NewScreenSize.X < ScreenInfo->BufferInfo.TextInfo.WindowedWindowSize.X) {
ScreenInfo->BufferInfo.TextInfo.WindowedWindowSize.X = NewScreenSize.X;
}
if (NewScreenSize.Y < ScreenInfo->BufferInfo.TextInfo.WindowedWindowSize.Y) {
ScreenInfo->BufferInfo.TextInfo.WindowedWindowSize.Y = NewScreenSize.Y;
}
ScreenInfo->BufferInfo.TextInfo.WindowedScreenSize = NewScreenSize;
}
#endif
UpdateWindow = FALSE;
//
// if the screen buffer shrunk beyond the boundaries of the window,
// adjust the window origin.
//
if (NewScreenSize.X > CONSOLE_WINDOW_SIZE_X(ScreenInfo)) {
if (ScreenInfo->Window.Right >= NewScreenSize.X) {
ScreenInfo->Window.Left -= ScreenInfo->Window.Right - NewScreenSize.X + 1;
ScreenInfo->Window.Right -= ScreenInfo->Window.Right - NewScreenSize.X + 1;
UpdateWindow = TRUE;
}
} else {
ScreenInfo->Window.Left = 0;
ScreenInfo->Window.Right = NewScreenSize.X - 1;
UpdateWindow = TRUE;
}
if (NewScreenSize.Y > CONSOLE_WINDOW_SIZE_Y(ScreenInfo)) {
if (ScreenInfo->Window.Bottom >= NewScreenSize.Y) {
ScreenInfo->Window.Top -= ScreenInfo->Window.Bottom - NewScreenSize.Y + 1;
ScreenInfo->Window.Bottom -= ScreenInfo->Window.Bottom - NewScreenSize.Y + 1;
UpdateWindow = TRUE;
}
} else {
ScreenInfo->Window.Top = 0;
ScreenInfo->Window.Bottom = NewScreenSize.Y - 1;
UpdateWindow = TRUE;
}
//
// adjust cursor position if it's no longer with screen buffer
//
CursorPosition=ScreenInfo->BufferInfo.TextInfo.CursorPosition;
if (CursorPosition.X >= NewScreenSize.X) {
CursorPosition.X = 0;
}
if (CursorPosition.Y >= NewScreenSize.Y) {
CursorPosition.Y = NewScreenSize.Y-1;
}
if (CursorPosition.X != ScreenInfo->BufferInfo.TextInfo.CursorPosition.X ||
CursorPosition.Y != ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y) {
SetCursorPosition(ScreenInfo,
CursorPosition,
FALSE
);
}
ASSERT (ScreenInfo->Window.Left >= 0);
ASSERT (ScreenInfo->Window.Right < NewScreenSize.X);
ASSERT (ScreenInfo->Window.Top >= 0);
ASSERT (ScreenInfo->Window.Bottom < NewScreenSize.Y);
ScreenInfo->ScreenBufferSize = NewScreenSize;
ResetTextFlags(ScreenInfo,0,(SHORT)(ScreenInfo->ScreenBufferSize.Y-1));
WindowMaximizedX = (CONSOLE_WINDOW_SIZE_X(ScreenInfo) ==
ScreenInfo->ScreenBufferSize.X);
WindowMaximizedY = (CONSOLE_WINDOW_SIZE_Y(ScreenInfo) ==
ScreenInfo->ScreenBufferSize.Y);
UpdateScreenSizes(ScreenInfo, ScreenInfo->ScreenBufferSize);
if (ScreenInfo->WindowMaximizedX != WindowMaximizedX ||
ScreenInfo->WindowMaximizedY != WindowMaximizedY) {
ScreenInfo->WindowMaximizedX = WindowMaximizedX;
ScreenInfo->WindowMaximizedY = WindowMaximizedY;
UpdateWindow = TRUE;
}
if (UpdateWindow) {
SetWindowSize(ScreenInfo);
}
if (DoScrollBarUpdate) {
UpdateScrollBars(ScreenInfo);
}
if (ScreenInfo->Console->InputBuffer.InputMode & ENABLE_WINDOW_INPUT) {
ScreenBufferSizeChange(ScreenInfo,ScreenInfo->ScreenBufferSize);
}
return STATUS_SUCCESS;
}
NTSTATUS
AllocateScrollBuffer(
DWORD Size
)
{
ScrollBuffer = (PCHAR_INFO)HeapAlloc(pConHeap,MAKE_TAG( SCREEN_TAG ),Size);
if (ScrollBuffer == NULL) {
ScrollBufferSize = 0;
return STATUS_NO_MEMORY;
}
ScrollBufferSize = Size;
return STATUS_SUCCESS;
}
VOID
FreeScrollBuffer( VOID )
{
HeapFree(pConHeap,0,ScrollBuffer);
ScrollBuffer = NULL;
ScrollBufferSize = 0;
}
NTSTATUS
InitializeScrollBuffer( VOID )
{
NTSTATUS Status;
ghrgnScroll = CreateRectRgn(0,0,1,1);
gprgnData = (LPRGNDATA)HeapAlloc(pConHeap,MAKE_TAG( SCREEN_TAG ),GRGNDATASIZE);
Status = AllocateScrollBuffer(DefaultRegInfo.ScreenBufferSize.X *
DefaultRegInfo.ScreenBufferSize.Y *
sizeof(CHAR_INFO));
if (!NT_SUCCESS(Status)) {
return Status;
}
RtlInitializeCriticalSection(&ScrollBufferLock);
return STATUS_SUCCESS;
}
VOID
UpdateComplexRegion(
IN PSCREEN_INFORMATION ScreenInfo,
IN COORD FontSize
)
{
int iSize,i;
LPRECT pRect;
SMALL_RECT UpdateRegion;
LPRGNDATA pRgnData;
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
ScreenInfo->BufferInfo.TextInfo.Flags &= ~TEXT_VALID_HINT;
}
pRgnData = gprgnData;
/*
* the dreaded complex region.
*/
iSize = GetRegionData(ghrgnScroll, 0, NULL);
if (iSize > GRGNDATASIZE) {
pRgnData = (LPRGNDATA)HeapAlloc(pConHeap,MAKE_TAG( TMP_TAG ),iSize);
if (pRgnData == NULL)
return;
}
if (!GetRegionData(ghrgnScroll, iSize, pRgnData)) {
ASSERT(FALSE);
if (pRgnData != gprgnData) {
HeapFree(pConHeap,0,pRgnData);
}
return;
}
pRect = (PRECT)&pRgnData->Buffer;
/*
* Redraw each rectangle
*/
for(i=0;i<(int)pRgnData->rdh.nCount;i++,pRect++) {
/*
* ICK!!!!!! Convert to chars. This sucks. We know
* this is only get to get converted back during
* the textout call.
*/
UpdateRegion.Left = (SHORT)((pRect->left/FontSize.X)+ \
ScreenInfo->Window.Left);
UpdateRegion.Right = (SHORT)(((pRect->right-1)/FontSize.X)+ \
ScreenInfo->Window.Left);
UpdateRegion.Top = (SHORT)((pRect->top/FontSize.Y)+ \
ScreenInfo->Window.Top);
UpdateRegion.Bottom = (SHORT)(((pRect->bottom-1)/FontSize.Y)+ \
ScreenInfo->Window.Top);
/*
* Fill the rectangle with goodies.
*/
WriteToScreen(ScreenInfo, &UpdateRegion);
}
if (pRgnData != gprgnData) {
HeapFree(pConHeap,0,pRgnData);
}
}
VOID
ScrollScreen(
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT ScrollRect,
IN PSMALL_RECT MergeRect,
IN COORD TargetPoint
)
{
RECT ScrollRectGdi;
SMALL_RECT UpdateRegion;
COORD FontSize;
BOOL Success;
RECT BoundingBox;
DBGOUTPUT(("ScrollScreen\n"));
if (!ACTIVE_SCREEN_BUFFER(ScreenInfo)) {
return;
}
if (ScreenInfo->Console->FullScreenFlags == 0 && !(ScreenInfo->Console->Flags & CONSOLE_IS_ICONIC)) {
ScrollRectGdi.left = ScrollRect->Left-ScreenInfo->Window.Left;
ScrollRectGdi.right = (ScrollRect->Right-ScreenInfo->Window.Left+1);
ScrollRectGdi.top = ScrollRect->Top-ScreenInfo->Window.Top;
ScrollRectGdi.bottom = (ScrollRect->Bottom-ScreenInfo->Window.Top+1);
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
FontSize = SCR_FONTSIZE(ScreenInfo);
ScrollRectGdi.left *= FontSize.X;
ScrollRectGdi.right *= FontSize.X;
ScrollRectGdi.top *= FontSize.Y;
ScrollRectGdi.bottom *= FontSize.Y;
ASSERT (ScreenInfo->BufferInfo.TextInfo.UpdatingScreen>0);
} else {
FontSize.X = 1;
FontSize.Y = 1;
}
SCROLLDC_CALL;
LockScrollBuffer();
Success = (int)ScrollDC(ScreenInfo->Console->hDC,
(TargetPoint.X-ScrollRect->Left)*FontSize.X,
(TargetPoint.Y-ScrollRect->Top)*FontSize.Y,
&ScrollRectGdi,
NULL,
ghrgnScroll,
NULL);
if (Success) {
/*
* Fetch our rectangles. If this is a simple rect then
* we have already retrieved the rectangle. Otherwise
* we need to call gdi to get the rectangles. We are
* optimzied for speed rather than size.
*/
switch (GetRgnBox(ghrgnScroll, &BoundingBox)) {
case SIMPLEREGION:
UpdateRegion.Left = (SHORT)((BoundingBox.left / FontSize.X) + \
ScreenInfo->Window.Left);
UpdateRegion.Right = (SHORT)(((BoundingBox.right-1) / FontSize.X) + \
ScreenInfo->Window.Left);
UpdateRegion.Top = (SHORT)((BoundingBox.top / FontSize.Y) + \
ScreenInfo->Window.Top);
UpdateRegion.Bottom = (SHORT)(((BoundingBox.bottom-1) / FontSize.Y) + \
ScreenInfo->Window.Top);
WriteToScreen(ScreenInfo, &UpdateRegion);
break;
case COMPLEXREGION:
UpdateComplexRegion(ScreenInfo, FontSize);
break;
}
if (MergeRect) {
WriteToScreen(ScreenInfo, MergeRect);
}
} else {
WriteToScreen(ScreenInfo, &ScreenInfo->Window);
}
UnlockScrollBuffer();
}
#ifdef i386
else if (ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
ScrollHW(ScreenInfo,
ScrollRect,
MergeRect,
TargetPoint
);
}
#endif
}
BOOL
PolyTextOutCandidate(
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT Region
)
/*
This function returns TRUE if the input region is reasonable to
pass to ConsolePolyTextOut. The criteria are that there is only
one attribute per line.
*/
{
SHORT RowIndex;
PROW Row;
SHORT i;
if (ScreenInfo->BufferInfo.TextInfo.Flags & SINGLE_ATTRIBUTES_PER_LINE) {
return TRUE;
}
//
// make sure there is only one attr per line.
//
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+Region->Top) % ScreenInfo->ScreenBufferSize.Y;
for (i=Region->Top;i<=Region->Bottom;i++) {
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
if (Row->AttrRow.Length != 1) {
return FALSE;
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
}
return TRUE;
}
#define MAX_POLY_LINES 80
#define VERY_BIG_NUMBER 0x0FFFFFFF
VOID
ConsolePolyTextOut(
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT Region
)
/*
This function calls PolyTextOut. The only restriction is that
there can't be more than one attribute per line in the region.
*/
{
PROW Row,LastRow;
SHORT i,k;
WORD Attr;
POLYTEXTW TextInfo[MAX_POLY_LINES];
RECT TextRect;
RECTL BoundingRect;
int xSize = SCR_FONTSIZE(ScreenInfo).X;
int ySize = SCR_FONTSIZE(ScreenInfo).Y;
ULONG Flags = ScreenInfo->BufferInfo.TextInfo.Flags;
int WindowLeft = ScreenInfo->Window.Left;
int RegionLeft = Region->Left;
int RegionRight = Region->Right + 1;
int DefaultLeft = (RegionLeft - WindowLeft) * xSize;
int DefaultRight = (RegionRight - WindowLeft) * xSize;
PCONSOLE_INFORMATION Console = ScreenInfo->Console;
try { // capture TextOut exceptions for low memory
//
// initialize the text rect and window position.
//
TextRect.top = (Region->Top - ScreenInfo->Window.Top) * ySize;
// TextRect.bottom is invalid.
BoundingRect.top = TextRect.top;
BoundingRect.left = VERY_BIG_NUMBER;
BoundingRect.right = 0;
//
// copy the chars and attrs from their respective arrays
//
Row = &ScreenInfo->BufferInfo.TextInfo.Rows
[ScreenInfo->BufferInfo.TextInfo.FirstRow+Region->Top];
LastRow = &ScreenInfo->BufferInfo.TextInfo.Rows[ScreenInfo->ScreenBufferSize.Y];
if (Row >= LastRow)
Row -= ScreenInfo->ScreenBufferSize.Y;
Attr = Row->AttrRow.AttrPair.Attr;
if (Console->LastAttributes != Attr) {
SetTextColor(Console->hDC, ConvertAttrToRGB(Console, LOBYTE(Attr)));
SetBkColor(Console->hDC, ConvertAttrToRGB(Console, LOBYTE(Attr >> 4)));
Console->LastAttributes = Attr;
}
for (i=Region->Top;i<=Region->Bottom;) {
for(k=0;i<=Region->Bottom&&k<MAX_POLY_LINES;i++) {
SHORT NumberOfChars;
SHORT LeftChar,RightChar;
//
// make the bounding rect smaller, if we can. the TEXT_VALID_HINT
// flag gets set each time we write to the screen buffer. it gets
// turned off any time we get asked to redraw the screen
// and we don't know exactly what needs to be redrawn
// (i.e. paint messages).
//
// we have the left and right bounds of the text on the
// line. the opaqueing rectangle and the number of
// chars get set according to those values.
//
TextRect.left = DefaultLeft;
TextRect.right = DefaultRight;
if (Flags & TEXT_VALID_HINT)
{
// We compute an opaquing interval. If A is the old interval of text,
// B is the new interval, and R is the Region, then the opaquing interval
// must be R*(A+B), where * represents intersection and + represents union.
if (Row->CharRow.OldLeft != INVALID_OLD_LENGTH)
{
// The min determines the left of (A+B). The max intersects that with
// the left of the region.
TextRect.left = (
max
(
min
(
Row->CharRow.Left,
Row->CharRow.OldLeft
),
RegionLeft
)
-WindowLeft
) * xSize;
}
if (Row->CharRow.OldRight != INVALID_OLD_LENGTH)
{
// The max determines the right of (A+B). The min intersects that with
// the right of the region.
TextRect.right = (
min
(
max
(
Row->CharRow.Right,
Row->CharRow.OldRight
),
RegionRight
)
-WindowLeft
) * xSize;
}
}
//
// We've got to draw any new text that appears in the region, so we just
// intersect the new text interval with the region.
//
LeftChar = max(Row->CharRow.Left,RegionLeft);
RightChar = min(Row->CharRow.Right,RegionRight);
NumberOfChars = RightChar - LeftChar;
//
// Empty rows are represented by CharRow.Right=0, CharRow.Left=MAX, so we
// may have NumberOfChars<0 at this point if there is no text that needs
// drawing. (I.e. the intersection was empty.)
//
if (NumberOfChars < 0) {
NumberOfChars = 0;
LeftChar = 0;
RightChar = 0;
}
//
// We may also have TextRect.right<TextRect.left if the screen
// is already cleared, and we really don't need to do anything at all.
//
if (TextRect.right > TextRect.left)
{
TextInfo[k].x = (LeftChar-WindowLeft) * xSize;
TextInfo[k].y = TextRect.top;
TextRect.bottom = TextRect.top + ySize;
TextInfo[k].n = NumberOfChars;
TextInfo[k].lpstr = &Row->CharRow.Chars[LeftChar];
TextInfo[k].rcl = TextRect;
TextInfo[k].pdx = NULL;
TextInfo[k].uiFlags = ETO_OPAQUE;
k++;
if (BoundingRect.left > TextRect.left) {
BoundingRect.left = TextRect.left;
}
if (BoundingRect.right < TextRect.right) {
BoundingRect.right = TextRect.right;
}
}
// Advance the high res bounds.
TextRect.top += ySize;
// Advance the row pointer.
if (++Row >= LastRow)
Row = ScreenInfo->BufferInfo.TextInfo.Rows;
// Draw now if the attributes are about to change.
if (Attr != Row->AttrRow.AttrPair.Attr) {
Attr = Row->AttrRow.AttrPair.Attr;
i++;
break;
}
}
if (k)
{
BoundingRect.bottom = TextRect.top;
ASSERT(BoundingRect.left != VERY_BIG_NUMBER);
ASSERT(BoundingRect.left <= BoundingRect.right);
ASSERT(BoundingRect.top <= BoundingRect.bottom);
GdiConsoleTextOut(Console->hDC,
TextInfo,
k,
&BoundingRect);
}
if (Console->LastAttributes != Attr) {
SetTextColor(Console->hDC, ConvertAttrToRGB(Console, LOBYTE(Attr)));
SetBkColor(Console->hDC, ConvertAttrToRGB(Console, LOBYTE(Attr >> 4)));
Console->LastAttributes = Attr;
BoundingRect.top = TextRect.top;
BoundingRect.left = VERY_BIG_NUMBER;
BoundingRect.right = 0;
}
}
GdiFlush();
} except( EXCEPTION_EXECUTE_HANDLER ) {
KdPrint(("CONSRV: ConsoleTextOut raised exception\n"));
return;
}
}
void CopyRow(
PROW Row,
PROW PrevRow)
{
if (PrevRow->AttrRow.Length != 1 ||
Row->AttrRow.Length != 1 ||
PrevRow->AttrRow.Attrs->Attr != Row->AttrRow.Attrs->Attr) {
Row->CharRow.OldRight = INVALID_OLD_LENGTH;
Row->CharRow.OldLeft = INVALID_OLD_LENGTH;
} else {
Row->CharRow.OldRight = PrevRow->CharRow.Right;
Row->CharRow.OldLeft = PrevRow->CharRow.Left;
}
}
SHORT
ScrollEntireScreen(
IN PSCREEN_INFORMATION ScreenInfo,
IN SHORT ScrollValue,
IN BOOL UpdateRowIndex
)
/**++
this routine updates FirstRow and all the OldLeft and OldRight
values when the screen is scrolled up by ScrollValue.
--*/
{
SHORT RowIndex;
int i;
int new;
int old;
ScreenInfo->BufferInfo.TextInfo.Flags |= TEXT_VALID_HINT;
//
// store index of first row
//
RowIndex = ScreenInfo->BufferInfo.TextInfo.FirstRow;
//
// update the oldright and oldleft values
//
new = (RowIndex + ScreenInfo->Window.Bottom + ScrollValue) %
ScreenInfo->ScreenBufferSize.Y;
old = (RowIndex + ScreenInfo->Window.Bottom) %
ScreenInfo->ScreenBufferSize.Y;
for (i = WINDOW_SIZE_Y(&ScreenInfo->Window) - 1; i >= 0; i--) {
CopyRow(
&ScreenInfo->BufferInfo.TextInfo.Rows[new],
&ScreenInfo->BufferInfo.TextInfo.Rows[old]);
if (--new < 0)
new = ScreenInfo->ScreenBufferSize.Y - 1;
if (--old < 0)
old = ScreenInfo->ScreenBufferSize.Y - 1;
}
//
// update screen buffer
//
if (UpdateRowIndex) {
ScreenInfo->BufferInfo.TextInfo.FirstRow =
(SHORT)((RowIndex + ScrollValue) % ScreenInfo->ScreenBufferSize.Y);
}
return RowIndex;
}
VOID
StreamScrollRegion(
IN PSCREEN_INFORMATION ScreenInfo
)
/*++
Routine Description:
This routine is a special-purpose scroll for use by
AdjustCursorPosition.
Arguments:
ScreenInfo - pointer to screen buffer info.
Return Value:
--*/
{
SHORT RowIndex;
PROW Row;
PWCHAR Char;
RECT Rect;
RECT BoundingBox;
int ScreenWidth,ScrollHeight,ScreenHeight;
COORD FontSize;
SMALL_RECT UpdateRegion;
BOOL Success;
int i;
PCONSOLE_INFORMATION Console = ScreenInfo->Console;
RowIndex = ScrollEntireScreen(ScreenInfo,1,TRUE);
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
//
// fill line with blanks
//
Char = &Row->CharRow.Chars[Row->CharRow.Left];
for (i=Row->CharRow.Left;i<Row->CharRow.Right;i++) {
*Char = (WCHAR)' ';
Char++;
}
Row->CharRow.Right = 0;
Row->CharRow.Left = ScreenInfo->ScreenBufferSize.X;
//
// set up attributes
//
if (Row->AttrRow.Length != 1) {
HeapFree(pConHeap,0,Row->AttrRow.Attrs);
Row->AttrRow.Attrs = &Row->AttrRow.AttrPair;
Row->AttrRow.AttrPair.Length = ScreenInfo->ScreenBufferSize.X;
Row->AttrRow.Length = 1;
}
Row->AttrRow.AttrPair.Attr = ScreenInfo->Attributes;
//
// update screen
//
if (ACTIVE_SCREEN_BUFFER(ScreenInfo) &&
Console->FullScreenFlags == 0 &&
!(Console->Flags & CONSOLE_IS_ICONIC)) {
ConsoleHideCursor(ScreenInfo);
if (UsePolyTextOut) {
WriteRegionToScreen(ScreenInfo, &ScreenInfo->Window);
} else {
FontSize = SCR_FONTSIZE(ScreenInfo);
ScreenWidth = WINDOW_SIZE_X(&ScreenInfo->Window) * FontSize.X;
ScreenHeight = WINDOW_SIZE_Y(&ScreenInfo->Window) * FontSize.Y;
ScrollHeight = ScreenHeight - FontSize.Y;
Rect.left = 0;
Rect.right = ScreenWidth;
Rect.top = FontSize.Y;
Rect.bottom = ScreenHeight;
//
// find smallest bounding rectangle
//
if (ScreenInfo->BufferInfo.TextInfo.Flags & TEXT_VALID_HINT) {
SHORT MinLeft,MaxRight;
MinLeft = ScreenInfo->ScreenBufferSize.X;
MaxRight = 0;
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+ScreenInfo->Window.Top) % ScreenInfo->ScreenBufferSize.Y;
for (i=ScreenInfo->Window.Top+1;i<=ScreenInfo->Window.Bottom;i++) {
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
if (Row->CharRow.OldLeft == INVALID_OLD_LENGTH) {
MinLeft = 0;
} else {
if (MinLeft > min(Row->CharRow.Left,Row->CharRow.OldLeft)) {
MinLeft = min(Row->CharRow.Left,Row->CharRow.OldLeft);
}
}
if (Row->CharRow.OldRight == INVALID_OLD_LENGTH) {
MaxRight = ScreenInfo->ScreenBufferSize.X-1;
} else {
if (MaxRight < max(Row->CharRow.Right,Row->CharRow.OldRight)) {
MaxRight = max(Row->CharRow.Right,Row->CharRow.OldRight);
}
}
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
RowIndex = 0;
}
}
Rect.left = MinLeft*FontSize.X;
Rect.right = (MaxRight+1)*FontSize.X;
}
LockScrollBuffer();
ASSERT (ScreenInfo->BufferInfo.TextInfo.UpdatingScreen>0);
Success = (int)ScrollDC(Console->hDC,
0,
-FontSize.Y,
&Rect,
NULL,
ghrgnScroll,
NULL
);
if (Success && ScreenInfo->Window.Top!=ScreenInfo->Window.Bottom) {
switch (GetRgnBox(ghrgnScroll, &BoundingBox)) {
case SIMPLEREGION:
if (BoundingBox.left == 0 &&
BoundingBox.right == ScreenWidth &&
BoundingBox.top == ScrollHeight &&
BoundingBox.bottom == ScreenHeight) {
POLYPATBLT PolyData;
PolyData.x = 0;
PolyData.y = ScrollHeight;
PolyData.cx = ScreenWidth;
PolyData.cy = FontSize.Y;
PolyData.BrClr.hbr = ScreenInfo->hBackground;
PolyPatBlt(Console->hDC,PATCOPY,&PolyData,1,PPB_BRUSH);
GdiFlush();
} else {
UpdateRegion.Left = (SHORT)((BoundingBox.left/FontSize.X)+ScreenInfo->Window.Left);
UpdateRegion.Right = (SHORT)(((BoundingBox.right-1)/FontSize.X)+ScreenInfo->Window.Left);
UpdateRegion.Top = (SHORT)((BoundingBox.top/FontSize.Y)+ScreenInfo->Window.Top);
UpdateRegion.Bottom = (SHORT)(((BoundingBox.bottom-1)/FontSize.Y)+ScreenInfo->Window.Top);
WriteToScreen(ScreenInfo,&UpdateRegion);
}
break;
case COMPLEXREGION:
UpdateComplexRegion(ScreenInfo,FontSize);
break;
}
} else {
WriteToScreen(ScreenInfo,&ScreenInfo->Window);
}
UnlockScrollBuffer();
}
ConsoleShowCursor(ScreenInfo);
}
#ifdef i386
else if (Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
SMALL_RECT ScrollRect;
COORD TargetPoint;
ScrollRect = ScreenInfo->Window;
TargetPoint.Y = ScrollRect.Top;
ScrollRect.Top += 1;
TargetPoint.X = 0;
ScrollHW(ScreenInfo,
&ScrollRect,
NULL,
TargetPoint
);
ScrollRect.Top = ScrollRect.Bottom - 1;
WriteRegionToScreenHW(ScreenInfo,&ScrollRect);
}
#endif
}
NTSTATUS
ScrollRegion(
IN PSCREEN_INFORMATION ScreenInfo,
IN OUT PSMALL_RECT ScrollRectangle,
IN PSMALL_RECT ClipRectangle OPTIONAL,
IN COORD DestinationOrigin,
IN PCHAR_INFO Fill
)
/*++
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 - pointer to screen buffer info.
ScrollRectangle - Region to copy
ClipRectangle - Optional pointer to clip region.
DestinationOrigin - Upper left corner of target region.
Fill - Character and attribute to fill source region with.
Return Value:
--*/
{
SMALL_RECT TargetRectangle, SourceRectangle;
COORD TargetPoint;
COORD Size;
SMALL_RECT OurClipRectangle;
SMALL_RECT ScrollRectangle2,ScrollRectangle3;
NTSTATUS Status;
PCONSOLE_INFORMATION Console = ScreenInfo->Console;
// here's how we clip:
//
// Clip source rectangle to screen buffer => S
// Create target rectangle based on S => T
// Clip T to ClipRegion => T
// Create S2 based on clipped T => S2
// Clip S to ClipRegion => S3
//
// S2 is the region we copy to T
// S3 is the region to fill
if (Fill->Char.UnicodeChar == '\0' && Fill->Attributes == 0) {
Fill->Char.UnicodeChar = (WCHAR)' ';
Fill->Attributes = ScreenInfo->Attributes;
}
//
// clip the source rectangle to the screen buffer
//
if (ScrollRectangle->Left < 0) {
DestinationOrigin.X += -ScrollRectangle->Left;
ScrollRectangle->Left = 0;
}
if (ScrollRectangle->Top < 0) {
DestinationOrigin.Y += -ScrollRectangle->Top;
ScrollRectangle->Top = 0;
}
if (ScrollRectangle->Right >= ScreenInfo->ScreenBufferSize.X) {
ScrollRectangle->Right = (SHORT)(ScreenInfo->ScreenBufferSize.X-1);
}
if (ScrollRectangle->Bottom >= ScreenInfo->ScreenBufferSize.Y) {
ScrollRectangle->Bottom = (SHORT)(ScreenInfo->ScreenBufferSize.Y-1);
}
//
// if source rectangle doesn't intersect screen buffer, return.
//
if (ScrollRectangle->Bottom < ScrollRectangle->Top ||
ScrollRectangle->Right < ScrollRectangle->Left) {
return STATUS_SUCCESS;
}
//
// clip the target rectangle
// if a cliprectangle was provided, clip it to the screen buffer.
// if not, set the cliprectangle to the screen buffer region.
//
if (ClipRectangle) {
//
// clip the cliprectangle.
//
if (ClipRectangle->Left < 0) {
ClipRectangle->Left = 0;
}
if (ClipRectangle->Top < 0) {
ClipRectangle->Top = 0;
}
if (ClipRectangle->Right >= ScreenInfo->ScreenBufferSize.X) {
ClipRectangle->Right = (SHORT)(ScreenInfo->ScreenBufferSize.X-1);
}
if (ClipRectangle->Bottom >= ScreenInfo->ScreenBufferSize.Y) {
ClipRectangle->Bottom = (SHORT)(ScreenInfo->ScreenBufferSize.Y-1);
}
}
else {
OurClipRectangle.Left = 0;
OurClipRectangle.Top = 0;
OurClipRectangle.Right = (SHORT)(ScreenInfo->ScreenBufferSize.X-1);
OurClipRectangle.Bottom = (SHORT)(ScreenInfo->ScreenBufferSize.Y-1);
ClipRectangle = &OurClipRectangle;
}
//
// Create target rectangle based on S => T
// Clip T to ClipRegion => T
// Create S2 based on clipped T => S2
//
ScrollRectangle2 = *ScrollRectangle;
TargetRectangle.Left = DestinationOrigin.X;
TargetRectangle.Top = DestinationOrigin.Y;
TargetRectangle.Right = (SHORT)(DestinationOrigin.X + (ScrollRectangle2.Right - ScrollRectangle2.Left + 1) - 1);
TargetRectangle.Bottom = (SHORT)(DestinationOrigin.Y + (ScrollRectangle2.Bottom - ScrollRectangle2.Top + 1) - 1);
if (TargetRectangle.Left < ClipRectangle->Left) {
ScrollRectangle2.Left += ClipRectangle->Left - TargetRectangle.Left;
TargetRectangle.Left = ClipRectangle->Left;
}
if (TargetRectangle.Top < ClipRectangle->Top) {
ScrollRectangle2.Top += ClipRectangle->Top - TargetRectangle.Top;
TargetRectangle.Top = ClipRectangle->Top;
}
if (TargetRectangle.Right > ClipRectangle->Right) {
ScrollRectangle2.Right -= TargetRectangle.Right - ClipRectangle->Right;
TargetRectangle.Right = ClipRectangle->Right;
}
if (TargetRectangle.Bottom > ClipRectangle->Bottom) {
ScrollRectangle2.Bottom -= TargetRectangle.Bottom - ClipRectangle->Bottom;
TargetRectangle.Bottom = ClipRectangle->Bottom;
}
//
// clip scroll rect to clipregion => S3
//
ScrollRectangle3 = *ScrollRectangle;
if (ScrollRectangle3.Left < ClipRectangle->Left) {
ScrollRectangle3.Left = ClipRectangle->Left;
}
if (ScrollRectangle3.Top < ClipRectangle->Top) {
ScrollRectangle3.Top = ClipRectangle->Top;
}
if (ScrollRectangle3.Right > ClipRectangle->Right) {
ScrollRectangle3.Right = ClipRectangle->Right;
}
if (ScrollRectangle3.Bottom > ClipRectangle->Bottom) {
ScrollRectangle3.Bottom = ClipRectangle->Bottom;
}
ConsoleHideCursor(ScreenInfo);
//
// if target rectangle doesn't intersect screen buffer, skip scrolling
// part.
//
if (!(TargetRectangle.Bottom < TargetRectangle.Top ||
TargetRectangle.Right < TargetRectangle.Left)) {
//
// if we can, don't use intermediate scroll region buffer. do this
// by figuring out fill rectangle. NOTE: this code will only work
// if CopyRectangle copies from low memory to high memory (otherwise
// we would overwrite the scroll region before reading it).
//
if (ScrollRectangle2.Right == TargetRectangle.Right &&
ScrollRectangle2.Left == TargetRectangle.Left &&
ScrollRectangle2.Top > TargetRectangle.Top &&
ScrollRectangle2.Top < TargetRectangle.Bottom) {
SMALL_RECT FillRect;
SHORT LastRowIndex,OldRight,OldLeft;
PROW Row;
TargetPoint.X = TargetRectangle.Left;
TargetPoint.Y = TargetRectangle.Top;
if (ScrollRectangle2.Right == (SHORT)(ScreenInfo->ScreenBufferSize.X-1) &&
ScrollRectangle2.Left == 0 &&
ScrollRectangle2.Bottom == (SHORT)(ScreenInfo->ScreenBufferSize.Y-1) &&
ScrollRectangle2.Top == 1 ) {
LastRowIndex = ScrollEntireScreen(ScreenInfo,(SHORT)(ScrollRectangle2.Top-TargetRectangle.Top),TRUE);
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[LastRowIndex];
OldRight = Row->CharRow.OldRight;
OldLeft = Row->CharRow.OldLeft;
} else {
LastRowIndex = -1;
CopyRectangle(ScreenInfo,
&ScrollRectangle2,
TargetPoint
);
}
FillRect.Left = TargetRectangle.Left;
FillRect.Right = TargetRectangle.Right;
FillRect.Top = (SHORT)(TargetRectangle.Bottom+1);
FillRect.Bottom = ScrollRectangle->Bottom;
if (FillRect.Top < ClipRectangle->Top) {
FillRect.Top = ClipRectangle->Top;
}
if (FillRect.Bottom > ClipRectangle->Bottom) {
FillRect.Bottom = ClipRectangle->Bottom;
}
FillRectangle(Fill,
ScreenInfo,
&FillRect
);
//
// After ScrollEntireScreen, the OldRight and OldLeft values
// for the last row are set correctly. however, FillRectangle
// resets them with the previous first row of the screen.
// reset them here.
//
if (LastRowIndex != -1) {
Row->CharRow.OldRight = OldRight;
Row->CharRow.OldLeft = OldLeft;
}
//
// update to screen, if we're not iconic. we're marked as
// iconic if we're fullscreen, so check for fullscreen.
//
if (!(Console->Flags & CONSOLE_IS_ICONIC) ||
Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
ScrollScreen(ScreenInfo,
&ScrollRectangle2,
&FillRect,
TargetPoint
);
}
}
//
// if no overlap, don't need intermediate copy
//
else if (ScrollRectangle3.Right < TargetRectangle.Left ||
ScrollRectangle3.Left > TargetRectangle.Right ||
ScrollRectangle3.Top > TargetRectangle.Bottom ||
ScrollRectangle3.Bottom < TargetRectangle.Top) {
TargetPoint.X = TargetRectangle.Left;
TargetPoint.Y = TargetRectangle.Top;
CopyRectangle(ScreenInfo,
&ScrollRectangle2,
TargetPoint
);
FillRectangle(Fill,
ScreenInfo,
&ScrollRectangle3
);
//
// update to screen, if we're not iconic. we're marked as
// iconic if we're fullscreen, so check for fullscreen.
//
if (!(Console->Flags & CONSOLE_IS_ICONIC) ||
Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
ScrollScreen(ScreenInfo,
&ScrollRectangle2,
&ScrollRectangle3,
TargetPoint
);
}
}
//
// for the case where the source and target rectangles overlap, we
// copy the source rectangle, fill it, then copy it to the target.
//
else {
SMALL_RECT TargetRect;
COORD SourcePoint;
LockScrollBuffer();
Size.X = (SHORT)(ScrollRectangle2.Right - ScrollRectangle2.Left + 1);
Size.Y = (SHORT)(ScrollRectangle2.Bottom - ScrollRectangle2.Top + 1);
if (ScrollBufferSize < (Size.X * Size.Y * sizeof(CHAR_INFO))) {
FreeScrollBuffer();
Status = AllocateScrollBuffer(Size.X * Size.Y * sizeof(CHAR_INFO));
if (!NT_SUCCESS(Status)) {
UnlockScrollBuffer();
ConsoleShowCursor(ScreenInfo);
return Status;
}
}
TargetRect.Left = 0;
TargetRect.Top = 0;
TargetRect.Right = ScrollRectangle2.Right - ScrollRectangle2.Left;
TargetRect.Bottom = ScrollRectangle2.Bottom - ScrollRectangle2.Top;
SourcePoint.X = ScrollRectangle2.Left;
SourcePoint.Y = ScrollRectangle2.Top;
ReadRectFromScreenBuffer(ScreenInfo,
SourcePoint,
ScrollBuffer,
Size,
&TargetRect
);
FillRectangle(Fill,
ScreenInfo,
&ScrollRectangle3
);
SourceRectangle.Top = 0;
SourceRectangle.Left = 0;
SourceRectangle.Right = (SHORT)(Size.X-1);
SourceRectangle.Bottom = (SHORT)(Size.Y-1);
TargetPoint.X = TargetRectangle.Left;
TargetPoint.Y = TargetRectangle.Top;
WriteRectToScreenBuffer((PBYTE)ScrollBuffer,
Size,
&SourceRectangle,
ScreenInfo,
TargetPoint,
0xFFFFFFFF
);
UnlockScrollBuffer();
//
// update to screen, if we're not iconic. we're marked as
// iconic if we're fullscreen, so check for fullscreen.
//
if (!(Console->Flags & CONSOLE_IS_ICONIC) ||
Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
//
// update regions on screen.
//
ScrollScreen(ScreenInfo,
&ScrollRectangle2,
&ScrollRectangle3,
TargetPoint
);
}
}
}
else {
//
// do fill
//
FillRectangle(Fill,
ScreenInfo,
&ScrollRectangle3
);
//
// update to screen, if we're not iconic. we're marked as
// iconic if we're fullscreen, so check for fullscreen.
//
if (ACTIVE_SCREEN_BUFFER(ScreenInfo) &&
!(Console->Flags & CONSOLE_IS_ICONIC) ||
Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
WriteToScreen(ScreenInfo,&ScrollRectangle3);
}
}
ConsoleShowCursor(ScreenInfo);
return STATUS_SUCCESS;
}
NTSTATUS
SetWindowOrigin(
IN PSCREEN_INFORMATION ScreenInfo,
IN BOOLEAN Absolute,
IN COORD WindowOrigin
)
/*++
Routine Description:
This routine sets the window origin.
Arguments:
ScreenInfo - pointer to screen buffer info.
Absolute - if TRUE, WindowOrigin is specified in absolute screen
buffer coordinates. if FALSE, WindowOrigin is specified in coordinates
relative to the current window origin.
WindowOrigin - New window origin.
Return Value:
--*/
{
SMALL_RECT NewWindow;
COORD WindowSize;
RECT BoundingBox;
BOOL Success;
RECT ScrollRect;
SMALL_RECT UpdateRegion;
COORD FontSize;
PCONSOLE_INFORMATION Console = ScreenInfo->Console;
//
// calculate window size
//
WindowSize.X = (SHORT)CONSOLE_WINDOW_SIZE_X(ScreenInfo);
WindowSize.Y = (SHORT)CONSOLE_WINDOW_SIZE_Y(ScreenInfo);
//
// if relative coordinates, figure out absolute coords.
//
if (!Absolute) {
if (WindowOrigin.X == 0 && WindowOrigin.Y == 0) {
return STATUS_SUCCESS;
}
NewWindow.Left = ScreenInfo->Window.Left + WindowOrigin.X;
NewWindow.Top = ScreenInfo->Window.Top + WindowOrigin.Y;
}
else {
if (WindowOrigin.X == ScreenInfo->Window.Left &&
WindowOrigin.Y == ScreenInfo->Window.Top) {
return STATUS_SUCCESS;
}
NewWindow.Left = WindowOrigin.X;
NewWindow.Top = WindowOrigin.Y;
}
NewWindow.Right = (SHORT)(NewWindow.Left + WindowSize.X - 1);
NewWindow.Bottom = (SHORT)(NewWindow.Top + WindowSize.Y - 1);
//
// see if new window origin would extend window beyond extent of screen
// buffer
//
if (NewWindow.Left < 0 || NewWindow.Top < 0 ||
NewWindow.Right >= ScreenInfo->ScreenBufferSize.X ||
NewWindow.Bottom >= ScreenInfo->ScreenBufferSize.Y) {
return STATUS_INVALID_PARAMETER;
}
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
FontSize = SCR_FONTSIZE(ScreenInfo);
ScreenInfo->BufferInfo.TextInfo.Flags &= ~TEXT_VALID_HINT;
} else {
FontSize.X = 1;
FontSize.Y = 1;
}
ConsoleHideCursor(ScreenInfo);
if (ACTIVE_SCREEN_BUFFER(ScreenInfo) &&
Console->FullScreenFlags == 0 &&
!(Console->Flags & CONSOLE_IS_ICONIC)) {
InvertSelection(Console, TRUE);
if ( ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER
&& UsePolyTextOut
&& NewWindow.Left == ScreenInfo->Window.Left
) {
ScrollEntireScreen(ScreenInfo,
(SHORT)(NewWindow.Top - ScreenInfo->Window.Top),
FALSE);
ScreenInfo->Window = NewWindow;
WriteRegionToScreen(ScreenInfo, &NewWindow);
} else {
ScrollRect.left = 0;
ScrollRect.right = CONSOLE_WINDOW_SIZE_X(ScreenInfo)*FontSize.X;
ScrollRect.top = 0;
ScrollRect.bottom = CONSOLE_WINDOW_SIZE_Y(ScreenInfo)*FontSize.Y;
SCROLLDC_CALL;
Success = ScrollDC(Console->hDC,
(ScreenInfo->Window.Left-NewWindow.Left)*FontSize.X,
(ScreenInfo->Window.Top-NewWindow.Top)*FontSize.Y,
&ScrollRect,
NULL,
NULL,
&BoundingBox
);
if (Success) {
UpdateRegion.Left = (SHORT)((BoundingBox.left/FontSize.X)+NewWindow.Left);
UpdateRegion.Right = (SHORT)(((BoundingBox.right-1)/FontSize.X)+NewWindow.Left);
UpdateRegion.Top = (SHORT)((BoundingBox.top/FontSize.Y)+NewWindow.Top);
UpdateRegion.Bottom = (SHORT)(((BoundingBox.bottom-1)/FontSize.Y)+NewWindow.Top);
}
else {
UpdateRegion = NewWindow;
}
//
// new window is ok. store it in screeninfo and refresh screen.
//
ScreenInfo->Window = NewWindow;
WriteToScreen(ScreenInfo,&UpdateRegion);
}
InvertSelection(Console, FALSE);
}
#ifdef i386
else if (Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE &&
ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
//
// keep mouse pointer on screen
//
if (ScreenInfo->BufferInfo.TextInfo.MousePosition.X < NewWindow.Left) {
ScreenInfo->BufferInfo.TextInfo.MousePosition.X = NewWindow.Left;
} else if (ScreenInfo->BufferInfo.TextInfo.MousePosition.X > NewWindow.Right) {
ScreenInfo->BufferInfo.TextInfo.MousePosition.X = NewWindow.Right;
}
if (ScreenInfo->BufferInfo.TextInfo.MousePosition.Y < NewWindow.Top) {
ScreenInfo->BufferInfo.TextInfo.MousePosition.Y = NewWindow.Top;
} else if (ScreenInfo->BufferInfo.TextInfo.MousePosition.Y > NewWindow.Bottom) {
ScreenInfo->BufferInfo.TextInfo.MousePosition.Y = NewWindow.Bottom;
}
ScreenInfo->Window = NewWindow;
WriteToScreen(ScreenInfo,&ScreenInfo->Window);
}
#endif
else {
// we're iconic
ScreenInfo->Window = NewWindow;
}
ConsoleShowCursor(ScreenInfo);
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
ScreenInfo->BufferInfo.TextInfo.Flags |= TEXT_VALID_HINT;
}
UpdateScrollBars(ScreenInfo);
return STATUS_SUCCESS;
}
NTSTATUS
ResizeWindow(
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT WindowDimensions,
IN BOOL DoScrollBarUpdate
)
/*++
Routine Description:
This routine changes the console data structures to reflect the specified
window size change. it does not call the user component to update
the screen.
Arguments:
ScreenInformation - the new screen buffer.
dwWindowSize - the initial size of screen buffer's window.
nFont - the initial font to generate text with.
dwScreenBufferSize - the initial size of the screen buffer.
Return Value:
--*/
{
SMALL_RECT RelativeChange;
RelativeChange.Left = ScreenInfo->Window.Left - WindowDimensions->Left;
RelativeChange.Right = ScreenInfo->Window.Right - WindowDimensions->Right;
RelativeChange.Top = ScreenInfo->Window.Top - WindowDimensions->Top;
RelativeChange.Bottom = ScreenInfo->Window.Bottom - WindowDimensions->Bottom;
if (RelativeChange.Left == 0 &&
RelativeChange.Right == 0 &&
RelativeChange.Top == 0 &&
RelativeChange.Bottom == 0) {
return STATUS_SUCCESS;
}
if (WindowDimensions->Left < 0) {
WindowDimensions->Right -= WindowDimensions->Left;
WindowDimensions->Left = 0;
}
if (WindowDimensions->Top < 0) {
WindowDimensions->Bottom -= WindowDimensions->Top;
WindowDimensions->Top = 0;
}
if (WindowDimensions->Right >= ScreenInfo->ScreenBufferSize.X) {
WindowDimensions->Right = ScreenInfo->ScreenBufferSize.X;
}
if (WindowDimensions->Bottom >= ScreenInfo->ScreenBufferSize.Y) {
WindowDimensions->Bottom = ScreenInfo->ScreenBufferSize.Y;
}
ScreenInfo->Window = *WindowDimensions;
ScreenInfo->WindowMaximizedX = (ScreenInfo->Window.Left == 0 &&
(SHORT)(ScreenInfo->Window.Right+1) == ScreenInfo->ScreenBufferSize.X);
ScreenInfo->WindowMaximizedY = (ScreenInfo->Window.Top == 0 &&
(SHORT)(ScreenInfo->Window.Bottom+1) == ScreenInfo->ScreenBufferSize.Y);
if (DoScrollBarUpdate) {
UpdateScrollBars(ScreenInfo);
}
if (!(ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER)) {
return STATUS_SUCCESS;
}
if (ACTIVE_SCREEN_BUFFER(ScreenInfo)) {
ScreenInfo->BufferInfo.TextInfo.Flags &= ~TEXT_VALID_HINT;
}
#ifdef i386
if (ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
//
// keep mouse pointer on screen
//
if (ScreenInfo->BufferInfo.TextInfo.MousePosition.X < WindowDimensions->Left) {
ScreenInfo->BufferInfo.TextInfo.MousePosition.X = WindowDimensions->Left;
} else if (ScreenInfo->BufferInfo.TextInfo.MousePosition.X > WindowDimensions->Right) {
ScreenInfo->BufferInfo.TextInfo.MousePosition.X = WindowDimensions->Right;
}
if (ScreenInfo->BufferInfo.TextInfo.MousePosition.Y < WindowDimensions->Top) {
ScreenInfo->BufferInfo.TextInfo.MousePosition.Y = WindowDimensions->Top;
} else if (ScreenInfo->BufferInfo.TextInfo.MousePosition.Y > WindowDimensions->Bottom) {
ScreenInfo->BufferInfo.TextInfo.MousePosition.Y = WindowDimensions->Bottom;
}
}
#endif
return(STATUS_SUCCESS);
}
VOID
SetWindowSize(
IN PSCREEN_INFORMATION ScreenInfo
)
{
if (ScreenInfo->Console->Flags & CONSOLE_SETTING_WINDOW_SIZE)
return;
ScreenInfo->Console->Flags |= CONSOLE_SETTING_WINDOW_SIZE;
PostMessage(ScreenInfo->Console->hWnd,
CM_SET_WINDOW_SIZE,
(DWORD)ScreenInfo,
0x47474747
);
}
VOID
UpdateWindowSize(
IN PCONSOLE_INFORMATION Console,
IN PSCREEN_INFORMATION ScreenInfo
)
{
LONG WindowStyle;
if (!(Console->Flags & CONSOLE_IS_ICONIC)) {
InternalUpdateScrollBars(ScreenInfo);
WindowStyle = GetWindowLong(Console->hWnd, GWL_STYLE);
if (ScreenInfo->WindowMaximized) {
WindowStyle |= WS_MAXIMIZE;
} else {
WindowStyle &= ~WS_MAXIMIZE;
}
SetWindowLong(Console->hWnd, GWL_STYLE, WindowStyle);
SetWindowPos(Console->hWnd, NULL,
0,
0,
Console->WindowRect.right-Console->WindowRect.left,
Console->WindowRect.bottom-Console->WindowRect.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME
);
Console->ResizeFlags &= ~SCREEN_BUFFER_CHANGE;
} else {
Console->ResizeFlags |= SCREEN_BUFFER_CHANGE;
}
}
NTSTATUS
InternalSetWindowSize(
IN PCONSOLE_INFORMATION Console,
IN PSCREEN_INFORMATION ScreenInfo,
IN PSMALL_RECT Window
)
{
RECT WindowSize;
WORD WindowSizeX, WindowSizeY;
Console->Flags &= ~CONSOLE_SETTING_WINDOW_SIZE;
if (Console->CurrentScreenBuffer == ScreenInfo) {
if (Console->FullScreenFlags == 0) {
//
// Make sure our max screen sizes reflect reality
//
UpdateScreenSizes(ScreenInfo, ScreenInfo->ScreenBufferSize);
//
// figure out how big to make the window, given the desired client area
// size.
//
ScreenInfo->ResizingWindow++;
WindowSizeX = (SHORT)(Window->Right - Window->Left + 1);
WindowSizeY = (SHORT)(Window->Bottom - Window->Top + 1);
WindowSize.left = 0;
WindowSize.top = 0;
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
WindowSize.right = WindowSizeX*SCR_FONTSIZE(ScreenInfo).X;
WindowSize.bottom = WindowSizeY*SCR_FONTSIZE(ScreenInfo).Y;
} else {
WindowSize.right = WindowSizeX;
WindowSize.bottom = WindowSizeY;
}
WindowSize.right += VerticalClientToWindow;
WindowSize.bottom += HorizontalClientToWindow;
if (WindowSizeY != 0) {
if (!ScreenInfo->WindowMaximizedX) {
WindowSize.bottom += HorizontalScrollSize;
}
if (!ScreenInfo->WindowMaximizedY) {
WindowSize.right += VerticalScrollSize;
}
}
WindowSize.left += Console->WindowRect.left;
WindowSize.right += Console->WindowRect.left;
WindowSize.top += Console->WindowRect.top;
WindowSize.bottom += Console->WindowRect.top;
Console->WindowRect = WindowSize;
UpdateWindowSize(Console,ScreenInfo);
ScreenInfo->ResizingWindow--;
} else if (Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
WriteToScreen(ScreenInfo,&ScreenInfo->Window);
}
}
return STATUS_SUCCESS;
}
NTSTATUS
SetActiveScreenBuffer(
IN PSCREEN_INFORMATION ScreenInfo
)
{
PSCREEN_INFORMATION OldScreenInfo;
PCONSOLE_INFORMATION Console = ScreenInfo->Console;
OldScreenInfo = Console->CurrentScreenBuffer;
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
#if !defined(_X86_)
if (Console->FullScreenFlags & CONSOLE_FULLSCREEN) {
return STATUS_INVALID_PARAMETER;
}
#endif
Console->CurrentScreenBuffer = ScreenInfo;
if (Console->FullScreenFlags == 0) {
//
// initialize cursor
//
ScreenInfo->BufferInfo.TextInfo.CursorOn = FALSE;
//
// set font
//
SetFont(ScreenInfo);
}
#if defined(_X86_)
else if (Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
if (!(Console->Flags & CONSOLE_VDM_REGISTERED)) {
if ( (!(OldScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER)) ||
(OldScreenInfo->BufferInfo.TextInfo.ModeIndex!=ScreenInfo->BufferInfo.TextInfo.ModeIndex)) {
// set video mode and font
SetVideoMode(ScreenInfo);
}
//set up cursor
SetCursorInformationHW(ScreenInfo,
ScreenInfo->BufferInfo.TextInfo.CursorSize,
ScreenInfo->BufferInfo.TextInfo.CursorVisible);
SetCursorPositionHW(ScreenInfo,
ScreenInfo->BufferInfo.TextInfo.CursorPosition);
}
}
#endif
}
else {
Console->CurrentScreenBuffer = ScreenInfo;
}
//
// empty input buffer
//
FlushAllButKeys(&Console->InputBuffer);
if (Console->FullScreenFlags == 0) {
SelectObject(Console->hDC,ScreenInfo->hBackground);
//
// set window size
//
SetWindowSize(ScreenInfo);
//
// initialize the palette, if we have the focus and we're not fullscreen
//
if (!(Console->Flags & CONSOLE_IS_ICONIC) &&
Console->FullScreenFlags == 0) {
if (ScreenInfo->hPalette != NULL || OldScreenInfo->hPalette != NULL) {
HPALETTE hPalette;
BOOL bReset = FALSE;
if (GetCurrentThreadId() != Console->InputThreadInfo->ThreadId) {
bReset = TRUE;
NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseDesktop,
&Console->InputThreadInfo->ThreadHandle,
sizeof(HANDLE));
}
if (ScreenInfo->hPalette == NULL) {
hPalette = Console->hSysPalette;
} else {
hPalette = ScreenInfo->hPalette;
}
SelectPalette(Console->hDC,
hPalette,
FALSE);
SetActivePalette(ScreenInfo);
if (bReset == TRUE) {
HANDLE hNull = NULL;
NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseDesktop, &hNull, sizeof(HANDLE));
}
}
}
}
//
// write data to screen
//
ScreenInfo->BufferInfo.TextInfo.Flags &= ~TEXT_VALID_HINT;
WriteToScreen(ScreenInfo,&ScreenInfo->Window);
return STATUS_SUCCESS;
}
VOID
ModifyConsoleProcessFocus(
IN PCONSOLE_INFORMATION Console,
IN int Priority
)
{
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
PLIST_ENTRY ListHead, ListNext;
ListHead = &Console->ProcessHandleList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
ProcessHandleRecord = CONTAINING_RECORD( ListNext, CONSOLE_PROCESS_HANDLE, ListLink );
ListNext = ListNext->Flink;
{
if ( Priority == NORMAL_BASE_PRIORITY ) {
CsrSetBackgroundPriority(ProcessHandleRecord->Process);
}
else {
CsrSetForegroundPriority(ProcessHandleRecord->Process);
}
}
}
}
VOID
TrimConsoleWorkingSet(
IN PCONSOLE_INFORMATION Console
)
{
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
PLIST_ENTRY ListHead, ListNext;
ListHead = &Console->ProcessHandleList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
ProcessHandleRecord = CONTAINING_RECORD( ListNext, CONSOLE_PROCESS_HANDLE, ListLink );
ListNext = ListNext->Flink;
{
SetProcessWorkingSetSize(ProcessHandleRecord->Process->ProcessHandle,(DWORD)-1,(DWORD)-1);
}
}
}
VOID
LockNextConsole(
IN PCONSOLE_INFORMATION Console,
IN HWND hWnd)
{
PCONSOLE_INFORMATION ConsoleNext;
//
// If this console isn't being destroyed, don't do anything
//
if (!Console || !(Console->Flags & CONSOLE_IN_DESTRUCTION)) {
return;
}
//
// If we've already locked a console, don't do anything
//
if (Console->ConsoleNext != NULL) {
return;
}
//
// If it's not a valid window handle, don't do anything
//
if (hWnd == NULL) {
return;
}
//
// If it's not a console window, don't do anything
//
ConsoleNext = (PCONSOLE_INFORMATION)GetWindowLong(hWnd, GWL_USERDATA);
if (!(NT_SUCCESS(ValidateConsole(ConsoleNext)))) {
return;
}
//
// If it's terminating, don't do anything
//
if (ConsoleNext->Flags & CONSOLE_TERMINATING) {
return;
}
//
// Lock the next console so it won't be able to process focus
// events during window destruction. This prevents a potential
// deadlock shutting down console apps.
//
Console->ConsoleNext = ConsoleNext;
LockConsole(Console->ConsoleNext);
}
VOID
UnlockNextConsole(
IN PCONSOLE_INFORMATION Console)
{
//
// If this console isn't being destroyed, don't do anything
//
if (!Console || !(Console->Flags & CONSOLE_IN_DESTRUCTION)) {
return;
}
//
// Unlock the console behind us, if there is one and it's not already
// terminating
//
if (Console->ConsoleNext) {
if (!(Console->ConsoleNext->Flags & CONSOLE_TERMINATING)) {
UnlockConsole(Console->ConsoleNext);
}
Console->ConsoleNext = NULL;
}
}
VOID
AbortCreateConsole(
IN PCONSOLE_INFORMATION Console
)
{
//
// Signal any process waiting for us that initialization failed
//
NtSetEvent(Console->InitEvents[INITIALIZATION_FAILED],NULL);
//
// Now clean up the console structure
//
CloseHandle(Console->ClientThreadHandle);
FreeInputBuffer(&Console->InputBuffer);
HeapFree(pConHeap,0,Console->Title);
HeapFree(pConHeap,0,Console->OriginalTitle);
NtClose(Console->InitEvents[INITIALIZATION_SUCCEEDED]);
NtClose(Console->InitEvents[INITIALIZATION_FAILED]);
NtClose(Console->TerminationEvent);
FreeAliasBuffers(Console);
FreeCommandHistoryBuffers(Console);
FreeConsoleHandle(Console->ConsoleHandle);
RtlDeleteCriticalSection(&Console->ConsoleLock);
HeapFree(pConHeap,0,Console);
}
VOID
DestroyWindowsWindow(
IN PCONSOLE_INFORMATION Console,
IN HANDLE DestroyEvent
)
{
PSCREEN_INFORMATION Cur,Next;
HWND hWnd = Console->hWnd;
//
// Mark this window as being destroyed.
//
Console->Flags |= CONSOLE_IN_DESTRUCTION;
gnConsoleWindows--;
Console->InputThreadInfo->WindowCount--;
KillTimer(Console->hWnd,CURSOR_TIMER);
SelectObject(Console->hDC, GetStockObject(BLACK_BRUSH));
ReleaseDC(NULL, Console->hDC);
Console->hDC = NULL;
DestroyWindow(Console->hWnd);
Console->hWnd = NULL;
NtSetEvent(DestroyEvent,NULL);
//
// Clear out any keyboard messages we have stored away.
//
ClearKeyInfo(hWnd);
if (Console->hIcon != NULL && Console->hIcon != ghDefaultIcon) {
DestroyIcon(Console->hIcon);
}
//
// Unlock the console window behind us if there is one, so it
// gets a chance to process the focus events.
//
UnlockNextConsole(Console);
//
// must keep this thread handle around until after the destroywindow
// call so that impersonation will work.
//
CloseHandle(Console->ClientThreadHandle);
//
// once the sendmessage returns, there will be no more input to
// the console so we don't need to lock it.
// also, we've freed the console handle, so no apis may access the console.
//
//
// free screen buffers
//
for (Cur=Console->ScreenBuffers;Cur!=NULL;Cur=Next) {
Next = Cur->Next;
FreeScreenBuffer(Cur);
}
FreeAliasBuffers(Console);
FreeCommandHistoryBuffers(Console);
//
// free input buffer
//
FreeInputBuffer(&Console->InputBuffer);
HeapFree(pConHeap,0,Console->Title);
HeapFree(pConHeap,0,Console->OriginalTitle);
NtClose(Console->InitEvents[INITIALIZATION_SUCCEEDED]);
NtClose(Console->InitEvents[INITIALIZATION_FAILED]);
NtClose(Console->TerminationEvent);
if (Console->hWinSta != NULL) {
CloseDesktop(Console->hDesk);
CloseWindowStation(Console->hWinSta);
}
if (Console->VDMProcessHandle)
CloseHandle(Console->VDMProcessHandle);
ASSERT(!(Console->Flags & CONSOLE_VDM_REGISTERED));
/*if (Console->VDMBuffer != NULL) {
NtUnmapViewOfSection(NtCurrentProcess(),Console->VDMBuffer);
NtClose(Console->VDMBufferSectionHandle);
}*/
FreeConsoleHandle(Console->ConsoleHandle);
RtlDeleteCriticalSection(&Console->ConsoleLock);
HeapFree(pConHeap,0,Console);
}
void EndScroll(
IN PCONSOLE_INFORMATION Console)
{
}
VOID
VerticalScroll(
IN PCONSOLE_INFORMATION Console,
IN PSCREEN_INFORMATION ScreenInfo,
IN WORD ScrollCommand,
IN WORD AbsoluteChange
)
{
COORD NewOrigin;
NewOrigin.X = ScreenInfo->Window.Left;
NewOrigin.Y = ScreenInfo->Window.Top;
switch (ScrollCommand) {
case SB_LINEUP:
NewOrigin.Y--;
break;
case SB_LINEDOWN:
NewOrigin.Y++;
break;
case SB_PAGEUP:
NewOrigin.Y-=CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-1;
break;
case SB_PAGEDOWN:
NewOrigin.Y+=CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-1;
break;
case SB_THUMBTRACK:
Console->Flags |= CONSOLE_SCROLLBAR_TRACKING;
NewOrigin.Y= AbsoluteChange;
break;
case SB_THUMBPOSITION:
UnblockWriteConsole(Console, CONSOLE_SCROLLBAR_TRACKING);
break;
case SB_TOP:
NewOrigin.Y=0;
break;
case SB_BOTTOM:
NewOrigin.Y=(WORD)(ScreenInfo->ScreenBufferSize.Y-CONSOLE_WINDOW_SIZE_Y(ScreenInfo));
break;
case SB_ENDSCROLL:
EndScroll(Console);
default:
return;
}
NewOrigin.Y = (WORD)(max(0,min((SHORT)NewOrigin.Y,
(SHORT)ScreenInfo->ScreenBufferSize.Y-(SHORT)CONSOLE_WINDOW_SIZE_Y(ScreenInfo))));
SetWindowOrigin(ScreenInfo,
(BOOLEAN)TRUE,
NewOrigin
);
}
VOID
HorizontalScroll(
IN PCONSOLE_INFORMATION Console,
IN PSCREEN_INFORMATION ScreenInfo,
IN WORD ScrollCommand,
IN WORD AbsoluteChange
)
{
COORD NewOrigin;
NewOrigin.X = ScreenInfo->Window.Left;
NewOrigin.Y = ScreenInfo->Window.Top;
switch (ScrollCommand) {
case SB_LINEUP:
NewOrigin.X--;
break;
case SB_LINEDOWN:
NewOrigin.X++;
break;
case SB_PAGEUP:
NewOrigin.X-=CONSOLE_WINDOW_SIZE_X(ScreenInfo)-1;
break;
case SB_PAGEDOWN:
NewOrigin.X+=CONSOLE_WINDOW_SIZE_X(ScreenInfo)-1;
break;
case SB_THUMBTRACK:
NewOrigin.X= AbsoluteChange;
break;
case SB_TOP:
NewOrigin.X=0;
break;
case SB_BOTTOM:
NewOrigin.X=(WORD)(ScreenInfo->ScreenBufferSize.X-CONSOLE_WINDOW_SIZE_X(ScreenInfo));
break;
case SB_ENDSCROLL:
EndScroll(Console);
default:
return;
}
NewOrigin.X = (WORD)(max(0,min((SHORT)NewOrigin.X,
(SHORT)ScreenInfo->ScreenBufferSize.X-(SHORT)CONSOLE_WINDOW_SIZE_X(ScreenInfo))));
SetWindowOrigin(ScreenInfo,
(BOOLEAN)TRUE,
NewOrigin
);
}
LONG APIENTRY
ConsoleWindowProc(
HWND hWnd,
UINT Message,
WPARAM wParam,
LPARAM lParam
)
{
HDC hDC;
PAINTSTRUCT ps;
PCONSOLE_INFORMATION Console;
PSCREEN_INFORMATION ScreenInfo;
SMALL_RECT PaintRect;
LONG Status;
Console = (PCONSOLE_INFORMATION) GetWindowLong(hWnd, GWL_USERDATA);
if (Console != NULL) {
ASSERT(Console->hWnd != (HWND)-1);
LockConsole(Console);
ScreenInfo = Console->CurrentScreenBuffer;
//
// Set up our thread so we can impersonate the client
// while processing the message.
//
CSR_SERVER_QUERYCLIENTTHREAD()->ThreadHandle =
Console->ClientThreadHandle;
}
try {
if ((Message < WM_USER || Message > CM_DESTROY_WINDOW) &&
(Console == NULL || ScreenInfo == NULL)) {
switch (Message) {
case WM_GETMINMAXINFO:
{
//
// createwindow issues a WM_GETMINMAXINFO
// message before we have the windowlong set up
// with the console pointer. we need to allow
// the created window to be bigger than the
// default size by the scroll size.
//
LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
lpmmi->ptMaxTrackSize.y += HorizontalScrollSize;
lpmmi->ptMaxTrackSize.x += VerticalScrollSize;
}
break;
case WM_KILLFOCUS:
//
// If this console is terminating, lock the console
// behind us so it can't process focus events until
// we're completely dead. This prevents a potential
// deadlock with CSR.
//
LockNextConsole(Console, (HWND)wParam);
break;
case WM_ACTIVATEAPP:
//
// Some other app is activating. If we locked a
// console, unlock it now.
//
UnlockNextConsole(Console);
break;
default:
goto CallDefWin;
}
} else if (Message == ProgmanHandleMessage && lParam==0) {
// NOTE: lParam will be 0 if progman is sending it and
// 1 if console is sending it.
Status = 0;
// this is a workaround for a progman bug. progman
// sends a progmanhandlemessage twice for each window
// in the system each time one is requested (for one window).
if ((HWND)wParam != hWnd && Console->bIconInit) {
ATOM App,Topic;
CHAR szItem[ITEM_MAX_SIZE+1];
PCHAR lpItem;
ATOM aItem;
HANDLE ConsoleHandle;
PCONSOLE_INFORMATION OldConsole;
MSG DestroyMsg;
if (!(Console->Flags & CONSOLE_TERMINATING)) {
ConsoleHandle = Console->ConsoleHandle;
OldConsole = Console;
Console->hWndProgMan = (HWND)wParam;
UnlockConsole(Console);
App = GlobalAddAtomA("Shell");
Topic = GlobalAddAtomA("AppIcon");
SendMessage(Console->hWndProgMan,
WM_DDE_INITIATE,
(DWORD)hWnd,
MAKELONG(App,Topic)
);
// see if a CM_DESTROY_WINDOW message has been posted.
// if yes, don't continue getting icon.
if (PeekMessage(&DestroyMsg,
hWnd,
CM_DESTROY_WINDOW,
CM_DESTROY_WINDOW,
PM_NOREMOVE
)) {
Console = NULL;
} else {
Status = RevalidateConsole(ConsoleHandle, &Console);
if (NT_SUCCESS(Status)) {
ASSERT(Console == OldConsole);
Console->bIconInit = FALSE;
lpItem = _itoa((int)Console->iIconId,szItem,10);
aItem = GlobalAddAtomA(lpItem);
PostMessage(Console->hWndProgMan,
WM_DDE_REQUEST,
(DWORD)hWnd,
MAKELONG(CF_TEXT,aItem)
);
} else {
Console = NULL;
}
}
}
}
} else {
Status = 0;
switch (Message) {
case WM_DROPFILES:
try {
DoDrop (wParam,Console);
} except( EXCEPTION_EXECUTE_HANDLER ) {
KdPrint(("CONSRV: WM_DROPFILES raised exception\n"));
}
break;
case WM_DESTROY:
SetWindowLong(hWnd, GWL_USERDATA, 0);
break;
case WM_MOVE:
if (!IsIconic(hWnd)) {
GetWindowRect(hWnd, &Console->WindowRect);
}
break;
case WM_SIZE:
if (wParam != SIZE_MINIMIZED) {
//
// both SetWindowPos and SetScrollRange cause WM_SIZE
// messages to be issued. ignore them if we have already
// figured out what size the window should be.
//
if (!ScreenInfo->ResizingWindow) {
#ifdef THERESES_DEBUG
DbgPrint("WM_SIZE message ");
if (wParam == SIZEFULLSCREEN)
DbgPrint("SIZEFULLSCREEN\n");
else if (wParam == SIZENORMAL)
DbgPrint("SIZENORMAL\n");
DbgPrint(" WindowSize is %d %d\n",CONSOLE_WINDOW_SIZE_X(ScreenInfo),CONSOLE_WINDOW_SIZE_Y(ScreenInfo));
#endif
ScreenInfo->WindowMaximized = (wParam == SIZE_MAXIMIZED);
if (Console->ResizeFlags & SCREEN_BUFFER_CHANGE) {
UpdateWindowSize(Console,ScreenInfo);
}
GetWindowRect(hWnd, &Console->WindowRect);
#ifdef THERESES_DEBUG
DbgPrint(" WindowRect is now %d %d %d %d\n",Console->WindowRect.left,
Console->WindowRect.top,
Console->WindowRect.right,
Console->WindowRect.bottom);
#endif
if (Console->ResizeFlags & SCROLL_BAR_CHANGE) {
InternalUpdateScrollBars(ScreenInfo);
Console->ResizeFlags &= ~SCROLL_BAR_CHANGE;
}
}
} else {
//
// Console is going iconic. Trim working set of all
// processes in the console
//
TrimConsoleWorkingSet(Console);
}
break;
case WM_DDE_ACK:
if (Console->bIconInit) {
Console->hWndProgMan = (HWND)wParam;
}
break;
case WM_DDE_DATA:
{
DDEDATA *lpDDEData;
LPPMICONDATA lpIconData;
HICON hIcon;
HANDLE hDdeData;
BOOL bRelease;
UINT atomTemp;
UnpackDDElParam(WM_DDE_DATA, lParam, (PUINT)&hDdeData, NULL);
if (hDdeData == NULL) {
break;
}
lpDDEData = (DDEDATA *)GlobalLock(hDdeData);
ASSERT(lpDDEData->cfFormat == CF_TEXT);
lpIconData = (LPPMICONDATA)lpDDEData->Value;
hIcon = CreateIconFromResourceEx(&lpIconData->iResource,
0, TRUE, 0x30000, 0, 0, LR_DEFAULTSIZE);
if (hIcon) {
if (Console->hIcon != NULL && Console->hIcon != ghDefaultIcon) {
DestroyIcon(Console->hIcon);
}
Console->hIcon = hIcon;
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LONG)hIcon);
}
if (lpDDEData->fAckReq) {
UnpackDDElParam(WM_DDE_DATA, lParam, NULL, &atomTemp);
PostMessage(Console->hWndProgMan,
WM_DDE_ACK,
(DWORD)hWnd,
MAKELONG(0x8000, atomTemp));
}
bRelease = lpDDEData->fRelease;
GlobalUnlock(hDdeData);
if (bRelease){
GlobalFree(hDdeData);
}
PostMessage(Console->hWndProgMan,
WM_DDE_TERMINATE,
(DWORD)hWnd,
0
);
if (Console->Flags & CONSOLE_IS_ICONIC) {
// force repaint of icon
InvalidateRect(hWnd, NULL, TRUE);
}
}
break;
case WM_ACTIVATE:
//
// if we're activated by a mouse click, remember it so
// we don't pass the click on to the app.
//
if (LOWORD(wParam) == WA_CLICKACTIVE) {
Console->Flags |= CONSOLE_IGNORE_NEXT_MOUSE_INPUT;
}
goto CallDefWin;
break;
case WM_DDE_TERMINATE:
break;
case WM_INPUTLANGCHANGE:
Console->hklActive = (HKL)lParam;
goto CallDefWin;
break;
case WM_SETFOCUS:
ModifyConsoleProcessFocus(Console,FOREGROUND_BASE_PRIORITY);
SetConsoleReserveKeys(hWnd, Console->ReserveKeys);
Console->Flags |= CONSOLE_HAS_FOCUS;
SetTimer(hWnd, CURSOR_TIMER, CURSOR_BLINK_RATE_IN_MSECS, NULL);
HandleFocusEvent(Console,TRUE);
if (!Console->hklActive) {
SystemParametersInfo(SPI_GETDEFAULTINPUTLANG, (UINT)NULL, &Console->hklActive, FALSE);
}
ActivateKeyboardLayout(Console->hklActive, 0);
break;
case WM_KILLFOCUS:
ModifyConsoleProcessFocus(Console,NORMAL_BASE_PRIORITY);
SetConsoleReserveKeys(hWnd, CONSOLE_NOSHORTCUTKEY);
Console->Flags &= ~CONSOLE_HAS_FOCUS;
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
ConsoleHideCursor(ScreenInfo);
ScreenInfo->BufferInfo.TextInfo.UpdatingScreen -= 1; // counteract HideCursor
}
KillTimer(hWnd, CURSOR_TIMER);
HandleFocusEvent(Console,FALSE);
break;
case WM_PAINT:
// ICONIC bit is not set if we're fullscreen and don't
// have the hardware
ConsoleHideCursor(ScreenInfo);
hDC = BeginPaint(hWnd, &ps);
if (Console->Flags & CONSOLE_IS_ICONIC ||
Console->FullScreenFlags == CONSOLE_FULLSCREEN) {
RECT rc;
UINT cxIcon, cyIcon;
GetClientRect(hWnd, &rc);
cxIcon = GetSystemMetrics(SM_CXICON);
cyIcon = GetSystemMetrics(SM_CYICON);
rc.left = (rc.right - cxIcon) >> 1;
rc.top = (rc.bottom - cyIcon) >> 1;
DrawIcon(hDC, rc.left, rc.top, Console->hIcon);
} else {
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
PaintRect.Left = (SHORT)((ps.rcPaint.left/SCR_FONTSIZE(ScreenInfo).X)+ScreenInfo->Window.Left);
PaintRect.Right = (SHORT)((ps.rcPaint.right/SCR_FONTSIZE(ScreenInfo).X)+ScreenInfo->Window.Left);
PaintRect.Top = (SHORT)((ps.rcPaint.top/SCR_FONTSIZE(ScreenInfo).Y)+ScreenInfo->Window.Top);
PaintRect.Bottom = (SHORT)((ps.rcPaint.bottom/SCR_FONTSIZE(ScreenInfo).Y)+ScreenInfo->Window.Top);
} else {
PaintRect.Left = (SHORT)(ps.rcPaint.left+ScreenInfo->Window.Left);
PaintRect.Right = (SHORT)(ps.rcPaint.right+ScreenInfo->Window.Left);
PaintRect.Top = (SHORT)(ps.rcPaint.top+ScreenInfo->Window.Top);
PaintRect.Bottom = (SHORT)(ps.rcPaint.bottom+ScreenInfo->Window.Top);
}
ScreenInfo->BufferInfo.TextInfo.Flags &= ~TEXT_VALID_HINT;
WriteToScreen(ScreenInfo,&PaintRect);
}
EndPaint(hWnd,&ps);
ConsoleShowCursor(ScreenInfo);
break;
case WM_CLOSE:
if (!(Console->Flags & CONSOLE_NO_WINDOW) ||
!(Console->Flags & CONSOLE_WOW_REGISTERED)) {
HandleCtrlEvent(Console,CTRL_CLOSE_EVENT);
}
break;
case CM_CONSOLE_SHUTDOWN:
if (lParam == 0x47474747) {
Status = ShutdownConsole(Console, wParam);
Console = NULL;
}
break;
case WM_ERASEBKGND:
// ICONIC bit is not set if we're fullscreen and don't
// have the hardware
if (Console->Flags & CONSOLE_IS_ICONIC ||
Console->FullScreenFlags == CONSOLE_FULLSCREEN) {
Message = WM_ICONERASEBKGND;
goto CallDefWin;
}
break;
case WM_SETTINGCHANGE:
case WM_DISPLAYCHANGE:
gfInitSystemMetrics = TRUE;
break;
case WM_SETCURSOR:
if (lParam == -1) {
//
// the app changed the cursor visibility or shape.
// see if the cursor is in the client area.
//
POINT Point;
HWND hWndTmp;
GetCursorPos(&Point);
hWndTmp = WindowFromPoint(Point);
if (hWndTmp == hWnd) {
lParam = DefWindowProc(hWnd,WM_NCHITTEST,0,MAKELONG((WORD)Point.x, (WORD)Point.y));
}
}
if ((WORD)lParam == HTCLIENT) {
if (ScreenInfo->CursorDisplayCount < 0) {
SetCursor(NULL);
} else {
SetCursor(ScreenInfo->CursorHandle);
}
} else {
goto CallDefWin;
}
break;
case WM_GETMINMAXINFO:
{
LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
COORD FontSize;
UpdateScreenSizes(ScreenInfo, ScreenInfo->ScreenBufferSize);
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
FontSize = SCR_FONTSIZE(ScreenInfo);
} else {
FontSize.X = 1;
FontSize.Y = 1;
}
lpmmi->ptMaxSize.x = lpmmi->ptMaxTrackSize.x = ScreenInfo->MaxWindow.X;
if (!ScreenInfo->WindowMaximizedY) {
lpmmi->ptMaxTrackSize.x += VerticalScrollSize;
while (lpmmi->ptMaxSize.x > ConsoleFullScreenX) {
lpmmi->ptMaxSize.x -= FontSize.X;
}
lpmmi->ptMaxSize.x += VerticalScrollSize;
}
lpmmi->ptMaxSize.y = lpmmi->ptMaxTrackSize.y = ScreenInfo->MaxWindow.Y;
if (!ScreenInfo->WindowMaximizedX) {
lpmmi->ptMaxTrackSize.y += HorizontalScrollSize;
while (lpmmi->ptMaxSize.y > ConsoleFullScreenY) {
lpmmi->ptMaxSize.y -= FontSize.Y;
}
lpmmi->ptMaxSize.y += HorizontalScrollSize;
}
lpmmi->ptMinTrackSize.x = ScreenInfo->MinX * FontSize.X + VerticalClientToWindow;
lpmmi->ptMinTrackSize.y = HorizontalClientToWindow;
}
break;
case WM_QUERYDRAGICON:
Status = (DWORD)Console->hIcon;
break;
case WM_WINDOWPOSCHANGING:
// ignore window pos changes if going fullscreen
if (TRUE || (Console->FullScreenFlags == 0)) {
LPWINDOWPOS WindowPos = (LPWINDOWPOS)lParam;
DWORD fMinimized;
/*
* This message is sent before a SetWindowPos() operation
* occurs. We use it here to set/clear the CONSOLE_IS_ICONIC
* bit appropriately... doing so in the WM_SIZE handler
* is incorrect because the WM_SIZE comes after the
* WM_ERASEBKGND during SetWindowPos() processing, and the
* WM_ERASEBKGND needs to know if the console window is
* iconic or not.
*/
fMinimized = IsIconic(hWnd);
if (fMinimized) {
if (!(Console->Flags & CONSOLE_IS_ICONIC)) {
Console->Flags |= CONSOLE_IS_ICONIC;
//
// if the palette is something other than default,
// select the default palette in. otherwise, the
// screen will repaint twice each time the icon
// is painted.
//
if (ScreenInfo->hPalette != NULL &&
Console->FullScreenFlags == 0) {
SelectPalette(Console->hDC,
Console->hSysPalette,
FALSE);
UnsetActivePalette(ScreenInfo);
}
}
} else {
if (Console->Flags & CONSOLE_IS_ICONIC) {
Console->Flags &= ~CONSOLE_IS_ICONIC;
//
// if the palette is something other than default,
// select the default palette in. otherwise, the
// screen will repaint twice each time the icon
// is painted.
//
if (ScreenInfo->hPalette != NULL &&
Console->FullScreenFlags == 0) {
SelectPalette(Console->hDC,
ScreenInfo->hPalette,
FALSE);
SetActivePalette(ScreenInfo);
}
}
}
if (!ScreenInfo->ResizingWindow &&
(WindowPos->cx || WindowPos->cy) &&
!fMinimized) {
ProcessResizeWindow(ScreenInfo,Console,WindowPos);
}
}
break;
case WM_NCLBUTTONDOWN:
// allow user to move window even when bigger than the screen
switch (wParam & 0x00FF) {
case HTCAPTION:
UnlockConsole(Console);
Console = NULL;
SetActiveWindow(hWnd);
SendMessage(hWnd, WM_SYSCOMMAND,
SC_MOVE | wParam, lParam);
break;
default:
goto CallDefWin;
}
break;
case WM_KEYDOWN:
case WM_KEYUP:
case WM_CHAR:
case WM_DEADCHAR:
HandleKeyEvent(&Console,hWnd,Message,wParam,lParam);
break;
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_SYSCHAR:
case WM_SYSDEADCHAR:
if (HandleSysKeyEvent(&Console,hWnd,Message,wParam,lParam) && Console != NULL) {
goto CallDefWin;
}
break;
case WM_SYSCOMMAND:
if (wParam >= ScreenInfo->CommandIdLow &&
wParam <= ScreenInfo->CommandIdHigh) {
HandleMenuEvent(Console,wParam);
} else if (wParam == cmMark) {
DoMark(Console);
} else if (wParam == cmCopy) {
DoCopy(Console);
} else if (wParam == cmPaste) {
DoPaste(Console);
} else if (wParam == cmScroll) {
DoScroll(Console);
} else if (wParam == cmControl) {
PropertiesDlgShow(Console);
} else {
// if we're restoring, remove any
// mouse events so app doesn't get them.
if (wParam == SC_RESTORE) {
MSG RestoreMsg;
SetCapture(hWnd);
while (GetCapture() != NULL &&
(GetKeyState(VK_LBUTTON) & KEY_PRESSED)) {
PeekMessage(&RestoreMsg,
hWnd,
WM_MOUSEFIRST,
WM_MOUSELAST,
PM_REMOVE
);
}
ReleaseCapture();
}
// if shutdown got rid of the console beneath us,
// don't try to unlock it.
if (!NT_SUCCESS(ValidateConsole(Console))) {
Console = NULL;
}
goto CallDefWin;
}
// if shutdown got rid of the console beneath us,
// don't try to unlock it.
if (!NT_SUCCESS(ValidateConsole(Console))) {
Console = NULL;
}
break;
case WM_TIMER:
CursorTimerRoutine(ScreenInfo);
ScrollIfNecessary(Console,ScreenInfo);
break;
case WM_HSCROLL:
HorizontalScroll(Console, ScreenInfo,LOWORD(wParam),HIWORD(wParam));
break;
case WM_VSCROLL:
VerticalScroll(Console, ScreenInfo,LOWORD(wParam),HIWORD(wParam));
break;
case WM_INITMENU:
HandleMenuEvent(Console,WM_INITMENU);
InitializeMenu(Console);
break;
case WM_MENUSELECT:
if (HIWORD(wParam) == 0xffff) {
HandleMenuEvent(Console,WM_MENUSELECT);
}
break;
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
if (HandleMouseEvent(Console,ScreenInfo,Message,wParam,lParam)) {
goto CallDefWin;
}
break;
case WM_MOUSEWHEEL:
/*
* Don't handle zoom and datazoom.
*/
if (wParam & (MK_SHIFT | MK_CONTROL)) {
goto CallDefWin;
}
Status = 1;
if (gfInitSystemMetrics) {
InitializeSystemMetrics();
}
ScreenInfo->WheelDelta -= (short)HIWORD(wParam);
if (abs(ScreenInfo->WheelDelta) >= WHEEL_DELTA &&
gucWheelScrollLines > 0) {
COORD NewOrigin;
SHORT dy;
NewOrigin.X = ScreenInfo->Window.Left;
NewOrigin.Y = ScreenInfo->Window.Top;
/*
* Limit a roll of one (1) WHEEL_DELTA to scroll one (1) page.
*/
dy = (int) min(
(UINT) CONSOLE_WINDOW_SIZE_Y(ScreenInfo) - 1,
gucWheelScrollLines);
if (dy == 0) {
dy++;
}
dy *= (ScreenInfo->WheelDelta / WHEEL_DELTA);
ScreenInfo->WheelDelta %= WHEEL_DELTA;
NewOrigin.Y += dy;
if (NewOrigin.Y < 0) {
NewOrigin.Y = 0;
} else if (NewOrigin.Y + CONSOLE_WINDOW_SIZE_Y(ScreenInfo) >
ScreenInfo->ScreenBufferSize.Y) {
NewOrigin.Y = ScreenInfo->ScreenBufferSize.Y -
CONSOLE_WINDOW_SIZE_Y(ScreenInfo);
}
SetWindowOrigin(ScreenInfo, TRUE, NewOrigin);
}
break;
case WM_PALETTECHANGED:
if (Console->FullScreenFlags == 0) {
if (ScreenInfo->hPalette != NULL) {
SetActivePalette(ScreenInfo);
if (ScreenInfo->Flags & CONSOLE_GRAPHICS_BUFFER) {
WriteRegionToScreenBitMap(ScreenInfo,
&ScreenInfo->Window);
}
} else {
SetScreenColors(ScreenInfo, ScreenInfo->Attributes,
ScreenInfo->PopupAttributes, TRUE);
}
}
break;
#if defined(_X86_)
case WM_FULLSCREEN:
//
// This message is sent by the system to tell console that
// the fullscreen state of a window has changed.
// In some cases, this message will be sent in response to
// a call from console to change to fullscreen (Atl-Enter)
// or may also come directly from the system (switch of
// focus from a windowed app to a fullscreen app).
//
KdPrint(("CONSRV: WindowProc - WM_FULLSCREEN\n"));
Status = DisplayModeTransition(wParam,Console,ScreenInfo);
break;
#endif
case CM_DESTROY_WINDOW:
// make sure this is a valid message
if (Console && Console->Flags & CONSOLE_TERMINATING) {
if (Console->hWndProperties) {
SendMessage(Console->hWndProperties, WM_CLOSE, 0, 0);
}
DestroyWindowsWindow(Console,(HANDLE)wParam);
Console = NULL;
}
break;
case CM_SET_WINDOW_SIZE:
if (lParam == 0x47474747) {
Status = InternalSetWindowSize(Console,
(PSCREEN_INFORMATION)wParam,
&ScreenInfo->Window
);
}
break;
case CM_UPDATE_SCROLL_BARS:
InternalUpdateScrollBars(ScreenInfo);
break;
case CM_UPDATE_TITLE:
SetWindowText(hWnd,Console->Title);
break;
#if defined(_X86_)
case CM_MODE_TRANSITION:
KdPrint(("CONSRV: WindowProc - CM_MODE_TRANSITION\n"));
if (wParam == FULLSCREEN) {
ChangeDispSettings(Console, hWnd, CDS_FULLSCREEN);
} else {
ChangeDispSettings(Console, hWnd, 0);
ShowWindow(hWnd, SW_RESTORE);
}
UnlockConsole(Console);
Console = NULL;
NtSetEvent((HANDLE)lParam, NULL);
NtClose((HANDLE)lParam);
break;
#endif
case CM_HIDE_WINDOW:
UnlockConsole(Console);
Console = NULL;
ShowWindow(hWnd,SW_MINIMIZE);
break;
case CM_PROPERTIES_START:
Console->hWndProperties = (HWND)wParam;
break;
case CM_PROPERTIES_UPDATE:
PropertiesUpdate(Console, (HANDLE)wParam);
break;
case CM_PROPERTIES_END:
Console->hWndProperties = NULL;
break;
CallDefWin:
default:
if (Console != NULL) {
UnlockConsole(Console);
Console = NULL;
}
Status = (DefWindowProc(hWnd,Message,wParam,lParam));
break;
}
}
} finally {
if (Console != NULL) {
UnlockConsole(Console);
Console = NULL;
}
}
return Status;
}
/*
* Drag and Drop support functions for console window
*/
/*++
Routine Description:
This routine retrieves the filenames of dropped files. It was copied from
shelldll API DragQueryFile. We didn't use DragQueryFile () because we don't
want to load Shell32.dll in CSR
Arguments:
Same as DragQueryFile
Return Value:
--*/
UINT ConsoleDragQueryFile(
IN HANDLE hDrop,
IN UINT iFile,
IN PVOID lpFile,
IN UINT cb
)
{
UINT i;
LPDROPFILESTRUCT lpdfs;
BOOL fWide;
lpdfs = (LPDROPFILESTRUCT)GlobalLock(hDrop);
if (lpdfs)
{
fWide = (LOWORD(lpdfs->pFiles) == sizeof(DROPFILES));
if (fWide)
{
//
// This is a new (NT-compatible) HDROP
//
fWide = lpdfs->fWide; // Redetermine fWide from struct
// since it is present.
}
if (fWide)
{
LPWSTR lpList;
//
// UNICODE HDROP
//
lpList = (LPWSTR)((LPBYTE)lpdfs + lpdfs->pFiles);
// find either the number of files or the start of the file
// we're looking for
//
for (i = 0; (iFile == (UINT)-1 || i != iFile) && *lpList; i++)
{
while (*lpList++)
;
}
if (iFile == (UINT)-1)
goto Exit;
iFile = i = lstrlenW(lpList);
if (!i || !cb || !lpFile)
goto Exit;
cb--;
if (cb < i)
i = cb;
lstrcpynW((LPWSTR)lpFile, lpList, i + 1);
}
else
{
LPSTR lpList;
//
// This is Win31-style HDROP or an ANSI NT Style HDROP
//
lpList = (LPSTR)((LPBYTE)lpdfs + lpdfs->pFiles);
// find either the number of files or the start of the file
// we're looking for
//
for (i = 0; (iFile == (UINT)-1 || i != iFile) && *lpList; i++)
{
while (*lpList++)
;
}
if (iFile == (UINT)-1)
goto Exit;
iFile = i = lstrlenA(lpList);
if (!i || !cb || !lpFile)
goto Exit;
cb--;
if (cb < i)
i = cb;
MultiByteToWideChar(CP_ACP, 0, lpList, -1, (LPWSTR)lpFile, cb);
}
}
i = iFile;
Exit:
GlobalUnlock(hDrop);
return(i);
}
/*++
Routine Description:
This routine is called when ConsoleWindowProc receives a WM_DROPFILES
message. It initially calls ConsoleDragQueryFile() to calculate the number
of files dropped and then ConsoleDragQueryFile() is called
to retrieve the filename. DoStringPaste() pastes the filename to the console
window
Arguments:
wParam - Identifies the structure containing the filenames of the
dropped files.
Console - Pointer to CONSOLE_INFORMATION structure
Return Value:
None
--*/
void DoDrop (WPARAM wParam,
PCONSOLE_INFORMATION Console)
{
WCHAR szPath[MAX_PATH];
if (ConsoleDragQueryFile((HANDLE)wParam, 0xFFFFFFFF, NULL, 0))
/* # of files dropped */
{
ConsoleDragQueryFile ((HANDLE)wParam, 0, szPath, CharSizeOf(szPath));
DoStringPaste(Console,szPath,(wcslen(szPath) * sizeof(WCHAR)));
}
GlobalFree((HANDLE)wParam); /* Delete structure alocated for WM_DROPFILES*/
}