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.
6182 lines
202 KiB
6182 lines
202 KiB
/*++
|
|
|
|
Copyright (c) 1985 - 1999, 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 PROFILE_GDI
|
|
#ifdef PROFILE_GDI
|
|
LONG ScrollDCCount;
|
|
LONG ExtTextOutCount;
|
|
LONG TextColor = 1;
|
|
|
|
#define SCROLLDC_CALL ScrollDCCount++
|
|
#define TEXTOUT_CALL ExtTextOutCount++
|
|
#define TEXTCOLOR_CALL TextColor++
|
|
#else
|
|
#define SCROLLDC_CALL
|
|
#define TEXTOUT_CALL
|
|
#define TEXTCOLOR_CALL
|
|
#endif // PROFILE_GDI
|
|
|
|
#define ITEM_MAX_SIZE 256
|
|
|
|
// NOTE: we use this to communicate with progman - see Q105446 for details.
|
|
typedef struct _PMIconData {
|
|
DWORD dwResSize;
|
|
DWORD dwVer;
|
|
BYTE iResource; // icon resource
|
|
} PMICONDATA, *LPPMICONDATA;
|
|
|
|
//
|
|
// Screen dimensions
|
|
//
|
|
|
|
int ConsoleFullScreenX;
|
|
int ConsoleFullScreenY;
|
|
int ConsoleCaptionY;
|
|
int MinimumWidthX;
|
|
SHORT VerticalScrollSize;
|
|
SHORT HorizontalScrollSize;
|
|
|
|
SHORT VerticalClientToWindow;
|
|
SHORT HorizontalClientToWindow;
|
|
|
|
PCHAR_INFO ScrollBuffer;
|
|
ULONG ScrollBufferSize;
|
|
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;
|
|
|
|
BOOL gfInitSystemMetrics;
|
|
|
|
BOOL UsePolyTextOut;
|
|
|
|
HRGN ghrgnScroll;
|
|
LPRGNDATA gprgnData;
|
|
|
|
ULONG gucWheelScrollLines;
|
|
|
|
UINT guCaretBlinkTime;
|
|
|
|
#define GRGNDATASIZE (sizeof(RGNDATAHEADER) + (6 * sizeof(RECTL)))
|
|
|
|
|
|
#define LockScrollBuffer() RtlEnterCriticalSection(&ScrollBufferLock)
|
|
#define UnlockScrollBuffer() RtlLeaveCriticalSection(&ScrollBufferLock)
|
|
|
|
#define SetWindowConsole(hWnd, Console) SetWindowLongPtr((hWnd), GWLP_USERDATA, (LONG_PTR)(Console))
|
|
|
|
#ifdef LATER
|
|
#ifndef IS_IME_KBDLAYOUT
|
|
#define IS_IME_KBDLAYOUT(hkl) ((((ULONG_PTR)(hkl)) & 0xf0000000) == 0xe0000000)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
VOID GetNonBiDiKeyboardLayout(
|
|
HKL *phklActive);
|
|
|
|
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
|
|
);
|
|
|
|
#if defined(FE_SB)
|
|
BOOL
|
|
SB_PolyTextOutCandidate(
|
|
IN PSCREEN_INFORMATION ScreenInfo,
|
|
IN PSMALL_RECT Region
|
|
);
|
|
|
|
VOID
|
|
SB_ConsolePolyTextOut(
|
|
IN PSCREEN_INFORMATION ScreenInfo,
|
|
IN PSMALL_RECT Region
|
|
);
|
|
#endif
|
|
|
|
|
|
|
|
VOID
|
|
InitializeSystemMetrics( VOID )
|
|
{
|
|
RECT WindowSize;
|
|
|
|
gfInitSystemMetrics = FALSE;
|
|
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &gucWheelScrollLines, FALSE);
|
|
ConsoleFullScreenX = GetSystemMetrics(SM_CXFULLSCREEN);
|
|
ConsoleFullScreenY = GetSystemMetrics(SM_CYFULLSCREEN);
|
|
ConsoleCaptionY = GetSystemMetrics(SM_CYCAPTION);
|
|
VerticalScrollSize = (SHORT)GetSystemMetrics(SM_CXVSCROLL);
|
|
HorizontalScrollSize = (SHORT)GetSystemMetrics(SM_CYHSCROLL);
|
|
WindowSize.left = WindowSize.top = 0;
|
|
WindowSize.right = WindowSize.bottom = 50;
|
|
AdjustWindowRectEx(&WindowSize,
|
|
CONSOLE_WINDOW_FLAGS,
|
|
FALSE,
|
|
CONSOLE_WINDOW_EX_FLAGS
|
|
);
|
|
VerticalClientToWindow = (SHORT)(WindowSize.right-WindowSize.left-50);
|
|
HorizontalClientToWindow = (SHORT)(WindowSize.bottom-WindowSize.top-50);
|
|
|
|
#ifdef LATER
|
|
gfIsIMEEnabled = !!GetSystemMetrics(SM_IMMENABLED);
|
|
RIPMSG1(RIP_VERBOSE, "InitializeSystemMetrics: gfIsIMEEnabled=%d", gfIsIMEEnabled);
|
|
#endif
|
|
|
|
guCaretBlinkTime = GetCaretBlinkTime();
|
|
}
|
|
|
|
VOID
|
|
GetWindowLimits(
|
|
IN PSCREEN_INFORMATION ScreenInfo,
|
|
OUT PWINDOW_LIMITS WindowLimits
|
|
)
|
|
{
|
|
HMONITOR hMonitor;
|
|
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
|
|
COORD FontSize;
|
|
|
|
//
|
|
// 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->Console &&
|
|
(ScreenInfo->Console->hWnd || !(ScreenInfo->Console->Flags & CONSOLE_AUTO_POSITION)) &&
|
|
((hMonitor = MonitorFromRect(&ScreenInfo->Console->WindowRect, MONITOR_DEFAULTTOPRIMARY)) != NULL) &&
|
|
GetMonitorInfo(hMonitor, &MonitorInfo)) {
|
|
WindowLimits->FullScreenSize.X = (SHORT)(MonitorInfo.rcWork.right - MonitorInfo.rcWork.left);
|
|
WindowLimits->FullScreenSize.Y = (SHORT)(MonitorInfo.rcWork.bottom - MonitorInfo.rcWork.top - ConsoleCaptionY);
|
|
} else {
|
|
WindowLimits->FullScreenSize.X = (SHORT)ConsoleFullScreenX;
|
|
WindowLimits->FullScreenSize.Y = (SHORT)ConsoleFullScreenY;
|
|
}
|
|
|
|
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
FontSize = SCR_FONTSIZE(ScreenInfo);
|
|
} else {
|
|
FontSize.X = 1;
|
|
FontSize.Y = 1;
|
|
}
|
|
|
|
WindowLimits->MinimumWindowSize.X = ((MinimumWidthX - VerticalClientToWindow + FontSize.X - 1) / FontSize.X);
|
|
WindowLimits->MinimumWindowSize.Y = 1;
|
|
WindowLimits->MaximumWindowSize.X = min(WindowLimits->FullScreenSize.X/FontSize.X, ScreenInfo->ScreenBufferSize.X);
|
|
WindowLimits->MaximumWindowSize.X = max(WindowLimits->MaximumWindowSize.X, WindowLimits->MinimumWindowSize.X);
|
|
WindowLimits->MaximumWindowSize.Y = min(WindowLimits->FullScreenSize.Y/FontSize.Y, ScreenInfo->ScreenBufferSize.Y);
|
|
WindowLimits->MaxWindow.X = WindowLimits->MaximumWindowSize.X*FontSize.X + VerticalClientToWindow;
|
|
WindowLimits->MaxWindow.Y = WindowLimits->MaximumWindowSize.Y*FontSize.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
|
|
)
|
|
|
|
/*++
|
|
|
|
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;
|
|
|
|
if (ConsoleInfo->dwStartupFlags & STARTF_USESHOWWINDOW) {
|
|
Console->wShowWindow = ConsoleInfo->wShowWindow;
|
|
} else {
|
|
Console->wShowWindow = SW_SHOWNORMAL;
|
|
}
|
|
|
|
//
|
|
// 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 = ConsoleInfo->dwScreenBufferSize;
|
|
if (!(ConsoleInfo->dwStartupFlags & STARTF_USECOUNTCHARS)) {
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Grab font
|
|
//
|
|
#if defined(FE_SB)
|
|
FontIndexWant = FindCreateFont(ConsoleInfo->uFontFamily,
|
|
ConsoleInfo->FaceName,
|
|
ConsoleInfo->dwFontSize,
|
|
ConsoleInfo->uFontWeight,
|
|
ConsoleInfo->uCodePage
|
|
);
|
|
#else
|
|
FontIndexWant = FindCreateFont(ConsoleInfo->uFontFamily,
|
|
ConsoleInfo->FaceName,
|
|
ConsoleInfo->dwFontSize,
|
|
ConsoleInfo->uFontWeight);
|
|
#endif
|
|
|
|
//
|
|
// grab window size information
|
|
//
|
|
|
|
dwWindowSize = ConsoleInfo->dwWindowSize;
|
|
if (ConsoleInfo->dwStartupFlags & STARTF_USESIZE) {
|
|
dwWindowSize.X /= FontInfo[FontIndexWant].Size.X;
|
|
dwWindowSize.Y /= FontInfo[FontIndexWant].Size.Y;
|
|
} else 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;
|
|
|
|
Console->dwWindowOriginX = ConsoleInfo->dwWindowOrigin.X;
|
|
Console->dwWindowOriginY = ConsoleInfo->dwWindowOrigin.Y;
|
|
|
|
if (ConsoleInfo->bAutoPosition) {
|
|
Console->Flags |= CONSOLE_AUTO_POSITION;
|
|
Console->dwWindowOriginX = CW_USEDEFAULT;
|
|
} else {
|
|
Console->WindowRect.left = Console->dwWindowOriginX;
|
|
Console->WindowRect.top = Console->dwWindowOriginY;
|
|
Console->WindowRect.right = Console->dwWindowOriginX + dwWindowSize.X * FontInfo[FontIndexWant].Size.X;
|
|
Console->WindowRect.bottom = Console->dwWindowOriginY + dwWindowSize.Y * FontInfo[FontIndexWant].Size.Y;
|
|
}
|
|
|
|
#ifdef i386
|
|
if (FullScreenInitialized && !GetSystemMetrics(SM_REMOTESESSION)) {
|
|
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 != FALSE);
|
|
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 ));
|
|
|
|
#if defined(FE_SB)
|
|
// for FarEast version, we want get the code page from registry or shell32,
|
|
// so we can specify console codepage by console.cpl or shell32
|
|
// default codepage is OEMCP. scotthsu
|
|
Console->CP = ConsoleInfo->uCodePage;
|
|
Console->OutputCP = ConsoleInfo->uCodePage;
|
|
Console->fIsDBCSCP = CONSOLE_IS_DBCS_ENABLED() && IsAvailableFarEastCodePage(Console->CP);
|
|
Console->fIsDBCSOutputCP = CONSOLE_IS_DBCS_ENABLED() && IsAvailableFarEastCodePage(Console->OutputCP);
|
|
#endif
|
|
#if defined(FE_IME)
|
|
Console->ConsoleIme.ScrollWaitTimeout = guCaretBlinkTime * 2;
|
|
#endif
|
|
TryNewSize:
|
|
Status = CreateScreenBuffer(&Console->ScreenBuffers,
|
|
dwWindowSize,
|
|
FontIndexWant,
|
|
dwScreenBufferSize,
|
|
Fill,
|
|
PopupFill,
|
|
Console,
|
|
CONSOLE_TEXTMODE_BUFFER,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
ConsoleInfo->uCursorSize,
|
|
ConsoleInfo->FaceName
|
|
);
|
|
if (Status == STATUS_NO_MEMORY) {
|
|
//
|
|
// If we failed to create a large buffer, try again with a small one.
|
|
//
|
|
if (dwScreenBufferSize.X > 80 || dwScreenBufferSize.Y > 50) {
|
|
dwScreenBufferSize.X = min(dwScreenBufferSize.X, 80);
|
|
dwScreenBufferSize.Y = min(dwScreenBufferSize.Y, 50);
|
|
dwWindowSize.X = min(dwWindowSize.X, dwScreenBufferSize.X);
|
|
dwWindowSize.Y = min(dwWindowSize.Y, dwScreenBufferSize.Y);
|
|
Console->Flags |= CONSOLE_DEFAULT_BUFFER_SIZE;
|
|
goto TryNewSize;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CreateScreenBuffer(
|
|
OUT PSCREEN_INFORMATION *ScreenInformation,
|
|
IN COORD dwWindowSize,
|
|
IN DWORD nFont,
|
|
IN COORD dwScreenBufferSize,
|
|
IN CHAR_INFO Fill,
|
|
IN CHAR_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,
|
|
IN LPWSTR FaceName
|
|
)
|
|
|
|
/*++
|
|
|
|
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;
|
|
#if defined(FE_SB)
|
|
PBYTE AttrRowPtr;
|
|
#endif
|
|
WINDOW_LIMITS WindowLimits;
|
|
|
|
/*
|
|
* Make sure we have a valid font. Bail if no fonts are available.
|
|
*/
|
|
ASSERT(nFont < NumberOfFonts);
|
|
if (NumberOfFonts == 0) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ScreenInfo = ConsoleHeapAlloc(SCREEN_TAG, sizeof(SCREEN_INFORMATION));
|
|
if (ScreenInfo == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
ScreenInfo->Console = Console;
|
|
ScreenInfo->Flags = Flags;
|
|
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
|
|
ASSERT(FontInfo[nFont].FaceName != NULL);
|
|
|
|
ScreenInfo->BufferInfo.TextInfo.ListOfTextBufferFont = NULL;
|
|
|
|
Status = StoreTextBufferFontInfo(ScreenInfo,
|
|
nFont,
|
|
FontInfo[nFont].Size,
|
|
FontInfo[nFont].Family,
|
|
FontInfo[nFont].Weight,
|
|
FaceName ? FaceName : FontInfo[nFont].FaceName,
|
|
Console->OutputCP);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ConsoleHeapFree(ScreenInfo);
|
|
return((ULONG) Status);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
ScreenInfo->ScreenBufferSize = dwScreenBufferSize;
|
|
GetWindowLimits(ScreenInfo, &WindowLimits);
|
|
dwScreenBufferSize.X = max(dwScreenBufferSize.X, WindowLimits.MinimumWindowSize.X);
|
|
dwWindowSize.X = max(dwWindowSize.X, WindowLimits.MinimumWindowSize.X);
|
|
|
|
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(Console->OutputCP,dwWindowSize,&WindowSize);
|
|
}
|
|
#endif
|
|
ScreenInfo->BufferInfo.TextInfo.FirstRow = 0;
|
|
ScreenInfo->BufferInfo.TextInfo.Rows = ConsoleHeapAlloc(SCREEN_TAG, dwScreenBufferSize.Y * sizeof(ROW));
|
|
if (ScreenInfo->BufferInfo.TextInfo.Rows == NULL) {
|
|
RemoveTextBufferFontInfo(ScreenInfo);
|
|
ConsoleHeapFree(ScreenInfo);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
ScreenInfo->BufferInfo.TextInfo.TextRows = ConsoleHeapAlloc(SCREEN_TAG, dwScreenBufferSize.X * dwScreenBufferSize.Y * sizeof(WCHAR));
|
|
if (ScreenInfo->BufferInfo.TextInfo.TextRows == NULL) {
|
|
ConsoleHeapFree(ScreenInfo->BufferInfo.TextInfo.Rows);
|
|
RemoveTextBufferFontInfo(ScreenInfo);
|
|
ConsoleHeapFree(ScreenInfo);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
#if defined(FE_SB)
|
|
if (!CreateDbcsScreenBuffer(Console, dwScreenBufferSize, &ScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer)) {
|
|
ConsoleHeapFree(ScreenInfo->BufferInfo.TextInfo.TextRows);
|
|
ConsoleHeapFree(ScreenInfo->BufferInfo.TextInfo.Rows);
|
|
RemoveTextBufferFontInfo(ScreenInfo);
|
|
ConsoleHeapFree(ScreenInfo);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
AttrRowPtr=ScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer.KAttrRows;
|
|
#endif
|
|
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;
|
|
#if defined(FE_SB)
|
|
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.KAttrs = AttrRowPtr;
|
|
#endif
|
|
for (j=0;j<dwScreenBufferSize.X;j++) {
|
|
TextRowPtr[j] = (WCHAR)' ';
|
|
}
|
|
#if defined(FE_SB)
|
|
if (AttrRowPtr) {
|
|
RtlZeroMemory(AttrRowPtr, dwScreenBufferSize.X);
|
|
AttrRowPtr+=dwScreenBufferSize.X;
|
|
}
|
|
#endif
|
|
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.CursorMoved = FALSE;
|
|
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 >= WindowLimits.MaximumWindowSize.X) {
|
|
ScreenInfo->Window.Right = WindowLimits.MaximumWindowSize.X-1;
|
|
dwWindowSize.X = CONSOLE_WINDOW_SIZE_X(ScreenInfo);
|
|
}
|
|
if (ScreenInfo->Window.Bottom >= WindowLimits.MaximumWindowSize.Y) {
|
|
ScreenInfo->Window.Bottom = WindowLimits.MaximumWindowSize.Y-1;
|
|
dwWindowSize.Y = CONSOLE_WINDOW_SIZE_Y(ScreenInfo);
|
|
}
|
|
ScreenInfo->WindowMaximizedX = (dwWindowSize.X == dwScreenBufferSize.X);
|
|
ScreenInfo->WindowMaximizedY = (dwWindowSize.Y == dwScreenBufferSize.Y);
|
|
#if defined(FE_SB)
|
|
#if defined(_X86_)
|
|
ScreenInfo->BufferInfo.TextInfo.MousePosition.X = 0;
|
|
ScreenInfo->BufferInfo.TextInfo.MousePosition.Y = 0;
|
|
#endif // i386
|
|
|
|
ScreenInfo->BufferInfo.TextInfo.CursorBlink = TRUE;
|
|
ScreenInfo->BufferInfo.TextInfo.CursorDBEnable = TRUE;
|
|
#endif
|
|
|
|
}
|
|
else {
|
|
Status = CreateConsoleBitmap(GraphicsBufferInfo,
|
|
ScreenInfo,
|
|
lpBitmap,
|
|
hMutex
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ConsoleHeapFree(ScreenInfo);
|
|
return Status;
|
|
}
|
|
ScreenInfo->WindowMaximizedX = TRUE;
|
|
ScreenInfo->WindowMaximizedY = TRUE;
|
|
}
|
|
|
|
ScreenInfo->WindowMaximized = FALSE;
|
|
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 = ghNormalCursor;
|
|
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;
|
|
|
|
ScreenInfo->WheelDelta = 0;
|
|
|
|
#if defined(FE_SB)
|
|
ScreenInfo->WriteConsoleDbcsLeadByte[0] = 0;
|
|
ScreenInfo->BisectFlag = 0;
|
|
if (Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
SetLineChar(ScreenInfo);
|
|
}
|
|
ScreenInfo->FillOutDbcsLeadChar = 0;
|
|
ScreenInfo->ConvScreenInfo = NULL;
|
|
#endif
|
|
|
|
*ScreenInformation = ScreenInfo;
|
|
DBGOUTPUT(("SCREEN at %lx\n", ScreenInfo));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
PositionConsoleWindow(
|
|
IN PCONSOLE_INFORMATION Console,
|
|
IN BOOL Initialize
|
|
)
|
|
{
|
|
GetWindowRect(Console->hWnd, &Console->WindowRect);
|
|
|
|
//
|
|
// If this is an autoposition window being initialized, make sure it's
|
|
// client area doesn't descend below the tray
|
|
//
|
|
|
|
if (Initialize && (Console->Flags & CONSOLE_AUTO_POSITION)) {
|
|
RECT ClientRect;
|
|
LONG dx = 0;
|
|
LONG dy = 0;
|
|
HMONITOR hMonitor;
|
|
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
|
|
|
|
hMonitor = MonitorFromRect(&Console->WindowRect, MONITOR_DEFAULTTONULL);
|
|
if (hMonitor && GetMonitorInfo(hMonitor, &MonitorInfo)) {
|
|
GetClientRect(Console->hWnd, &ClientRect);
|
|
ClientToScreen(Console->hWnd, (LPPOINT)&ClientRect.left);
|
|
ClientToScreen(Console->hWnd, (LPPOINT)&ClientRect.right);
|
|
if (Console->WindowRect.right > MonitorInfo.rcWork.right) {
|
|
dx = max(min((Console->WindowRect.right - MonitorInfo.rcWork.right),
|
|
(Console->WindowRect.left - MonitorInfo.rcWork.left)),
|
|
min((ClientRect.right - MonitorInfo.rcWork.right),
|
|
(ClientRect.left - MonitorInfo.rcWork.left)));
|
|
}
|
|
if (Console->WindowRect.bottom > MonitorInfo.rcWork.bottom) {
|
|
dy = max(min((Console->WindowRect.bottom - MonitorInfo.rcWork.bottom),
|
|
(Console->WindowRect.top - MonitorInfo.rcWork.top)),
|
|
min((ClientRect.bottom - MonitorInfo.rcWork.bottom),
|
|
(ClientRect.top - MonitorInfo.rcWork.top)));
|
|
}
|
|
if (dx || dy) {
|
|
SetWindowPos(Console->hWnd,
|
|
NULL,
|
|
Console->WindowRect.left - dx,
|
|
Console->WindowRect.top - dy,
|
|
0,
|
|
0,
|
|
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Bug 273518 - joejo
|
|
*
|
|
* This will allow console windows to set foreground correctly on new
|
|
* process' it launches, as opposed it just forcing foreground.
|
|
*/
|
|
NTSTATUS
|
|
ConsoleSetActiveWindow(
|
|
IN PCONSOLE_INFORMATION Console
|
|
)
|
|
{
|
|
HWND hWnd = Console->hWnd;
|
|
HANDLE ConsoleHandle = Console->ConsoleHandle;
|
|
|
|
UnlockConsole(Console);
|
|
SetActiveWindow(hWnd);
|
|
return RevalidateConsole(ConsoleHandle, &Console);
|
|
}
|
|
|
|
NTSTATUS
|
|
CreateWindowsWindow(
|
|
IN PCONSOLE_INFORMATION Console
|
|
)
|
|
{
|
|
PSCREEN_INFORMATION ScreenInfo;
|
|
SIZE 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.cx = CONSOLE_WINDOW_SIZE_X(ScreenInfo)*SCR_FONTSIZE(ScreenInfo).X + VerticalClientToWindow;
|
|
WindowSize.cy = CONSOLE_WINDOW_SIZE_Y(ScreenInfo)*SCR_FONTSIZE(ScreenInfo).Y + HorizontalClientToWindow;
|
|
Style = CONSOLE_WINDOW_FLAGS & ~WS_VISIBLE;
|
|
if (!ScreenInfo->WindowMaximizedX) {
|
|
WindowSize.cy += HorizontalScrollSize;
|
|
} else {
|
|
Style &= ~WS_HSCROLL;
|
|
}
|
|
if (!ScreenInfo->WindowMaximizedY) {
|
|
WindowSize.cx += VerticalScrollSize;
|
|
} else {
|
|
Style &= ~WS_VSCROLL;
|
|
}
|
|
|
|
//
|
|
// create the window.
|
|
//
|
|
|
|
Console->WindowRect.left = Console->dwWindowOriginX;
|
|
Console->WindowRect.top = Console->dwWindowOriginY;
|
|
Console->WindowRect.right = WindowSize.cx + Console->dwWindowOriginX;
|
|
Console->WindowRect.bottom = WindowSize.cy + Console->dwWindowOriginY;
|
|
hWnd = CreateWindowEx(CONSOLE_WINDOW_EX_FLAGS,
|
|
CONSOLE_WINDOW_CLASS,
|
|
Console->Title,
|
|
Style,
|
|
Console->dwWindowOriginX,
|
|
Console->dwWindowOriginY,
|
|
WindowSize.cx,
|
|
WindowSize.cy,
|
|
(Console->Flags & CONSOLE_NO_WINDOW) ? HWND_MESSAGE : HWND_DESKTOP,
|
|
NULL,
|
|
ghInstance,
|
|
NULL);
|
|
if (hWnd == NULL) {
|
|
NtSetEvent(Console->InitEvents[INITIALIZATION_FAILED],NULL);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Console->hWnd = hWnd;
|
|
|
|
SetWindowConsole(hWnd, 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, HandleToUlong(ThreadInfo.ClientId.UniqueProcess));
|
|
SetConsoleTid(Console->hWnd, HandleToUlong(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);
|
|
|
|
gnConsoleWindows++;
|
|
Console->InputThreadInfo->WindowCount++;
|
|
|
|
#if defined(FE_IME)
|
|
SetUndetermineAttribute(Console);
|
|
#endif
|
|
#if defined(FE_SB)
|
|
RegisterKeisenOfTTFont(ScreenInfo);
|
|
#endif
|
|
|
|
//
|
|
// 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,
|
|
(WPARAM)Console->hWnd,
|
|
1);
|
|
}
|
|
if (Console->hIcon == NULL) {
|
|
Console->hIcon = ghDefaultIcon;
|
|
Console->hSmIcon = ghDefaultSmIcon;
|
|
} else if (Console->hIcon != ghDefaultIcon) {
|
|
SendMessage(Console->hWnd, WM_SETICON, ICON_BIG, (LPARAM)Console->hIcon);
|
|
SendMessage(Console->hWnd, WM_SETICON, ICON_SMALL, (LPARAM)Console->hSmIcon);
|
|
}
|
|
|
|
SetBkMode(Console->hDC,OPAQUE);
|
|
SetFont(ScreenInfo);
|
|
SelectObject(Console->hDC, GetStockObject(DC_BRUSH));
|
|
SetScreenColors(ScreenInfo, ScreenInfo->Attributes,
|
|
ScreenInfo->PopupAttributes, FALSE);
|
|
if (Console->Flags & CONSOLE_NO_WINDOW) {
|
|
ShowWindowAsync(Console->hWnd, SW_HIDE);
|
|
#ifdef i386
|
|
} else if (Console->FullScreenFlags != 0) {
|
|
if (Console->wShowWindow == SW_SHOWMINNOACTIVE) {
|
|
ShowWindowAsync(Console->hWnd, Console->wShowWindow);
|
|
Console->FullScreenFlags = 0;
|
|
Console->Flags |= CONSOLE_IS_ICONIC;
|
|
} else {
|
|
ConvertToFullScreen(Console);
|
|
if (!NT_SUCCESS(ConsoleSetActiveWindow(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(ConsoleSetActiveWindow(Console))) {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
} else if (Console->wShowWindow == SW_SHOWMINNOACTIVE) {
|
|
Console->Flags |= CONSOLE_IS_ICONIC;
|
|
}
|
|
ShowWindowAsync(Console->hWnd, Console->wShowWindow);
|
|
}
|
|
|
|
//UpdateWindow(Console->hWnd);
|
|
InternalUpdateScrollBars(ScreenInfo);
|
|
if (!(Console->Flags & CONSOLE_IS_ICONIC) &&
|
|
(Console->FullScreenFlags == 0) ) {
|
|
|
|
PositionConsoleWindow(Console, TRUE);
|
|
}
|
|
|
|
#if defined(FE_IME)
|
|
if (CONSOLE_IS_IME_ENABLED() && !(Console->Flags & CONSOLE_NO_WINDOW)) {
|
|
SetTimer(Console->hWnd, SCROLL_WAIT_TIMER, guCaretBlinkTime, NULL);
|
|
}
|
|
#endif
|
|
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;
|
|
|
|
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) {
|
|
ConsoleHeapFree(ScreenInfo->BufferInfo.TextInfo.Rows[i].AttrRow.Attrs);
|
|
}
|
|
}
|
|
ConsoleHeapFree(ScreenInfo->BufferInfo.TextInfo.TextRows);
|
|
ConsoleHeapFree(ScreenInfo->BufferInfo.TextInfo.Rows);
|
|
#if defined(FE_SB)
|
|
DeleteDbcsScreenBuffer(&ScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer);
|
|
#endif
|
|
RemoveTextBufferFontInfo(ScreenInfo);
|
|
} else {
|
|
if (ScreenInfo->hPalette != NULL) {
|
|
if (GetCurrentObject(Console->hDC, OBJ_PAL) == ScreenInfo->hPalette) {
|
|
SelectPalette(Console->hDC, Console->hSysPalette, FALSE);
|
|
}
|
|
DeleteObject(ScreenInfo->hPalette);
|
|
}
|
|
FreeConsoleBitmap(ScreenInfo);
|
|
}
|
|
ConsoleHeapFree(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 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 = ConsoleHeapAlloc(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);
|
|
*Target = NewString;
|
|
if (*TargetLength == 1) {
|
|
*Target = &Row->AttrRow.AttrPair;
|
|
**Target = *NewString;
|
|
ConsoleHeapFree(NewString);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
ResetTextFlags(
|
|
IN PSCREEN_INFORMATION ScreenInfo,
|
|
IN SHORT StartX,
|
|
IN SHORT StartY,
|
|
IN SHORT EndX,
|
|
IN SHORT EndY
|
|
)
|
|
{
|
|
SHORT RowIndex;
|
|
PROW Row;
|
|
WCHAR Char;
|
|
PATTR_PAIR Attr;
|
|
SHORT CountOfAttr;
|
|
SHORT i;
|
|
|
|
//
|
|
// Fire off a winevent to let accessibility apps know what changed.
|
|
//
|
|
|
|
if (ACTIVE_SCREEN_BUFFER(ScreenInfo)) {
|
|
ASSERT(EndX < ScreenInfo->ScreenBufferSize.X);
|
|
if (StartX == EndX && StartY == EndY) {
|
|
RowIndex = (ScreenInfo->BufferInfo.TextInfo.FirstRow+StartY) % ScreenInfo->ScreenBufferSize.Y;
|
|
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
|
|
Char = Row->CharRow.Chars[StartX];
|
|
FindAttrIndex(Row->AttrRow.Attrs, StartX, &Attr, &CountOfAttr);
|
|
ConsoleNotifyWinEvent(ScreenInfo->Console,
|
|
EVENT_CONSOLE_UPDATE_SIMPLE,
|
|
MAKELONG(StartX, StartY),
|
|
MAKELONG(Char, Attr->Attr));
|
|
} else {
|
|
ConsoleNotifyWinEvent(ScreenInfo->Console,
|
|
EVENT_CONSOLE_UPDATE_REGION,
|
|
MAKELONG(StartX, StartY),
|
|
MAKELONG(EndX, EndY));
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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
|
|
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)
|
|
((PBYTE)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)
|
|
((PBYTE)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;
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(ScreenInfo->Console)) {
|
|
PBYTE AttrP = &Row->CharRow.KAttrs[SourcePoint.X];
|
|
for (j=0;j<XSize;TargetPtr++) {
|
|
BYTE AttrR;
|
|
AttrR = *AttrP++;
|
|
if (j==0 && AttrR & ATTR_TRAILING_BYTE)
|
|
{
|
|
TargetPtr->Char.UnicodeChar = UNICODE_SPACE;
|
|
AttrR = 0;
|
|
}
|
|
else if (j+1 >= XSize && AttrR & ATTR_LEADING_BYTE)
|
|
{
|
|
TargetPtr->Char.UnicodeChar = UNICODE_SPACE;
|
|
AttrR = 0;
|
|
}
|
|
else
|
|
TargetPtr->Char.UnicodeChar = *Char;
|
|
Char++;
|
|
TargetPtr->Attributes = Attr->Attr | (WCHAR)(AttrR & ATTR_DBCSSBCS_BYTE) << 8;
|
|
j+=1;
|
|
if (++k==CountOfAttr && j<XSize) {
|
|
Attr++;
|
|
k=0;
|
|
CountOfAttr = Attr->Length;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
#endif
|
|
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 defined(FE_SB)
|
|
}
|
|
#endif
|
|
|
|
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; this calculation must be
|
|
// done 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;
|
|
}
|
|
|
|
//
|
|
// Ensure that the write region is within the constraints of the screen
|
|
// buffer.
|
|
//
|
|
if (WriteRegion->Left >= ScreenInformation->ScreenBufferSize.X ||
|
|
WriteRegion->Top >= ScreenInformation->ScreenBufferSize.Y) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Do clipping.
|
|
//
|
|
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;
|
|
}
|
|
|
|
if (SourceRect.Left > SourceRect.Right ||
|
|
SourceRect.Top > SourceRect.Bottom) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
TargetPoint.X = WriteRegion->Left;
|
|
TargetPoint.Y = WriteRegion->Top;
|
|
WriteRectToScreenBuffer((PBYTE)Buffer,
|
|
SourceSize,
|
|
&SourceRect,
|
|
ScreenInformation,
|
|
TargetPoint,
|
|
0xFFFFFFFF
|
|
);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
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_REAL_UNICODE - read a string of Real Unicode characters.
|
|
CONSOLE_FALSE_UNICODE - read a string of False 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 = NULL;
|
|
PWCHAR BufPtr;
|
|
#if defined(FE_SB)
|
|
PBYTE AttrP;
|
|
PBYTE TransBufferA,BufPtrA;
|
|
PCONSOLE_INFORMATION Console = ScreenInfo->Console;
|
|
#endif
|
|
|
|
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 = ConsoleHeapAlloc(TMP_TAG, *NumRecords * sizeof(WCHAR));
|
|
if (TransBuffer == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
BufPtr = TransBuffer;
|
|
} else {
|
|
BufPtr = Buffer;
|
|
}
|
|
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console))
|
|
{
|
|
TransBufferA = ConsoleHeapAlloc(TMP_DBCS_TAG, *NumRecords * sizeof(BYTE));
|
|
if (TransBufferA == NULL) {
|
|
if (TransBuffer != NULL)
|
|
ConsoleHeapFree(TransBuffer);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
BufPtrA = TransBufferA;
|
|
}
|
|
#endif
|
|
if ((StringType == CONSOLE_ASCII) ||
|
|
(StringType == CONSOLE_REAL_UNICODE) ||
|
|
(StringType == CONSOLE_FALSE_UNICODE)) {
|
|
while (NumRead < *NumRecords) {
|
|
|
|
//
|
|
// copy the chars from its array
|
|
//
|
|
|
|
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
|
|
Char = &Row->CharRow.Chars[X];
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console))
|
|
AttrP = &Row->CharRow.KAttrs[X];
|
|
#endif
|
|
if ((ULONG)(ScreenInfo->ScreenBufferSize.X - X) > (*NumRecords - NumRead)) {
|
|
RtlCopyMemory(BufPtr,Char,(*NumRecords - NumRead) * sizeof(WCHAR));
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console))
|
|
RtlCopyMemory(BufPtrA,AttrP,(*NumRecords - NumRead) * sizeof(CHAR));
|
|
#endif
|
|
NumRead += *NumRecords - NumRead;
|
|
break;
|
|
}
|
|
RtlCopyMemory(BufPtr,Char,(ScreenInfo->ScreenBufferSize.X - X) * sizeof(WCHAR));
|
|
BufPtr = (PVOID)((PBYTE)BufPtr + ((ScreenInfo->ScreenBufferSize.X - X) * sizeof(WCHAR)));
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console)) {
|
|
RtlCopyMemory(BufPtrA,AttrP,(ScreenInfo->ScreenBufferSize.X - X) * sizeof(CHAR));
|
|
BufPtrA = (PVOID)((PBYTE)BufPtrA + ((ScreenInfo->ScreenBufferSize.X - X) * sizeof(CHAR)));
|
|
}
|
|
#endif
|
|
NumRead += ScreenInfo->ScreenBufferSize.X - X;
|
|
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
|
|
RowIndex = 0;
|
|
}
|
|
X = 0;
|
|
Y++;
|
|
if (Y>=ScreenInfo->ScreenBufferSize.Y) {
|
|
break;
|
|
}
|
|
}
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console) && (NumRead)) {
|
|
if (StringType == CONSOLE_ASCII) {
|
|
Char = BufPtr = TransBuffer;
|
|
} else {
|
|
Char = BufPtr = Buffer;
|
|
}
|
|
AttrP = BufPtrA = TransBufferA;
|
|
|
|
if (*BufPtrA & ATTR_TRAILING_BYTE)
|
|
{
|
|
j = k = (SHORT)(NumRead - 1);
|
|
BufPtr++;
|
|
*Char++ = UNICODE_SPACE;
|
|
BufPtrA++;
|
|
NumRead = 1;
|
|
}
|
|
else {
|
|
j = k = (SHORT)NumRead;
|
|
NumRead = 0;
|
|
}
|
|
while (j--) {
|
|
if (!(*BufPtrA & ATTR_TRAILING_BYTE)) {
|
|
*Char++ = *BufPtr;
|
|
NumRead++;
|
|
}
|
|
BufPtr++;
|
|
BufPtrA++;
|
|
}
|
|
if (k && *(BufPtrA-1) & ATTR_LEADING_BYTE)
|
|
{
|
|
*(Char-1) = UNICODE_SPACE;
|
|
}
|
|
}
|
|
#endif
|
|
} else if (StringType == CONSOLE_ATTRIBUTE) {
|
|
PWORD TargetPtr=BufPtr;
|
|
while (NumRead < *NumRecords) {
|
|
|
|
//
|
|
// copy the attrs from its array
|
|
//
|
|
|
|
Row = &ScreenInfo->BufferInfo.TextInfo.Rows[RowIndex];
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console))
|
|
AttrP = &Row->CharRow.KAttrs[X];
|
|
#endif
|
|
FindAttrIndex(Row->AttrRow.Attrs,
|
|
X,
|
|
&Attr,
|
|
&CountOfAttr
|
|
);
|
|
k=0;
|
|
for (j=X;j<ScreenInfo->ScreenBufferSize.X;TargetPtr++) {
|
|
#if defined(FE_SB)
|
|
if (!CONSOLE_IS_DBCS_OUTPUTCP(Console) )
|
|
*TargetPtr = Attr->Attr;
|
|
else if ((j == X) && (*AttrP & ATTR_TRAILING_BYTE))
|
|
*TargetPtr = Attr->Attr;
|
|
else if (*AttrP & ATTR_LEADING_BYTE){
|
|
if ((NumRead == *NumRecords-1)||(j == ScreenInfo->ScreenBufferSize.X-1))
|
|
*TargetPtr = Attr->Attr;
|
|
else
|
|
*TargetPtr = Attr->Attr | (WCHAR)(*AttrP & ATTR_DBCSSBCS_BYTE) << 8;
|
|
}
|
|
else
|
|
*TargetPtr = Attr->Attr | (WCHAR)(*AttrP & ATTR_DBCSSBCS_BYTE) << 8;
|
|
#else
|
|
*TargetPtr = Attr->Attr;
|
|
#endif
|
|
NumRead++;
|
|
j+=1;
|
|
if (++k==CountOfAttr && j<ScreenInfo->ScreenBufferSize.X) {
|
|
Attr++;
|
|
k=0;
|
|
CountOfAttr = Attr->Length;
|
|
}
|
|
if (NumRead == *NumRecords) {
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console))
|
|
ConsoleHeapFree(TransBufferA);
|
|
#endif
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console))
|
|
AttrP++;
|
|
#endif
|
|
}
|
|
if (++RowIndex == ScreenInfo->ScreenBufferSize.Y) {
|
|
RowIndex = 0;
|
|
}
|
|
X = 0;
|
|
Y++;
|
|
if (Y>=ScreenInfo->ScreenBufferSize.Y) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
*NumRecords = 0;
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console))
|
|
ConsoleHeapFree(TransBufferA);
|
|
#endif
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (StringType == CONSOLE_ASCII) {
|
|
UINT Codepage;
|
|
#if defined(FE_SB)
|
|
if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
|
|
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
|
|
if (ScreenInfo->Console->OutputCP != WINDOWSCP)
|
|
Codepage = USACP;
|
|
else
|
|
Codepage = WINDOWSCP;
|
|
} else {
|
|
Codepage = ScreenInfo->Console->OutputCP;
|
|
}
|
|
#else
|
|
if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
|
|
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
|
|
Codepage = WINDOWSCP;
|
|
} else {
|
|
Codepage = ScreenInfo->Console->OutputCP;
|
|
}
|
|
#endif
|
|
#if defined(FE_SB)
|
|
if ((NumRead == 1) && !CONSOLE_IS_DBCS_OUTPUTCP(Console))
|
|
#else
|
|
if (NumRead == 1)
|
|
#endif
|
|
{
|
|
*((PBYTE)Buffer) = WcharToChar(Codepage, *TransBuffer);
|
|
} else {
|
|
NumRead = ConvertOutputToOem(Codepage, TransBuffer, NumRead, Buffer, *NumRecords);
|
|
}
|
|
ConsoleHeapFree(TransBuffer);
|
|
} else if (StringType == CONSOLE_REAL_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
|
|
);
|
|
}
|
|
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console))
|
|
ConsoleHeapFree(TransBufferA);
|
|
#endif
|
|
*NumRecords = NumRead;
|
|
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:
|
|
|
|
--*/
|
|
|
|
{
|
|
WINDOW_LIMITS WindowLimits;
|
|
|
|
*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);
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(ScreenInfo->Console)) {
|
|
MaximumWindowSize->Y = min(25,ScreenInfo->ScreenBufferSize.Y);
|
|
} else {
|
|
MaximumWindowSize->Y = min(50,ScreenInfo->ScreenBufferSize.Y);
|
|
}
|
|
#else
|
|
MaximumWindowSize->Y = min(50,ScreenInfo->ScreenBufferSize.Y);
|
|
#endif
|
|
} else {
|
|
GetWindowLimits(ScreenInfo, &WindowLimits);
|
|
*MaximumWindowSize = WindowLimits.MaximumWindowSize;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
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,
|
|
(WPARAM)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);
|
|
|
|
//
|
|
// Fire off an event to let accessibility apps know the layout has changed.
|
|
//
|
|
|
|
ConsoleNotifyWinEvent(ScreenInfo->Console,
|
|
EVENT_CONSOLE_LAYOUT,
|
|
0,
|
|
0);
|
|
|
|
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;
|
|
BOOLEAN WindowMaximizedX,WindowMaximizedY;
|
|
SHORT LimitX,LimitY;
|
|
PWCHAR TextRows,TextRowPtr;
|
|
BOOL UpdateWindow;
|
|
SHORT TopRow,TopRowIndex; // new top row of screen buffer
|
|
COORD CursorPosition;
|
|
#if defined(FE_SB)
|
|
DBCS_SCREEN_BUFFER NewDbcsScreenBuffer;
|
|
PBYTE TextRowPtrA;
|
|
#endif
|
|
|
|
//
|
|
// Don't allow resize of graphics apps
|
|
//
|
|
|
|
if (!(ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER)) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
TextRows = ConsoleHeapAlloc(SCREEN_TAG,
|
|
NewScreenSize.X * NewScreenSize.Y * sizeof(WCHAR));
|
|
if (TextRows == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
#if defined(FE_SB)
|
|
if (! CreateDbcsScreenBuffer(ScreenInfo->Console,NewScreenSize,&NewDbcsScreenBuffer))
|
|
{
|
|
ConsoleHeapFree(TextRows);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
#endif
|
|
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 = ConsoleHeapAlloc(SCREEN_TAG, NewScreenSize.Y * sizeof(ROW));
|
|
if (Temp == NULL) {
|
|
ConsoleHeapFree(TextRows);
|
|
#if defined(FE_SB)
|
|
DeleteDbcsScreenBuffer(&NewDbcsScreenBuffer);
|
|
#endif
|
|
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) {
|
|
ConsoleHeapFree(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;
|
|
ConsoleHeapFree(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.
|
|
//
|
|
#if defined(FE_SB)
|
|
TextRowPtrA=NewDbcsScreenBuffer.KAttrRows;
|
|
#endif
|
|
for (i=0,TextRowPtr=TextRows;i<LimitY;i++,TextRowPtr+=NewScreenSize.X)
|
|
{
|
|
RtlCopyMemory(TextRowPtr,
|
|
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.Chars,
|
|
LimitX*sizeof(WCHAR));
|
|
#if defined(FE_SB)
|
|
if (TextRowPtrA) {
|
|
RtlCopyMemory(TextRowPtrA,
|
|
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.KAttrs,
|
|
LimitX*sizeof(CHAR));
|
|
}
|
|
#endif
|
|
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;
|
|
#if defined(FE_SB)
|
|
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.KAttrs = TextRowPtrA;
|
|
if (TextRowPtrA) {
|
|
if (NewScreenSize.X > ScreenInfo->ScreenBufferSize.X)
|
|
RtlZeroMemory(TextRowPtrA+ScreenInfo->ScreenBufferSize.X,
|
|
NewScreenSize.X-ScreenInfo->ScreenBufferSize.X);
|
|
TextRowPtrA+=NewScreenSize.X;
|
|
}
|
|
#endif
|
|
}
|
|
for (;i<NewScreenSize.Y;i++,TextRowPtr+=NewScreenSize.X)
|
|
{
|
|
for (j=0;j<NewScreenSize.X;j++) {
|
|
TextRowPtr[j] = (WCHAR)' ';
|
|
}
|
|
#if defined(FE_SB)
|
|
if (TextRowPtrA) {
|
|
RtlZeroMemory(TextRowPtrA, NewScreenSize.X);
|
|
}
|
|
#endif
|
|
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;
|
|
#if defined(FE_SB)
|
|
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.KAttrs = TextRowPtrA;
|
|
if (TextRowPtrA) {
|
|
TextRowPtrA+=NewScreenSize.X;
|
|
}
|
|
#endif
|
|
}
|
|
ConsoleHeapFree(ScreenInfo->BufferInfo.TextInfo.TextRows);
|
|
ScreenInfo->BufferInfo.TextInfo.TextRows = TextRows;
|
|
#if defined(FE_SB)
|
|
DeleteDbcsScreenBuffer(&ScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer);
|
|
ScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer = NewDbcsScreenBuffer;
|
|
#endif
|
|
|
|
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 = ConsoleHeapReAlloc(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;
|
|
ConsoleHeapFree(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;
|
|
}
|
|
|
|
#if defined(FE_SB)
|
|
// Should be sets ScreenBufferSize before calls SetCursorPosition
|
|
// because SetCursorPosition refer ScreenBufferSize.
|
|
// Also, FE version refer in InvertPixels.
|
|
//
|
|
// kkntbug:11311
|
|
ScreenInfo->ScreenBufferSize = NewScreenSize;
|
|
#endif
|
|
|
|
//
|
|
// adjust cursor position if it's no longer with screen buffer
|
|
//
|
|
|
|
CursorPosition=ScreenInfo->BufferInfo.TextInfo.CursorPosition;
|
|
if (CursorPosition.X >= NewScreenSize.X) {
|
|
if (ScreenInfo->OutputMode & ENABLE_WRAP_AT_EOL_OUTPUT) {
|
|
CursorPosition.X = 0;
|
|
CursorPosition.Y += 1;
|
|
} else {
|
|
CursorPosition.X = NewScreenSize.X-1;
|
|
}
|
|
}
|
|
if (CursorPosition.Y >= NewScreenSize.Y) {
|
|
CursorPosition.Y = NewScreenSize.Y-1;
|
|
}
|
|
#if defined(FE_SB)
|
|
// set cursor position Y is ZERO when expand screen buffer with IME open mode
|
|
// from screen buffer is one line mode.
|
|
// Because, One line screen buffer mode and IME open mode is set -1 as cursor position Y.
|
|
if (ScreenInfo->Console->InputBuffer.ImeMode.Open && CursorPosition.Y < 0) {
|
|
CursorPosition.Y = 0;
|
|
}
|
|
#endif
|
|
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,
|
|
0,
|
|
(SHORT)(ScreenInfo->ScreenBufferSize.X - 1),
|
|
(SHORT)(ScreenInfo->ScreenBufferSize.Y - 1));
|
|
WindowMaximizedX = (CONSOLE_WINDOW_SIZE_X(ScreenInfo) ==
|
|
ScreenInfo->ScreenBufferSize.X);
|
|
WindowMaximizedY = (CONSOLE_WINDOW_SIZE_Y(ScreenInfo) ==
|
|
ScreenInfo->ScreenBufferSize.Y);
|
|
|
|
#if defined(FE_IME)
|
|
if (CONSOLE_IS_IME_ENABLED()) {
|
|
if (!NT_SUCCESS(ConsoleImeMessagePump(ScreenInfo->Console,
|
|
CONIME_NOTIFY_SCREENBUFFERSIZE,
|
|
(WPARAM)ScreenInfo->Console->ConsoleHandle,
|
|
(LPARAM)MAKELPARAM(NewScreenSize.X, NewScreenSize.Y)
|
|
))) {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
}
|
|
|
|
if ( (! ScreenInfo->ConvScreenInfo) &&
|
|
(CONSOLE_IS_DBCS_OUTPUTCP(ScreenInfo->Console)))
|
|
{
|
|
if (!NT_SUCCESS(ConsoleImeResizeModeSystemScreenBuffer(ScreenInfo->Console,NewScreenSize)) ||
|
|
!NT_SUCCESS(ConsoleImeResizeCompStrScreenBuffer(ScreenInfo->Console,NewScreenSize))) {
|
|
/*
|
|
* If something went wrong, just bail out.
|
|
*/
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
}
|
|
#endif // FE_IME
|
|
if (ScreenInfo->WindowMaximizedX != WindowMaximizedX ||
|
|
ScreenInfo->WindowMaximizedY != WindowMaximizedY) {
|
|
ScreenInfo->WindowMaximizedX = WindowMaximizedX;
|
|
ScreenInfo->WindowMaximizedY = WindowMaximizedY;
|
|
UpdateWindow = TRUE;
|
|
}
|
|
if (UpdateWindow) {
|
|
SetWindowSize(ScreenInfo);
|
|
}
|
|
|
|
//
|
|
// Fire off an event to let accessibility apps know the layout has changed.
|
|
//
|
|
|
|
if (ACTIVE_SCREEN_BUFFER(ScreenInfo)) {
|
|
ConsoleNotifyWinEvent(ScreenInfo->Console,
|
|
EVENT_CONSOLE_LAYOUT,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
if (DoScrollBarUpdate) {
|
|
UpdateScrollBars(ScreenInfo);
|
|
}
|
|
if (ScreenInfo->Console->InputBuffer.InputMode & ENABLE_WINDOW_INPUT) {
|
|
ScreenBufferSizeChange(ScreenInfo,ScreenInfo->ScreenBufferSize);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
AllocateScrollBuffer(
|
|
DWORD Size
|
|
)
|
|
{
|
|
ScrollBuffer = ConsoleHeapAlloc(SCREEN_TAG, Size);
|
|
if (ScrollBuffer == NULL) {
|
|
ScrollBufferSize = 0;
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
ScrollBufferSize = Size;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
FreeScrollBuffer( VOID )
|
|
{
|
|
ConsoleHeapFree(ScrollBuffer);
|
|
ScrollBuffer = NULL;
|
|
ScrollBufferSize = 0;
|
|
}
|
|
|
|
NTSTATUS
|
|
InitializeScrollBuffer(
|
|
VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* It's possible for this function to be called multiple times, if, e.g.,
|
|
* console initialization fails the first time *after* this function is
|
|
* called.
|
|
*/
|
|
if (ghrgnScroll) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ghrgnScroll = CreateRectRgn(0,0,1,1);
|
|
if (ghrgnScroll == NULL) {
|
|
RIPMSGF0(RIP_WARNING, "Cannot allocate ghrgnScroll.");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
gprgnData = ConsoleHeapAlloc(SCREEN_TAG, GRGNDATASIZE);
|
|
if (gprgnData == NULL) {
|
|
RIPMSGF0(RIP_WARNING, "Cannot allocate gprgnData.");
|
|
Status = STATUS_NO_MEMORY;
|
|
goto error;
|
|
}
|
|
|
|
Status = AllocateScrollBuffer(DefaultRegInfo.ScreenBufferSize.X *
|
|
DefaultRegInfo.ScreenBufferSize.Y *
|
|
sizeof(CHAR_INFO));
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto error;
|
|
}
|
|
|
|
Status = RtlInitializeCriticalSectionAndSpinCount(&ScrollBufferLock,
|
|
0x80000000);
|
|
|
|
error:
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG0(RIP_WARNING, "InitializeScrollBuffer failed, cleaning up");
|
|
if (ghrgnScroll) {
|
|
DeleteObject(ghrgnScroll);
|
|
ghrgnScroll = NULL;
|
|
}
|
|
|
|
if (gprgnData) {
|
|
ConsoleHeapFree(gprgnData);
|
|
gprgnData = NULL;
|
|
}
|
|
}
|
|
|
|
UserAssert(!NT_SUCCESS(Status) || ghrgnScroll);
|
|
|
|
return Status;
|
|
}
|
|
|
|
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 = ConsoleHeapAlloc(TMP_TAG, iSize);
|
|
if (pRgnData == NULL)
|
|
return;
|
|
}
|
|
|
|
if (!GetRegionData(ghrgnScroll, iSize, pRgnData)) {
|
|
ASSERT(FALSE);
|
|
if (pRgnData != gprgnData) {
|
|
ConsoleHeapFree(pRgnData);
|
|
}
|
|
return;
|
|
}
|
|
|
|
pRect = (PRECT)&pRgnData->Buffer;
|
|
|
|
/*
|
|
* Redraw each rectangle
|
|
*/
|
|
for(i=0;i<(int)pRgnData->rdh.nCount;i++,pRect++) {
|
|
/*
|
|
* Convert to chars. 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) {
|
|
ConsoleHeapFree(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;
|
|
#if defined(FE_SB)
|
|
BYTE fBisect = 0;
|
|
SMALL_RECT UpdateRect;
|
|
SMALL_RECT TmpBisect;
|
|
#endif
|
|
|
|
DBGOUTPUT(("ScrollScreen\n"));
|
|
if (!ACTIVE_SCREEN_BUFFER(ScreenInfo)) {
|
|
return;
|
|
}
|
|
if (ScreenInfo->Console->FullScreenFlags == 0 &&
|
|
!(ScreenInfo->Console->Flags & (CONSOLE_IS_ICONIC | CONSOLE_NO_WINDOW))) {
|
|
#if defined(FE_SB)
|
|
if (ScreenInfo->BisectFlag){
|
|
SMALL_RECT RedrawRect;
|
|
if (ScrollRect->Top < TargetPoint.Y){
|
|
RedrawRect.Top = ScrollRect->Top;
|
|
RedrawRect.Bottom = TargetPoint.Y+(ScrollRect->Bottom-ScrollRect->Top);
|
|
}
|
|
else{
|
|
RedrawRect.Top = TargetPoint.Y;
|
|
RedrawRect.Bottom = ScrollRect->Bottom;
|
|
}
|
|
if (ScrollRect->Left < TargetPoint.X){
|
|
RedrawRect.Left = ScrollRect->Left;
|
|
RedrawRect.Right = TargetPoint.X+(ScrollRect->Right-ScrollRect->Left);
|
|
}
|
|
else{
|
|
RedrawRect.Left = TargetPoint.X;
|
|
RedrawRect.Right = ScrollRect->Right;
|
|
}
|
|
WriteToScreen(ScreenInfo,&RedrawRect);
|
|
}
|
|
else{
|
|
#endif
|
|
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);
|
|
|
|
//
|
|
// Fire off an event to let accessibility apps know we've scrolled.
|
|
//
|
|
|
|
ConsoleNotifyWinEvent(ScreenInfo->Console,
|
|
EVENT_CONSOLE_UPDATE_SCROLL,
|
|
TargetPoint.X - ScrollRect->Left,
|
|
TargetPoint.Y - ScrollRect->Top);
|
|
|
|
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
|
|
* optimized 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);
|
|
#if defined(FE_SB)
|
|
fBisect = ScreenInfo->BisectFlag;
|
|
#endif
|
|
WriteToScreen(ScreenInfo, &UpdateRegion);
|
|
break;
|
|
case COMPLEXREGION:
|
|
UpdateComplexRegion(ScreenInfo, FontSize);
|
|
break;
|
|
}
|
|
|
|
if (MergeRect) {
|
|
#if defined(FE_SB)
|
|
if (fBisect)
|
|
ScreenInfo->BisectFlag = fBisect;
|
|
else
|
|
fBisect = ScreenInfo->BisectFlag;
|
|
#endif
|
|
WriteToScreen(ScreenInfo, MergeRect);
|
|
}
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(ScreenInfo->Console)) {
|
|
UpdateRect.Left = TargetPoint.X;
|
|
UpdateRect.Right = ScrollRect->Right + (TargetPoint.X-ScrollRect->Left);
|
|
UpdateRect.Top = TargetPoint.Y;
|
|
UpdateRect.Bottom = ScrollRect->Bottom + (TargetPoint.Y-ScrollRect->Top);
|
|
if (UpdateRect.Left &&
|
|
UpdateRect.Right+1 < ScreenInfo->ScreenBufferSize.X &&
|
|
UpdateRect.Right-UpdateRect.Left <= 2) {
|
|
TmpBisect.Left = UpdateRect.Left-1;
|
|
TmpBisect.Right = UpdateRect.Right+1;
|
|
TmpBisect.Top = UpdateRect.Top;
|
|
TmpBisect.Bottom = UpdateRect.Bottom;
|
|
WriteToScreen(ScreenInfo, &TmpBisect);
|
|
}
|
|
else {
|
|
if (UpdateRect.Left) {
|
|
TmpBisect.Left = UpdateRect.Left-1;
|
|
TmpBisect.Right = UpdateRect.Left;
|
|
TmpBisect.Top = UpdateRect.Top;
|
|
TmpBisect.Bottom = UpdateRect.Bottom;
|
|
WriteToScreen(ScreenInfo, &TmpBisect);
|
|
}
|
|
if (UpdateRect.Right+1 < ScreenInfo->ScreenBufferSize.X) {
|
|
TmpBisect.Left = UpdateRect.Right;
|
|
TmpBisect.Right = UpdateRect.Right+1;
|
|
TmpBisect.Top = UpdateRect.Top;
|
|
TmpBisect.Bottom = UpdateRect.Bottom;
|
|
WriteToScreen(ScreenInfo, &TmpBisect);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
#if defined(FE_SB)
|
|
if (fBisect)
|
|
ScreenInfo->BisectFlag = fBisect;
|
|
else
|
|
fBisect = ScreenInfo->BisectFlag;
|
|
#endif
|
|
WriteToScreen(ScreenInfo, &ScreenInfo->Window);
|
|
}
|
|
UnlockScrollBuffer();
|
|
#if defined(FE_SB)
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef i386
|
|
else if (ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(ScreenInfo->Console)) {
|
|
if (! ScreenInfo->ConvScreenInfo) {
|
|
if (ScreenInfo->Console->CurrentScreenBuffer == ScreenInfo) {
|
|
ScrollHW(ScreenInfo,
|
|
ScrollRect,
|
|
MergeRect,
|
|
TargetPoint
|
|
);
|
|
}
|
|
}
|
|
else if (ScreenInfo->Console->CurrentScreenBuffer->Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
ScrollHW(ScreenInfo,
|
|
ScrollRect,
|
|
MergeRect,
|
|
TargetPoint
|
|
);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
ScrollHW(ScreenInfo,
|
|
ScrollRect,
|
|
MergeRect,
|
|
TargetPoint
|
|
);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
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;
|
|
#if defined(FE_SB)
|
|
PBYTE AttrP;
|
|
#endif
|
|
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++;
|
|
}
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console)){
|
|
int LineWidth = Row->CharRow.Right - Row->CharRow.Left;
|
|
AttrP = &Row->CharRow.KAttrs[Row->CharRow.Left];
|
|
if ( LineWidth > 0 )
|
|
RtlZeroMemory(AttrP, LineWidth);
|
|
AttrP += LineWidth;
|
|
Row->CharRow.OldRight = INVALID_OLD_LENGTH;
|
|
Row->CharRow.OldLeft = INVALID_OLD_LENGTH;
|
|
Console->ConsoleIme.ScrollWaitCountDown = Console->ConsoleIme.ScrollWaitTimeout;
|
|
}
|
|
#endif
|
|
Row->CharRow.Right = 0;
|
|
Row->CharRow.Left = ScreenInfo->ScreenBufferSize.X;
|
|
|
|
//
|
|
// set up attributes
|
|
//
|
|
|
|
if (Row->AttrRow.Length != 1) {
|
|
ConsoleHeapFree(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 | CONSOLE_NO_WINDOW))) {
|
|
|
|
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
|
|
);
|
|
|
|
//
|
|
// Fire off an event to let accessibility apps know we've scrolled.
|
|
//
|
|
|
|
ConsoleNotifyWinEvent(Console,
|
|
EVENT_CONSOLE_UPDATE_SCROLL,
|
|
0,
|
|
-1);
|
|
|
|
if (Success && ScreenInfo->Window.Top!=ScreenInfo->Window.Bottom) {
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console) &&
|
|
ScreenInfo->Attributes & (COMMON_LVB_GRID_HORIZONTAL +
|
|
COMMON_LVB_GRID_LVERTICAL +
|
|
COMMON_LVB_GRID_RVERTICAL +
|
|
COMMON_LVB_REVERSE_VIDEO +
|
|
COMMON_LVB_UNDERSCORE )){
|
|
UpdateRegion = ScreenInfo->Window;
|
|
UpdateRegion.Top = UpdateRegion.Bottom;
|
|
ScreenInfo->BufferInfo.TextInfo.Flags &= ~TEXT_VALID_HINT;
|
|
WriteToScreen(ScreenInfo,&UpdateRegion);
|
|
}
|
|
else{
|
|
#endif
|
|
switch (GetRgnBox(ghrgnScroll, &BoundingBox)) {
|
|
case SIMPLEREGION:
|
|
if (BoundingBox.left == 0 &&
|
|
BoundingBox.right == ScreenWidth &&
|
|
BoundingBox.top == ScrollHeight &&
|
|
BoundingBox.bottom == ScreenHeight) {
|
|
|
|
PatBlt(Console->hDC,0,ScrollHeight,ScreenWidth,FontSize.Y,PATCOPY);
|
|
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;
|
|
}
|
|
#if defined(FE_SB)
|
|
}
|
|
#endif
|
|
} 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;
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console) ) {
|
|
if (! ScreenInfo->ConvScreenInfo) {
|
|
if (ScreenInfo->Console->CurrentScreenBuffer == ScreenInfo) {
|
|
ScrollHW(ScreenInfo,
|
|
&ScrollRect,
|
|
NULL,
|
|
TargetPoint
|
|
);
|
|
}
|
|
}
|
|
else if (ScreenInfo->Console->CurrentScreenBuffer->Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
ScrollHW(ScreenInfo,
|
|
&ScrollRect,
|
|
NULL,
|
|
TargetPoint
|
|
);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
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 CHAR_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;
|
|
}
|
|
|
|
//
|
|
// if scroll rect doesn't intersect clip region, return.
|
|
//
|
|
|
|
if (ScrollRectangle3.Bottom < ScrollRectangle3.Top ||
|
|
ScrollRectangle3.Right < ScrollRectangle3.Left) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ConsoleHideCursor(ScreenInfo);
|
|
|
|
#if defined(FE_IME)
|
|
Console->ConsoleIme.ScrollWaitCountDown = Console->ConsoleIme.ScrollWaitTimeout;
|
|
#endif // FE_IME
|
|
//
|
|
// 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 < 0 || NewWindow.Bottom < 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 | CONSOLE_NO_WINDOW))) {
|
|
|
|
InvertSelection(Console, TRUE);
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console) &&
|
|
!(Console->ConsoleIme.ScrollFlag & HIDE_FOR_SCROLL)) {
|
|
ConsoleImeBottomLineUse(ScreenInfo,0);
|
|
}
|
|
#endif
|
|
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 {
|
|
#if defined(FE_SB)
|
|
RECT ClipRect;
|
|
#endif
|
|
ScrollRect.left = 0;
|
|
ScrollRect.right = CONSOLE_WINDOW_SIZE_X(ScreenInfo)*FontSize.X;
|
|
ScrollRect.top = 0;
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console) &&
|
|
Console->InputBuffer.ImeMode.Open )
|
|
{
|
|
if (ScreenInfo->Window.Top <= NewWindow.Top)
|
|
ScrollRect.bottom = (CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-1)*FontSize.Y;
|
|
else
|
|
ScrollRect.bottom = (CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-2)*FontSize.Y;
|
|
ClipRect = ScrollRect;
|
|
ClipRect.bottom = (CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-1)*FontSize.Y;
|
|
}
|
|
else
|
|
#endif
|
|
ScrollRect.bottom = CONSOLE_WINDOW_SIZE_Y(ScreenInfo)*FontSize.Y;
|
|
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console) &&
|
|
ScrollRect.bottom == 0) {
|
|
UpdateRegion.Left = 0;
|
|
UpdateRegion.Top = 0;
|
|
UpdateRegion.Right = CONSOLE_WINDOW_SIZE_X(ScreenInfo);
|
|
UpdateRegion.Bottom = 0;
|
|
WriteToScreen(ScreenInfo,&UpdateRegion);
|
|
}
|
|
else {
|
|
#endif
|
|
SCROLLDC_CALL;
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console) &&
|
|
Console->InputBuffer.ImeMode.Open )
|
|
{
|
|
Success = ScrollDC(Console->hDC,
|
|
(ScreenInfo->Window.Left-NewWindow.Left)*FontSize.X,
|
|
(ScreenInfo->Window.Top-NewWindow.Top)*FontSize.Y,
|
|
&ScrollRect,
|
|
&ClipRect,
|
|
NULL,
|
|
&BoundingBox
|
|
);
|
|
}
|
|
else
|
|
#endif
|
|
Success = ScrollDC(Console->hDC,
|
|
(ScreenInfo->Window.Left-NewWindow.Left)*FontSize.X,
|
|
(ScreenInfo->Window.Top-NewWindow.Top)*FontSize.Y,
|
|
&ScrollRect,
|
|
NULL,
|
|
NULL,
|
|
&BoundingBox
|
|
);
|
|
|
|
//
|
|
// Fire off an event to let accessibility apps know we've scrolled.
|
|
//
|
|
|
|
ConsoleNotifyWinEvent(Console,
|
|
EVENT_CONSOLE_UPDATE_SCROLL,
|
|
ScreenInfo->Window.Left - NewWindow.Left,
|
|
ScreenInfo->Window.Top - NewWindow.Top);
|
|
|
|
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);
|
|
#if defined(FE_SB)
|
|
}
|
|
#endif
|
|
}
|
|
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;
|
|
}
|
|
|
|
#if defined(FE_SB)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console) ) {
|
|
ConsoleImeResizeModeSystemView(Console,ScreenInfo->Window);
|
|
ConsoleImeResizeCompStrView(Console,ScreenInfo->Window);
|
|
}
|
|
#endif
|
|
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:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// make sure there's something to do
|
|
//
|
|
|
|
if (RtlEqualMemory(&ScreenInfo->Window, WindowDimensions, sizeof(SMALL_RECT))) {
|
|
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 = (CONSOLE_WINDOW_SIZE_X(ScreenInfo) == ScreenInfo->ScreenBufferSize.X);
|
|
ScreenInfo->WindowMaximizedY = (CONSOLE_WINDOW_SIZE_Y(ScreenInfo) == 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 defined(FE_IME)
|
|
if (ScreenInfo->ConvScreenInfo != NULL)
|
|
return;
|
|
#endif
|
|
if (ScreenInfo->Console->Flags & CONSOLE_SETTING_WINDOW_SIZE)
|
|
return;
|
|
ScreenInfo->Console->Flags |= CONSOLE_SETTING_WINDOW_SIZE;
|
|
PostMessage(ScreenInfo->Console->hWnd,
|
|
CM_SET_WINDOW_SIZE,
|
|
(WPARAM)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
|
|
)
|
|
{
|
|
SIZE 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
|
|
//
|
|
|
|
if (gfInitSystemMetrics) {
|
|
InitializeSystemMetrics();
|
|
}
|
|
|
|
//
|
|
// figure out how big to make the window, given the desired client area
|
|
// size.
|
|
//
|
|
|
|
ScreenInfo->ResizingWindow++;
|
|
WindowSizeX = WINDOW_SIZE_X(Window);
|
|
WindowSizeY = WINDOW_SIZE_Y(Window);
|
|
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
WindowSize.cx = WindowSizeX*SCR_FONTSIZE(ScreenInfo).X;
|
|
WindowSize.cy = WindowSizeY*SCR_FONTSIZE(ScreenInfo).Y;
|
|
} else {
|
|
WindowSize.cx = WindowSizeX;
|
|
WindowSize.cy = WindowSizeY;
|
|
}
|
|
WindowSize.cx += VerticalClientToWindow;
|
|
WindowSize.cy += HorizontalClientToWindow;
|
|
|
|
if (WindowSizeY != 0) {
|
|
if (!ScreenInfo->WindowMaximizedX) {
|
|
WindowSize.cy += HorizontalScrollSize;
|
|
}
|
|
if (!ScreenInfo->WindowMaximizedY) {
|
|
WindowSize.cx += VerticalScrollSize;
|
|
}
|
|
}
|
|
|
|
Console->WindowRect.right = Console->WindowRect.left + WindowSize.cx;
|
|
Console->WindowRect.bottom = Console->WindowRect.top + WindowSize.cy;
|
|
|
|
UpdateWindowSize(Console,ScreenInfo);
|
|
ScreenInfo->ResizingWindow--;
|
|
} else if (Console->FullScreenFlags & CONSOLE_FULLSCREEN_HARDWARE) {
|
|
WriteToScreen(ScreenInfo,&ScreenInfo->Window);
|
|
}
|
|
#if defined(FE_IME)
|
|
if ( (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) &&
|
|
(CONSOLE_IS_DBCS_OUTPUTCP(Console)))
|
|
{
|
|
ConsoleImeResizeModeSystemView(Console,Console->CurrentScreenBuffer->Window);
|
|
ConsoleImeResizeCompStrView(Console,Console->CurrentScreenBuffer->Window);
|
|
}
|
|
#endif // FE_IME
|
|
}
|
|
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) {
|
|
|
|
SetScreenColors(ScreenInfo, ScreenInfo->Attributes,
|
|
ScreenInfo->PopupAttributes, FALSE);
|
|
|
|
//
|
|
// 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;
|
|
USERTHREAD_USEDESKTOPINFO utudi;
|
|
|
|
if (GetCurrentThreadId() != Console->InputThreadInfo->ThreadId) {
|
|
bReset = TRUE;
|
|
utudi.hThread = Console->InputThreadInfo->ThreadHandle;
|
|
utudi.drdRestore.pdeskRestore = NULL;
|
|
NtUserSetInformationThread(NtCurrentThread(),
|
|
UserThreadUseDesktop,
|
|
&utudi, sizeof(utudi));
|
|
}
|
|
|
|
if (ScreenInfo->hPalette == NULL) {
|
|
hPalette = Console->hSysPalette;
|
|
} else {
|
|
hPalette = ScreenInfo->hPalette;
|
|
}
|
|
SelectPalette(Console->hDC,
|
|
hPalette,
|
|
FALSE);
|
|
SetActivePalette(ScreenInfo);
|
|
|
|
if (bReset == TRUE) {
|
|
utudi.hThread = NULL;
|
|
NtUserSetInformationThread(NtCurrentThread(),
|
|
UserThreadUseDesktop, &utudi, sizeof(utudi));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(FE_IME)
|
|
SetUndetermineAttribute(Console);
|
|
#endif
|
|
//
|
|
// write data to screen
|
|
//
|
|
|
|
ScreenInfo->BufferInfo.TextInfo.Flags &= ~TEXT_VALID_HINT;
|
|
WriteToScreen(ScreenInfo,&ScreenInfo->Window);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
SetProcessFocus(
|
|
IN PCSR_PROCESS Process,
|
|
IN BOOL Foreground
|
|
)
|
|
{
|
|
if (Foreground) {
|
|
CsrSetForegroundPriority(Process);
|
|
} else {
|
|
CsrSetBackgroundPriority(Process);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SetProcessForegroundRights(
|
|
IN PCSR_PROCESS Process,
|
|
IN BOOL Foreground
|
|
)
|
|
{
|
|
USERTHREAD_FLAGS Flags;
|
|
|
|
Flags.dwMask = (W32PF_ALLOWSETFOREGROUND | W32PF_CONSOLEHASFOCUS);
|
|
Flags.dwFlags = (Foreground ? (W32PF_ALLOWSETFOREGROUND | W32PF_CONSOLEHASFOCUS) : 0);
|
|
|
|
NtUserSetInformationProcess(Process->ProcessHandle, UserProcessFlags, &Flags, sizeof(Flags));
|
|
}
|
|
|
|
VOID
|
|
ModifyConsoleProcessFocus(
|
|
IN PCONSOLE_INFORMATION Console,
|
|
IN BOOL Foreground
|
|
)
|
|
{
|
|
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;
|
|
SetProcessFocus(ProcessHandleRecord->Process, Foreground);
|
|
SetProcessForegroundRights(ProcessHandleRecord->Process, Foreground);
|
|
}
|
|
}
|
|
|
|
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, (SIZE_T)-1, (SIZE_T)-1);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
QueueConsoleMessage(
|
|
PCONSOLE_INFORMATION Console,
|
|
UINT Message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This inserts a message into the console's message queue and wakes up
|
|
the console input thread to process it.
|
|
|
|
Arguments:
|
|
|
|
Console - Pointer to console information structure.
|
|
|
|
Message - Message to store in queue.
|
|
|
|
wParam - wParam to store in queue.
|
|
|
|
lParam - lParam to store in queue.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if everything is OK.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONSOLE_MSG pConMsg;
|
|
|
|
ASSERT(ConsoleLocked(Console));
|
|
|
|
pConMsg = ConsoleHeapAlloc(TMP_TAG, sizeof(CONSOLE_MSG));
|
|
if (pConMsg == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
pConMsg->Message = Message;
|
|
pConMsg->wParam = wParam;
|
|
pConMsg->lParam = lParam;
|
|
|
|
InsertHeadList(&Console->MessageQueue, &pConMsg->ListLink);
|
|
|
|
if (!PostMessage(Console->hWnd, CM_CONSOLE_MSG, 0, 0)) {
|
|
RemoveEntryList(&pConMsg->ListLink);
|
|
ConsoleHeapFree(pConMsg);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOL
|
|
UnqueueConsoleMessage(
|
|
PCONSOLE_INFORMATION Console,
|
|
UINT *pMessage,
|
|
WPARAM *pwParam,
|
|
LPARAM *plParam
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes a message from the console's message queue.
|
|
|
|
Arguments:
|
|
|
|
Console - Pointer to console information structure.
|
|
|
|
pMessage - Pointer in which to return Message.
|
|
|
|
pwParam - Pointer in which to return wParam.
|
|
|
|
plParam - Pointer in which to return lParam.
|
|
|
|
Return Value:
|
|
|
|
BOOL - TRUE if message was found and FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
PCONSOLE_MSG pConMsg = NULL;
|
|
|
|
ASSERT(ConsoleLocked(Console));
|
|
|
|
if (IsListEmpty(&Console->MessageQueue)) {
|
|
return FALSE;
|
|
}
|
|
|
|
pEntry = RemoveTailList(&Console->MessageQueue);
|
|
pConMsg = CONTAINING_RECORD(pEntry, CONSOLE_MSG, ListLink);
|
|
|
|
*pMessage = pConMsg->Message;
|
|
*pwParam = pConMsg->wParam;
|
|
*plParam = pConMsg->lParam;
|
|
|
|
ConsoleHeapFree(pConMsg);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
CleanupConsoleMessages(
|
|
PCONSOLE_INFORMATION Console
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cleans up any console messages still in the queue.
|
|
|
|
Arguments:
|
|
|
|
Console - Pointer to console information structure.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT Message;
|
|
WPARAM wParam;
|
|
LPARAM lParam;
|
|
|
|
while (UnqueueConsoleMessage(Console, &Message, &wParam, &lParam)) {
|
|
switch (Message) {
|
|
case CM_MODE_TRANSITION:
|
|
NtSetEvent((HANDLE)lParam, NULL);
|
|
NtClose((HANDLE)lParam);
|
|
break;
|
|
case CM_SET_IME_CODEPAGE:
|
|
case CM_SET_NLSMODE:
|
|
case CM_GET_NLSMODE:
|
|
if (wParam) {
|
|
NtSetEvent((HANDLE)wParam, NULL);
|
|
NtClose((HANDLE)wParam);
|
|
}
|
|
break;
|
|
case EVENT_CONSOLE_CARET:
|
|
case EVENT_CONSOLE_UPDATE_REGION:
|
|
case EVENT_CONSOLE_UPDATE_SIMPLE:
|
|
case EVENT_CONSOLE_UPDATE_SCROLL:
|
|
case EVENT_CONSOLE_LAYOUT:
|
|
case EVENT_CONSOLE_START_APPLICATION:
|
|
case EVENT_CONSOLE_END_APPLICATION:
|
|
break;
|
|
default:
|
|
RIPMSG1(RIP_ERROR,
|
|
"CleanupConsoleMessages - unknown message 0x%x",
|
|
Message);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ConsoleNotifyWinEvent(
|
|
IN PCONSOLE_INFORMATION Console,
|
|
IN DWORD Event,
|
|
IN LONG idObjectType,
|
|
IN LONG idObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If this routine is called by the console input thread, it can notify the
|
|
system about the event by calling NotifyWinEvent directly. Otherwise, it
|
|
queues the event up for the input thread to deal with.
|
|
|
|
Arguments:
|
|
|
|
Console - Pointer to console information structure.
|
|
|
|
Event - Event that occurred.
|
|
|
|
idObjectType - Additional data about the event.
|
|
|
|
idObject - Additional data about the event.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// If no one's listening then there's no reason to send the winevent.
|
|
//
|
|
if (!IsWinEventHookInstalled(Event)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Due to the asynchronous nature of console creation, it's possible we'll get
|
|
// here but the InputThreadInfo pointer hasn't been set yet. If that's the case,
|
|
// we're certainly not the ConsoleInputThread, so conceptually we'd want to queue
|
|
// up the winevent anyway.
|
|
//
|
|
if (Console->InputThreadInfo != NULL &&
|
|
HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) == Console->InputThreadInfo->ThreadId) {
|
|
NotifyWinEvent(Event, Console->hWnd, idObjectType, idObject);
|
|
} else {
|
|
QueueConsoleMessage(Console, Event, idObjectType, idObject);
|
|
}
|
|
}
|
|
|
|
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);
|
|
ConsoleHeapFree(Console->Title);
|
|
ConsoleHeapFree(Console->OriginalTitle);
|
|
NtClose(Console->InitEvents[INITIALIZATION_SUCCEEDED]);
|
|
NtClose(Console->InitEvents[INITIALIZATION_FAILED]);
|
|
NtClose(Console->TerminationEvent);
|
|
FreeAliasBuffers(Console);
|
|
FreeCommandHistoryBuffers(Console);
|
|
#if defined(FE_SB)
|
|
FreeLocalEUDC(Console);
|
|
DestroyFontCache(Console->FontCacheInformation);
|
|
#endif
|
|
DestroyConsole(Console);
|
|
}
|
|
|
|
VOID
|
|
DestroyWindowsWindow(
|
|
IN PCONSOLE_INFORMATION Console
|
|
)
|
|
{
|
|
PSCREEN_INFORMATION Cur,Next;
|
|
HWND hWnd = Console->hWnd;
|
|
|
|
gnConsoleWindows--;
|
|
Console->InputThreadInfo->WindowCount--;
|
|
|
|
SetWindowConsole(hWnd, NULL);
|
|
|
|
KillTimer(Console->hWnd, CURSOR_TIMER);
|
|
|
|
if (Console->hWndProperties) {
|
|
SendMessage(Console->hWndProperties, WM_CLOSE, 0, 0);
|
|
}
|
|
|
|
// FE_SB
|
|
if (Console->FonthDC) {
|
|
ReleaseDC(NULL, Console->FonthDC);
|
|
DeleteObject(Console->hBitmap);
|
|
}
|
|
DeleteEUDC(Console);
|
|
|
|
// FE_IME
|
|
if (CONSOLE_IS_IME_ENABLED()) {
|
|
if (!(Console->Flags & CONSOLE_NO_WINDOW)) {
|
|
KillTimer(Console->hWnd, SCROLL_WAIT_TIMER);
|
|
}
|
|
ConsoleImeMessagePump(Console,
|
|
CONIME_DESTROY,
|
|
(WPARAM)Console->ConsoleHandle,
|
|
(LPARAM)NULL
|
|
);
|
|
}
|
|
|
|
CleanupConsoleMessages(Console);
|
|
|
|
ReleaseDC(NULL, Console->hDC);
|
|
Console->hDC = NULL;
|
|
|
|
DestroyWindow(Console->hWnd);
|
|
Console->hWnd = NULL;
|
|
|
|
//
|
|
// Tell the worker thread that the window is destroyed.
|
|
//
|
|
|
|
ReplyMessage(0);
|
|
|
|
//
|
|
// Clear out any keyboard messages we have stored away.
|
|
//
|
|
|
|
ClearKeyInfo(hWnd);
|
|
|
|
if (Console->hIcon != NULL && Console->hIcon != ghDefaultIcon) {
|
|
DestroyIcon(Console->hIcon);
|
|
}
|
|
if (Console->hSmIcon != NULL && Console->hSmIcon != ghDefaultSmIcon) {
|
|
DestroyIcon(Console->hSmIcon);
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
ConsoleHeapFree(Console->Title);
|
|
ConsoleHeapFree(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 defined(FE_SB)
|
|
FreeLocalEUDC(Console);
|
|
DestroyFontCache(Console->FontCacheInformation);
|
|
#endif
|
|
|
|
DestroyConsole(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:
|
|
#if defined(FE_IME)
|
|
// MSKK July.22.1993 KazuM
|
|
// Plan of bottom line reservation for console IME.
|
|
if (ScreenInfo->Console->InputBuffer.ImeMode.Open) {
|
|
ASSERT(ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER);
|
|
if (!(ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER)) {
|
|
return;
|
|
}
|
|
NewOrigin.Y-=CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-2;
|
|
ScreenInfo->BufferInfo.TextInfo.Flags &= ~TEXT_VALID_HINT;
|
|
ScreenInfo->BufferInfo.TextInfo.Flags |= CONSOLE_CONVERSION_AREA_REDRAW;
|
|
}
|
|
else
|
|
#endif // FE_IME
|
|
NewOrigin.Y-=CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-1;
|
|
break;
|
|
case SB_PAGEDOWN:
|
|
#if defined(FE_IME)
|
|
// MSKK July.22.1993 KazuM
|
|
// Plan of bottom line reservation for console IME.
|
|
if ( ScreenInfo->Console->InputBuffer.ImeMode.Open )
|
|
{
|
|
NewOrigin.Y+=CONSOLE_WINDOW_SIZE_Y(ScreenInfo)-2;
|
|
ScreenInfo->BufferInfo.TextInfo.Flags &= ~TEXT_VALID_HINT;
|
|
ScreenInfo->BufferInfo.TextInfo.Flags |= CONSOLE_CONVERSION_AREA_REDRAW;
|
|
}
|
|
else
|
|
#endif // FE_IME
|
|
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);
|
|
NewOrigin.Y= AbsoluteChange;
|
|
break;
|
|
case SB_TOP:
|
|
NewOrigin.Y=0;
|
|
break;
|
|
case SB_BOTTOM:
|
|
NewOrigin.Y=(WORD)(ScreenInfo->ScreenBufferSize.Y-CONSOLE_WINDOW_SIZE_Y(ScreenInfo));
|
|
break;
|
|
|
|
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 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:
|
|
case SB_THUMBPOSITION:
|
|
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;
|
|
|
|
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
|
|
);
|
|
}
|
|
|
|
/*
|
|
* If guCaretBlinkTime is -1, we don't want to blink the caret. However, we
|
|
* need to make sure it gets drawn, so we'll set a short timer. When that
|
|
* goes off, we'll hit CursorTimerRoutine, and it'll do the right thing if
|
|
* guCaretBlinkTime is -1.
|
|
*/
|
|
VOID SetCaretTimer(
|
|
HWND hWnd)
|
|
{
|
|
static CONST DWORD dwDefTimeout = 0x212;
|
|
|
|
SetTimer(hWnd,
|
|
CURSOR_TIMER,
|
|
guCaretBlinkTime == -1 ? dwDefTimeout : guCaretBlinkTime,
|
|
NULL);
|
|
}
|
|
|
|
LRESULT APIENTRY
|
|
ConsoleWindowProc(
|
|
HWND hWnd,
|
|
UINT Message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
HDC hDC;
|
|
PAINTSTRUCT ps;
|
|
PCONSOLE_INFORMATION Console;
|
|
PSCREEN_INFORMATION ScreenInfo;
|
|
SMALL_RECT PaintRect;
|
|
LRESULT Status = 0;
|
|
BOOL Unlock = TRUE;
|
|
|
|
Console = GetWindowConsole(hWnd);
|
|
if (Console != NULL) {
|
|
//
|
|
// Set up our thread so we can impersonate the client
|
|
// while processing the message.
|
|
//
|
|
|
|
CSR_SERVER_QUERYCLIENTTHREAD()->ThreadHandle =
|
|
Console->ClientThreadHandle;
|
|
|
|
//
|
|
// If the console is terminating, don't bother processing messages
|
|
// other than CM_DESTROY_WINDOW.
|
|
//
|
|
if (Console->Flags & CONSOLE_TERMINATING) {
|
|
LockConsole(Console);
|
|
DestroyWindowsWindow(Console);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Make sure the console pointer is still valid
|
|
//
|
|
ASSERT(NT_SUCCESS(ValidateConsole(Console)));
|
|
|
|
LockConsole(Console);
|
|
|
|
ScreenInfo = Console->CurrentScreenBuffer;
|
|
}
|
|
try {
|
|
if (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;
|
|
|
|
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. 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;
|
|
|
|
if (!(Console->Flags & CONSOLE_TERMINATING)) {
|
|
ConsoleHandle = Console->ConsoleHandle;
|
|
Console->hWndProgMan = (HWND)wParam;
|
|
UnlockConsole(Console);
|
|
App = GlobalAddAtomA("Shell");
|
|
Topic = GlobalAddAtomA("AppIcon");
|
|
SendMessage(Console->hWndProgMan,
|
|
WM_DDE_INITIATE,
|
|
(WPARAM)hWnd,
|
|
MAKELONG(App, Topic)
|
|
);
|
|
|
|
// If the console is still valid, continue getting icon.
|
|
Status = RevalidateConsole(ConsoleHandle, &Console);
|
|
if (NT_SUCCESS(Status)) {
|
|
Console->bIconInit = FALSE;
|
|
lpItem = _itoa((int)Console->iIconId, szItem, 10);
|
|
aItem = GlobalAddAtomA(lpItem);
|
|
PostMessage(Console->hWndProgMan,
|
|
WM_DDE_REQUEST,
|
|
(WPARAM)hWnd,
|
|
MAKELONG(CF_TEXT, aItem));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
switch (Message) {
|
|
case WM_DROPFILES:
|
|
DoDrop (wParam,Console);
|
|
break;
|
|
case WM_MOVE:
|
|
if (!IsIconic(hWnd)) {
|
|
PositionConsoleWindow(Console, (Console->WindowRect.left == CW_USEDEFAULT));
|
|
#if defined(FE_IME)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console)) {
|
|
ConsoleImeResizeModeSystemView(Console,ScreenInfo->Window);
|
|
ConsoleImeResizeCompStrView(Console,ScreenInfo->Window);
|
|
}
|
|
#endif // FE_IME
|
|
}
|
|
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) {
|
|
ScreenInfo->WindowMaximized = (wParam == SIZE_MAXIMIZED);
|
|
|
|
if (Console->ResizeFlags & SCREEN_BUFFER_CHANGE) {
|
|
UpdateWindowSize(Console,ScreenInfo);
|
|
}
|
|
PositionConsoleWindow(Console, (Console->WindowRect.left == CW_USEDEFAULT));
|
|
#if defined(FE_IME)
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console)) {
|
|
ConsoleImeResizeModeSystemView(Console,ScreenInfo->Window);
|
|
ConsoleImeResizeCompStrView(Console,ScreenInfo->Window);
|
|
}
|
|
#endif // FE_IME
|
|
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;
|
|
WPARAM atomTemp;
|
|
|
|
UnpackDDElParam(WM_DDE_DATA, lParam, (WPARAM *)&hDdeData, &atomTemp);
|
|
|
|
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, (LPARAM)hIcon);
|
|
|
|
if (Console->hSmIcon != NULL) {
|
|
if (Console->hSmIcon != ghDefaultSmIcon) {
|
|
DestroyIcon(Console->hSmIcon);
|
|
}
|
|
Console->hSmIcon = NULL;
|
|
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)NULL);
|
|
}
|
|
}
|
|
|
|
if (lpDDEData->fAckReq) {
|
|
|
|
PostMessage(Console->hWndProgMan,
|
|
WM_DDE_ACK,
|
|
(WPARAM)hWnd,
|
|
ReuseDDElParam(lParam, WM_DDE_DATA, WM_DDE_ACK, 0x8000, atomTemp));
|
|
}
|
|
|
|
bRelease = lpDDEData->fRelease;
|
|
GlobalUnlock(hDdeData);
|
|
if (bRelease){
|
|
GlobalFree(hDdeData);
|
|
}
|
|
PostMessage(Console->hWndProgMan,
|
|
WM_DDE_TERMINATE,
|
|
(WPARAM)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;
|
|
// FE_IME
|
|
case CM_CONIME_KL_ACTIVATE:
|
|
ActivateKeyboardLayout((HKL)wParam, KLF_SETFORPROCESS);
|
|
break;
|
|
case WM_INPUTLANGCHANGEREQUEST:
|
|
if (CONSOLE_IS_IME_ENABLED()) {
|
|
ULONG ConimeMessage;
|
|
LRESULT lResult;
|
|
|
|
if (wParam & INPUTLANGCHANGE_BACKWARD) {
|
|
ConimeMessage = CONIME_INPUTLANGCHANGEREQUESTBACKWARD;
|
|
} else if (wParam & INPUTLANGCHANGE_FORWARD) {
|
|
ConimeMessage = CONIME_INPUTLANGCHANGEREQUESTFORWARD;
|
|
} else {
|
|
ConimeMessage = CONIME_INPUTLANGCHANGEREQUEST;
|
|
}
|
|
|
|
if (!NT_SUCCESS(ConsoleImeMessagePumpWorker(Console,
|
|
ConimeMessage,
|
|
(WPARAM)Console->ConsoleHandle,
|
|
(LPARAM)lParam,
|
|
&lResult)) ||
|
|
!lResult) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
#ifdef LATER
|
|
else if (IS_IME_KBDLAYOUT(lParam)) {
|
|
// IME keyboard layout should be avoided
|
|
// if the console is not IME enabled.
|
|
break;
|
|
}
|
|
// Call the default window proc and let it handle
|
|
// the keyboard layout activation.
|
|
#endif
|
|
goto CallDefWin;
|
|
|
|
break;
|
|
// end FE_IME
|
|
|
|
case WM_INPUTLANGCHANGE:
|
|
Console->hklActive = (HKL)lParam;
|
|
// FE_IME
|
|
if (CONSOLE_IS_IME_ENABLED()) {
|
|
if (!NT_SUCCESS(ConsoleImeMessagePump(Console,
|
|
CONIME_INPUTLANGCHANGE,
|
|
(WPARAM)Console->ConsoleHandle,
|
|
(LPARAM)Console->hklActive
|
|
))) {
|
|
break;
|
|
} else{
|
|
GetImeKeyState(Console, NULL) ;
|
|
}
|
|
}
|
|
// end FE_IME
|
|
goto CallDefWin;
|
|
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
ModifyConsoleProcessFocus(Console, TRUE);
|
|
SetConsoleReserveKeys(hWnd, Console->ReserveKeys);
|
|
Console->Flags |= CONSOLE_HAS_FOCUS;
|
|
|
|
SetCaretTimer(hWnd);
|
|
|
|
HandleFocusEvent(Console, TRUE);
|
|
if (!Console->hklActive) {
|
|
SystemParametersInfo(SPI_GETDEFAULTINPUTLANG, 0, &Console->hklActive, FALSE);
|
|
GetNonBiDiKeyboardLayout(&Console->hklActive);
|
|
}
|
|
ActivateKeyboardLayout(Console->hklActive, 0);
|
|
// FE_IME
|
|
if (CONSOLE_IS_IME_ENABLED()) {
|
|
// v-HirShi Sep.15.1995 Support Console IME
|
|
if (!NT_SUCCESS(ConsoleImeMessagePump(Console,
|
|
CONIME_SETFOCUS,
|
|
(WPARAM)Console->ConsoleHandle,
|
|
(LPARAM)Console->hklActive
|
|
))) {
|
|
break;
|
|
}
|
|
|
|
if (Console->InputBuffer.hWndConsoleIME) {
|
|
/*
|
|
* Open property window by ImmConfigureIME.
|
|
* Never set focus on console window
|
|
* so, set focus to property window.
|
|
*/
|
|
HWND hwnd = GetLastActivePopup(Console->InputBuffer.hWndConsoleIME);
|
|
if (hwnd != NULL)
|
|
SetForegroundWindow(hwnd);
|
|
}
|
|
}
|
|
// FE_IME
|
|
break;
|
|
case WM_KILLFOCUS:
|
|
ModifyConsoleProcessFocus(Console, FALSE);
|
|
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);
|
|
|
|
// FE_IME
|
|
if (CONSOLE_IS_IME_ENABLED()) {
|
|
// v-HirShi Sep.16.1995 Support Console IME
|
|
if (!NT_SUCCESS(ConsoleImeMessagePump(Console,
|
|
CONIME_KILLFOCUS,
|
|
(WPARAM)Console->ConsoleHandle,
|
|
(LPARAM)NULL
|
|
))) {
|
|
break;
|
|
}
|
|
}
|
|
// end FE_IME
|
|
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 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:
|
|
{
|
|
DWORD dwCaretBlinkTime = GetCaretBlinkTime();
|
|
|
|
if (dwCaretBlinkTime != guCaretBlinkTime) {
|
|
KillTimer(hWnd, CURSOR_TIMER);
|
|
guCaretBlinkTime = dwCaretBlinkTime;
|
|
SetCaretTimer(hWnd);
|
|
}
|
|
}
|
|
/* Fall through */
|
|
|
|
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;
|
|
WINDOW_LIMITS WindowLimits;
|
|
|
|
GetWindowLimits(ScreenInfo, &WindowLimits);
|
|
if (ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
FontSize = SCR_FONTSIZE(ScreenInfo);
|
|
} else {
|
|
FontSize.X = 1;
|
|
FontSize.Y = 1;
|
|
}
|
|
lpmmi->ptMaxSize.x = lpmmi->ptMaxTrackSize.x = WindowLimits.MaxWindow.X;
|
|
if (!ScreenInfo->WindowMaximizedY) {
|
|
lpmmi->ptMaxTrackSize.x += VerticalScrollSize;
|
|
lpmmi->ptMaxSize.x += VerticalScrollSize;
|
|
}
|
|
while (lpmmi->ptMaxSize.x > WindowLimits.FullScreenSize.X + VerticalClientToWindow) {
|
|
lpmmi->ptMaxSize.x -= FontSize.X;
|
|
}
|
|
lpmmi->ptMaxSize.y = lpmmi->ptMaxTrackSize.y = WindowLimits.MaxWindow.Y;
|
|
if (!ScreenInfo->WindowMaximizedX) {
|
|
lpmmi->ptMaxTrackSize.y += HorizontalScrollSize;
|
|
lpmmi->ptMaxSize.y += HorizontalScrollSize;
|
|
}
|
|
while (lpmmi->ptMaxSize.y > WindowLimits.FullScreenSize.Y + HorizontalClientToWindow) {
|
|
lpmmi->ptMaxSize.y -= FontSize.Y;
|
|
}
|
|
lpmmi->ptMinTrackSize.x = WindowLimits.MinimumWindowSize.X * FontSize.X + VerticalClientToWindow;
|
|
lpmmi->ptMinTrackSize.y = HorizontalClientToWindow;
|
|
}
|
|
break;
|
|
case WM_QUERYDRAGICON:
|
|
Status = (LRESULT)Console->hIcon;
|
|
break;
|
|
case WM_WINDOWPOSCHANGING:
|
|
{
|
|
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_CONTEXTMENU:
|
|
if (DefWindowProc(hWnd, WM_NCHITTEST, 0, lParam) == HTCLIENT) {
|
|
TrackPopupMenuEx(Console->hHeirMenu,
|
|
TPM_RIGHTBUTTON,
|
|
GET_X_LPARAM(lParam),
|
|
GET_Y_LPARAM(lParam),
|
|
hWnd,
|
|
NULL);
|
|
} else {
|
|
goto CallDefWin;
|
|
}
|
|
break;
|
|
case WM_NCLBUTTONDOWN:
|
|
// allow user to move window even when bigger than the screen
|
|
switch (wParam & 0x00FF) {
|
|
case HTCAPTION:
|
|
UnlockConsole(Console);
|
|
Unlock = FALSE;
|
|
SetActiveWindow(hWnd);
|
|
SendMessage(hWnd, WM_SYSCOMMAND,
|
|
SC_MOVE | wParam, lParam);
|
|
break;
|
|
default:
|
|
goto CallDefWin;
|
|
}
|
|
break;
|
|
#if defined (FE_IME)
|
|
// Sep.16.1995 Support Console IME
|
|
case WM_KEYDOWN +CONIME_KEYDATA:
|
|
case WM_KEYUP +CONIME_KEYDATA:
|
|
case WM_CHAR +CONIME_KEYDATA:
|
|
case WM_DEADCHAR +CONIME_KEYDATA:
|
|
|
|
case WM_SYSKEYDOWN +CONIME_KEYDATA:
|
|
case WM_SYSKEYUP +CONIME_KEYDATA:
|
|
case WM_SYSCHAR +CONIME_KEYDATA:
|
|
case WM_SYSDEADCHAR+CONIME_KEYDATA:
|
|
#endif
|
|
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, &Unlock)) {
|
|
goto CallDefWin;
|
|
}
|
|
break;
|
|
case WM_COMMAND:
|
|
//
|
|
// If this is an edit command from the context menu, treat
|
|
// it like a sys command.
|
|
//
|
|
if ((wParam < cmCopy) || (wParam > cmSelectAll)) {
|
|
break;
|
|
}
|
|
// FALL THRU
|
|
case WM_SYSCOMMAND:
|
|
if (wParam >= ScreenInfo->CommandIdLow &&
|
|
wParam <= ScreenInfo->CommandIdHigh) {
|
|
HandleMenuEvent(Console, (DWORD)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 == cmFind) {
|
|
DoFind(Console);
|
|
} else if (wParam == cmSelectAll) {
|
|
DoSelectAll(Console);
|
|
} else if (wParam == cmControl) {
|
|
PropertiesDlgShow(Console, TRUE);
|
|
} else if (wParam == cmDefaults) {
|
|
PropertiesDlgShow(Console, FALSE);
|
|
} else if ((wParam == SC_RESTORE || wParam == SC_MAXIMIZE) &&
|
|
Console->Flags & CONSOLE_VDM_HIDDEN_WINDOW) {
|
|
Console->Flags &= ~CONSOLE_VDM_HIDDEN_WINDOW;
|
|
SendMessage(Console->hWnd, CM_MODE_TRANSITION, FULLSCREEN, 0L);
|
|
} else {
|
|
goto CallDefWin;
|
|
}
|
|
break;
|
|
case WM_TIMER:
|
|
#if defined(FE_IME)
|
|
if (wParam == SCROLL_WAIT_TIMER) {
|
|
ASSERT(CONSOLE_IS_IME_ENABLED());
|
|
if ((ScreenInfo->Console->ConsoleIme.ScrollFlag & (HIDE_FOR_SCROLL)) &&
|
|
(ScreenInfo->Console->ConsoleIme.ScrollWaitCountDown > 0)
|
|
) {
|
|
if ((ScreenInfo->Console->ConsoleIme.ScrollWaitCountDown -= guCaretBlinkTime) <= 0) {
|
|
ConsoleImeBottomLineInUse(ScreenInfo);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
CursorTimerRoutine(ScreenInfo);
|
|
ScrollIfNecessary(Console, ScreenInfo);
|
|
break;
|
|
case WM_HSCROLL:
|
|
HorizontalScroll(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:
|
|
case WM_MOUSEWHEEL:
|
|
if (HandleMouseEvent(Console, ScreenInfo, Message, wParam, lParam)) {
|
|
if (Message != WM_MOUSEWHEEL) {
|
|
goto CallDefWin;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Don't handle zoom.
|
|
*/
|
|
if (wParam & 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. If in shift scroll mode then scroll one page at
|
|
* a time regardless.
|
|
*/
|
|
|
|
if (!(wParam & MK_SHIFT)) {
|
|
dy = (int) min(
|
|
(UINT) CONSOLE_WINDOW_SIZE_Y(ScreenInfo) - 1,
|
|
gucWheelScrollLines);
|
|
} else {
|
|
dy = CONSOLE_WINDOW_SIZE_Y(ScreenInfo) - 1;
|
|
}
|
|
|
|
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).
|
|
//
|
|
|
|
RIPMSG0(RIP_WARNING, "WindowProc - WM_FULLSCREEN");
|
|
|
|
Status = DisplayModeTransition(wParam,Console,ScreenInfo);
|
|
#if defined(FE_IME)
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ImeWmFullScreen(wParam,Console,ScreenInfo);
|
|
}
|
|
#endif // FE_IME
|
|
break;
|
|
#endif
|
|
case CM_SET_WINDOW_SIZE:
|
|
if (lParam == 0x47474747) {
|
|
Status = InternalSetWindowSize(Console,
|
|
(PSCREEN_INFORMATION)wParam,
|
|
&ScreenInfo->Window
|
|
);
|
|
}
|
|
break;
|
|
case CM_BEEP:
|
|
if (lParam == 0x47474747) {
|
|
Beep(800, 200);
|
|
}
|
|
break;
|
|
case CM_UPDATE_SCROLL_BARS:
|
|
InternalUpdateScrollBars(ScreenInfo);
|
|
break;
|
|
case CM_UPDATE_TITLE:
|
|
SetWindowText(hWnd, Console->Title);
|
|
break;
|
|
case CM_CONSOLE_MSG:
|
|
if (!UnqueueConsoleMessage(Console, &Message, &wParam, &lParam)) {
|
|
break;
|
|
}
|
|
switch (Message) {
|
|
|
|
#if defined(_X86_)
|
|
case CM_MODE_TRANSITION:
|
|
|
|
RIPMSG0(RIP_WARNING, "WindowProc - CM_MODE_TRANSITION");
|
|
|
|
if (wParam == FULLSCREEN) {
|
|
if (Console->FullScreenFlags == 0) {
|
|
ConvertToFullScreen(Console);
|
|
Console->FullScreenFlags |= CONSOLE_FULLSCREEN;
|
|
ChangeDispSettings(Console, hWnd, CDS_FULLSCREEN);
|
|
}
|
|
} else {
|
|
if (Console->FullScreenFlags & CONSOLE_FULLSCREEN) {
|
|
ConvertToWindowed(Console);
|
|
Console->FullScreenFlags &= ~CONSOLE_FULLSCREEN;
|
|
ChangeDispSettings(Console, hWnd, 0);
|
|
|
|
ShowWindow(hWnd, SW_RESTORE);
|
|
}
|
|
}
|
|
|
|
UnlockConsole(Console);
|
|
Unlock = FALSE;
|
|
|
|
NtSetEvent((HANDLE)lParam, NULL);
|
|
NtClose((HANDLE)lParam);
|
|
break;
|
|
#endif
|
|
#if defined (FE_IME)
|
|
case CM_SET_IME_CODEPAGE: {
|
|
if (!LOWORD(lParam)) {
|
|
// Input code page
|
|
Status = SetImeCodePage(Console);
|
|
} else {
|
|
// Output code page
|
|
Status = SetImeOutputCodePage(Console, ScreenInfo, HIWORD(lParam));
|
|
}
|
|
|
|
if (wParam) {
|
|
NtSetEvent((HANDLE)wParam, NULL);
|
|
NtClose((HANDLE)wParam);
|
|
}
|
|
break;
|
|
}
|
|
case CM_SET_NLSMODE:
|
|
Status = SetImeKeyState(Console, ImmConversionFromConsole((DWORD)lParam));
|
|
if (wParam) {
|
|
NtSetEvent((HANDLE)wParam, NULL);
|
|
NtClose((HANDLE)wParam);
|
|
}
|
|
break;
|
|
case CM_GET_NLSMODE:
|
|
if (Console->InputThreadInfo->hWndConsoleIME) {
|
|
ASSERT(CONSOLE_IS_IME_ENABLED());
|
|
|
|
if (!NT_SUCCESS(GetImeKeyState(Console, NULL))) {
|
|
if (wParam) {
|
|
NtSetEvent((HANDLE)wParam, NULL);
|
|
NtClose((HANDLE)wParam);
|
|
}
|
|
break;
|
|
}
|
|
if (wParam) {
|
|
NtSetEvent((HANDLE)wParam, NULL);
|
|
NtClose((HANDLE)wParam);
|
|
}
|
|
} else if (lParam < 10) {
|
|
/*
|
|
* try get conversion mode until ready ConIME.
|
|
*/
|
|
Status = QueueConsoleMessage(Console,
|
|
CM_GET_NLSMODE,
|
|
wParam,
|
|
lParam+1
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (wParam) {
|
|
NtSetEvent((HANDLE)wParam, NULL);
|
|
NtClose((HANDLE)wParam);
|
|
}
|
|
}
|
|
} else {
|
|
if (wParam) {
|
|
NtSetEvent((HANDLE)wParam, NULL);
|
|
NtClose((HANDLE)wParam);
|
|
}
|
|
}
|
|
break;
|
|
#endif // FE_IME
|
|
case EVENT_CONSOLE_CARET:
|
|
case EVENT_CONSOLE_UPDATE_REGION:
|
|
case EVENT_CONSOLE_UPDATE_SIMPLE:
|
|
case EVENT_CONSOLE_UPDATE_SCROLL:
|
|
case EVENT_CONSOLE_LAYOUT:
|
|
case EVENT_CONSOLE_START_APPLICATION:
|
|
case EVENT_CONSOLE_END_APPLICATION:
|
|
NotifyWinEvent(Message, hWnd, (LONG)wParam, (LONG)lParam);
|
|
break;
|
|
default:
|
|
RIPMSG1(RIP_WARNING, "Unknown console message 0x%x", Message);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
#if defined(_X86_)
|
|
case CM_MODE_TRANSITION:
|
|
/*
|
|
* This is called by win32k.sys to request a display mode
|
|
* transition.
|
|
*/
|
|
|
|
|
|
RIPMSG0(RIP_WARNING, "WindowProc - CM_MODE_TRANSITION");
|
|
if (wParam == FULLSCREEN) {
|
|
if (Console->FullScreenFlags == 0) {
|
|
ConvertToFullScreen(Console);
|
|
Console->FullScreenFlags |= CONSOLE_FULLSCREEN;
|
|
ChangeDispSettings(Console, hWnd, CDS_FULLSCREEN);
|
|
}
|
|
} else {
|
|
if (Console->FullScreenFlags & CONSOLE_FULLSCREEN) {
|
|
ConvertToWindowed(Console);
|
|
Console->FullScreenFlags &= ~CONSOLE_FULLSCREEN;
|
|
ChangeDispSettings(Console, hWnd, 0);
|
|
|
|
ShowWindow(hWnd, SW_RESTORE);
|
|
}
|
|
}
|
|
|
|
UnlockConsole(Console);
|
|
Unlock = FALSE;
|
|
|
|
break;
|
|
#endif // _X86_
|
|
|
|
case CM_HIDE_WINDOW:
|
|
ShowWindowAsync(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;
|
|
#if defined(FE_IME)
|
|
case WM_COPYDATA:
|
|
if (CONSOLE_IS_IME_ENABLED() && CONSOLE_IS_DBCS_OUTPUTCP(Console)) {
|
|
Status = ImeControl(Console,(HWND)wParam,(PCOPYDATASTRUCT)lParam);
|
|
}
|
|
break;
|
|
// v-HirShi Sep.18.1995 Support Console IME
|
|
case WM_ENTERMENULOOP:
|
|
if (Console->Flags & CONSOLE_HAS_FOCUS) {
|
|
Console->InputBuffer.ImeMode.Unavailable = TRUE;
|
|
if (CONSOLE_IS_IME_ENABLED()) {
|
|
if (!NT_SUCCESS(ConsoleImeMessagePump(Console,
|
|
CONIME_KILLFOCUS,
|
|
(WPARAM)Console->ConsoleHandle,
|
|
(LPARAM)NULL
|
|
))) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_EXITMENULOOP:
|
|
if (Console->Flags & CONSOLE_HAS_FOCUS) {
|
|
if (CONSOLE_IS_IME_ENABLED()) {
|
|
if (!NT_SUCCESS(ConsoleImeMessagePump(Console,
|
|
CONIME_SETFOCUS,
|
|
(WPARAM)Console->ConsoleHandle,
|
|
(LPARAM)Console->hklActive
|
|
))) {
|
|
break;
|
|
}
|
|
}
|
|
Console->InputBuffer.ImeMode.Unavailable = FALSE;
|
|
}
|
|
break;
|
|
|
|
case WM_ENTERSIZEMOVE:
|
|
if (Console->Flags & CONSOLE_HAS_FOCUS) {
|
|
Console->InputBuffer.ImeMode.Unavailable = TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_EXITSIZEMOVE:
|
|
if (Console->Flags & CONSOLE_HAS_FOCUS) {
|
|
Console->InputBuffer.ImeMode.Unavailable = FALSE;
|
|
}
|
|
break;
|
|
#endif // FE_IME
|
|
|
|
CallDefWin:
|
|
default:
|
|
if (Unlock && Console != NULL) {
|
|
UnlockConsole(Console);
|
|
Unlock = FALSE;
|
|
}
|
|
Status = DefWindowProc(hWnd,Message,wParam,lParam);
|
|
break;
|
|
}
|
|
}
|
|
} finally {
|
|
if (Unlock && Console != NULL) {
|
|
UnlockConsole(Console);
|
|
}
|
|
}
|
|
|
|
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 PVOID lpFile,
|
|
IN UINT cb)
|
|
{
|
|
UINT i = 0;
|
|
LPDROPFILESTRUCT lpdfs;
|
|
BOOL fWide;
|
|
|
|
lpdfs = (LPDROPFILESTRUCT)GlobalLock(hDrop);
|
|
|
|
if (lpdfs && lpdfs != hDrop) {
|
|
try {
|
|
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);
|
|
|
|
i = lstrlenW(lpList);
|
|
|
|
if (!i)
|
|
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);
|
|
|
|
i = lstrlenA(lpList);
|
|
|
|
if (!i) {
|
|
goto Exit;
|
|
}
|
|
|
|
cb--;
|
|
if (cb < i) {
|
|
i = cb;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, lpList, -1, (LPWSTR)lpFile, cb);
|
|
|
|
}
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
RIPMSG1(RIP_WARNING, "CONSRV: WM_DROPFILES raised exception 0x%x", GetExceptionCode());
|
|
i = 0;
|
|
}
|
|
Exit:
|
|
GlobalUnlock(hDrop);
|
|
GlobalFree(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];
|
|
BOOL fAddQuotes;
|
|
|
|
if (ConsoleDragQueryFile((HANDLE)wParam, szPath, ARRAY_SIZE(szPath))) {
|
|
fAddQuotes = (wcschr(szPath, L' ') != NULL);
|
|
if (fAddQuotes) {
|
|
DoStringPaste(Console, L"\"", 1);
|
|
}
|
|
DoStringPaste(Console, szPath, wcslen(szPath));
|
|
if (fAddQuotes) {
|
|
DoStringPaste(Console, L"\"", 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CreateDbcsScreenBuffer(
|
|
IN PCONSOLE_INFORMATION Console,
|
|
IN COORD dwScreenBufferSize,
|
|
OUT PDBCS_SCREEN_BUFFER DbcsScreenBuffer)
|
|
{
|
|
if (CONSOLE_IS_DBCS_OUTPUTCP(Console)) {
|
|
DbcsScreenBuffer->TransBufferCharacter =
|
|
ConsoleHeapAlloc(SCREEN_DBCS_TAG,
|
|
(dwScreenBufferSize.X * dwScreenBufferSize.Y * sizeof(WCHAR)) + sizeof(WCHAR));
|
|
if (DbcsScreenBuffer->TransBufferCharacter == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
DbcsScreenBuffer->TransBufferAttribute =
|
|
ConsoleHeapAlloc(SCREEN_DBCS_TAG,
|
|
(dwScreenBufferSize.X * dwScreenBufferSize.Y * sizeof(BYTE)) + sizeof(BYTE));
|
|
if (DbcsScreenBuffer->TransBufferAttribute == NULL) {
|
|
ConsoleHeapFree(DbcsScreenBuffer->TransBufferCharacter);
|
|
return FALSE;
|
|
}
|
|
|
|
DbcsScreenBuffer->TransWriteConsole =
|
|
ConsoleHeapAlloc(SCREEN_DBCS_TAG,
|
|
(dwScreenBufferSize.X * dwScreenBufferSize.Y * sizeof(WCHAR)) + sizeof(WCHAR));
|
|
if (DbcsScreenBuffer->TransWriteConsole == NULL) {
|
|
ConsoleHeapFree(DbcsScreenBuffer->TransBufferAttribute);
|
|
ConsoleHeapFree(DbcsScreenBuffer->TransBufferCharacter);
|
|
return FALSE;
|
|
}
|
|
|
|
DbcsScreenBuffer->KAttrRows =
|
|
ConsoleHeapAlloc(SCREEN_DBCS_TAG,
|
|
dwScreenBufferSize.X * dwScreenBufferSize.Y * sizeof(BYTE));
|
|
if (DbcsScreenBuffer->KAttrRows == NULL) {
|
|
ConsoleHeapFree(DbcsScreenBuffer->TransWriteConsole);
|
|
ConsoleHeapFree(DbcsScreenBuffer->TransBufferAttribute);
|
|
ConsoleHeapFree(DbcsScreenBuffer->TransBufferCharacter);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
DbcsScreenBuffer->TransBufferCharacter = NULL;
|
|
DbcsScreenBuffer->TransBufferAttribute = NULL;
|
|
DbcsScreenBuffer->TransWriteConsole = NULL;
|
|
DbcsScreenBuffer->KAttrRows = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
DeleteDbcsScreenBuffer(
|
|
IN PDBCS_SCREEN_BUFFER DbcsScreenBuffer
|
|
)
|
|
{
|
|
if (DbcsScreenBuffer->KAttrRows) {
|
|
ConsoleHeapFree(DbcsScreenBuffer->TransBufferCharacter);
|
|
ConsoleHeapFree(DbcsScreenBuffer->TransBufferAttribute);
|
|
ConsoleHeapFree(DbcsScreenBuffer->TransWriteConsole);
|
|
ConsoleHeapFree(DbcsScreenBuffer->KAttrRows);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ReCreateDbcsScreenBufferWorker(
|
|
IN PCONSOLE_INFORMATION Console,
|
|
IN PSCREEN_INFORMATION ScreenInfo
|
|
)
|
|
{
|
|
SHORT i;
|
|
PBYTE KAttrRowPtr;
|
|
COORD dwScreenBufferSize;
|
|
DBCS_SCREEN_BUFFER NewDbcsScreenBuffer;
|
|
|
|
ASSERT(ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER);
|
|
if (!(ScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER)) {
|
|
return FALSE;
|
|
}
|
|
|
|
dwScreenBufferSize = ScreenInfo->ScreenBufferSize;
|
|
|
|
if (!CreateDbcsScreenBuffer(Console,
|
|
dwScreenBufferSize,
|
|
&NewDbcsScreenBuffer)) {
|
|
return FALSE;
|
|
}
|
|
|
|
KAttrRowPtr = NewDbcsScreenBuffer.KAttrRows;
|
|
for (i = 0; i < dwScreenBufferSize.Y; i++) {
|
|
ScreenInfo->BufferInfo.TextInfo.Rows[i].CharRow.KAttrs = KAttrRowPtr;
|
|
if (KAttrRowPtr) {
|
|
RtlZeroMemory(KAttrRowPtr, dwScreenBufferSize.X);
|
|
KAttrRowPtr += dwScreenBufferSize.X;
|
|
}
|
|
}
|
|
ScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer = NewDbcsScreenBuffer;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
typedef struct _DBCS_SCREEN_BUFFER_TRACKER {
|
|
DBCS_SCREEN_BUFFER data;
|
|
#if DBG
|
|
PSCREEN_INFORMATION pScreenInfo;
|
|
#endif
|
|
} DBCS_SCREEN_BUFFER_TRACKER, *PDBCS_SCREEN_BUFFER_TRACKER;
|
|
|
|
BOOL
|
|
ReCreateDbcsScreenBuffer(
|
|
IN PCONSOLE_INFORMATION pConsole,
|
|
IN UINT OldCodePage)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
PDBCS_SCREEN_BUFFER_TRACKER pDbcsScreenBuffer;
|
|
PSCREEN_INFORMATION pScreenInfo;
|
|
UINT nScreen;
|
|
UINT i;
|
|
#if DBG
|
|
UINT nScreenSave;
|
|
#endif
|
|
|
|
//
|
|
// If DbcsBuffers don't need to be modified, just bail out.
|
|
//
|
|
if (!IsAvailableFarEastCodePage(OldCodePage) == !CONSOLE_IS_DBCS_OUTPUTCP(pConsole) )
|
|
return TRUE;
|
|
|
|
//
|
|
// Count the number of screens allocated.
|
|
//
|
|
for (nScreen = 0, pScreenInfo = pConsole->ScreenBuffers; pScreenInfo; pScreenInfo = pScreenInfo->Next) {
|
|
//
|
|
// Ignore graphic mode buffer.
|
|
//
|
|
if (pScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
++nScreen;
|
|
}
|
|
}
|
|
#if DBG
|
|
nScreenSave = nScreen;
|
|
#endif
|
|
|
|
//
|
|
// Allocate the temporary buffer to store the old values
|
|
//
|
|
pDbcsScreenBuffer = ConsoleHeapAlloc(TMP_DBCS_TAG, sizeof(*pDbcsScreenBuffer) * nScreen);
|
|
if (pDbcsScreenBuffer == NULL) {
|
|
RIPMSG0(RIP_WARNING, "ReCreateDbcsScreenBuffer: not enough memory.");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Try to allocate or de-allocate the necessary DBCS buffers
|
|
//
|
|
for (nScreen = 0, pScreenInfo = pConsole->ScreenBuffers; pScreenInfo; pScreenInfo = pScreenInfo->Next) {
|
|
ASSERT(nScreen < nScreenSave); // make sure ScreenBuffers are not changed
|
|
|
|
//
|
|
// We only handle the text mode screen buffer.
|
|
//
|
|
if (pScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
//
|
|
// Save the previous value just in case something goes bad.
|
|
//
|
|
#if DBG
|
|
pDbcsScreenBuffer[nScreen].pScreenInfo = pScreenInfo;
|
|
#endif
|
|
pDbcsScreenBuffer[nScreen++].data = pScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer;
|
|
|
|
if (!ReCreateDbcsScreenBufferWorker(pConsole, pScreenInfo)) {
|
|
//
|
|
// If we fail to ReCreate the DbcsScreenBuffer,
|
|
// free all allocation to this point, and restore the orginal.
|
|
//
|
|
RIPMSG0(RIP_WARNING, "ReCreateDbcsScreenBuffer: failed to recreate dbcs screen buffer.");
|
|
|
|
for (i = 0, pScreenInfo = pConsole->ScreenBuffers; i < nScreen; pScreenInfo = pScreenInfo->Next) {
|
|
ASSERT(pScreenInfo);
|
|
|
|
if (pScreenInfo->Flags & CONSOLE_TEXTMODE_BUFFER) {
|
|
ASSERT(pDbcsScreenBuffer[i].pScreenInfo == pScreenInfo);
|
|
if (i < nScreen - 1) {
|
|
ASSERT(pScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer.KAttrRows != pDbcsScreenBuffer[i].data.KAttrRows);
|
|
DeleteDbcsScreenBuffer(&pScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer);
|
|
}
|
|
|
|
pScreenInfo->BufferInfo.TextInfo.DbcsScreenBuffer = pDbcsScreenBuffer[i++].data;
|
|
}
|
|
}
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// All allocation succeeded. Now we can delete the old allocation.
|
|
//
|
|
for (i = 0; i < nScreen; ++i) {
|
|
DeleteDbcsScreenBuffer(&pDbcsScreenBuffer[i].data);
|
|
}
|
|
|
|
fResult = TRUE;
|
|
|
|
exit:
|
|
ConsoleHeapFree(pDbcsScreenBuffer);
|
|
|
|
return fResult;
|
|
}
|
|
|
|
// Checks if the primary language of this keyborad layout is BiDi or not.
|
|
BOOL
|
|
IsNotBiDILayout(
|
|
HKL hkl)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
LANGID LangID = PRIMARYLANGID(HandleToUlong(hkl));
|
|
|
|
if (LangID == LANG_ARABIC || LangID == LANG_HEBREW) {
|
|
bRet = FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
VOID
|
|
GetNonBiDiKeyboardLayout(
|
|
HKL *phklActive)
|
|
{
|
|
HKL hkl = *phklActive;
|
|
HKL hklActive = *phklActive;
|
|
|
|
if (IsNotBiDILayout(hkl)) {
|
|
return;
|
|
}
|
|
|
|
// Start with the default one.
|
|
ActivateKeyboardLayout(hkl, 0);
|
|
// We know that the default is not good, activate the next.
|
|
ActivateKeyboardLayout((HKL)HKL_NEXT, 0);
|
|
|
|
// Loop until you find a none BiDi one or endof list.
|
|
while (hkl = GetKeyboardLayout(0)) {
|
|
if ((hkl == hklActive) || IsNotBiDILayout(hkl)) {
|
|
*phklActive = hkl;
|
|
break;
|
|
}
|
|
ActivateKeyboardLayout((HKL)HKL_NEXT, 0);
|
|
}
|
|
}
|
|
|
|
#if defined(FE_SB)
|
|
|
|
#define WWSB_NOFE
|
|
#include "_output.h"
|
|
#undef WWSB_NOFE
|
|
#define WWSB_FE
|
|
#include "_output.h"
|
|
#undef WWSB_FE
|
|
|
|
#endif // FE_SB
|