private static void WriteBlankLines(int count, int top) { var blanks = new CHAR_INFO[count * Console.BufferWidth]; for (int i = 0; i < blanks.Length; i++) { blanks[i].BackgroundColor = Console.BackgroundColor; blanks[i].ForegroundColor = Console.ForegroundColor; blanks[i].UnicodeChar = ' '; } WriteBufferLines(blanks, ref top); }
public void ScrollBuffer(int lines) { var handle = NativeMethods.GetStdHandle((uint)StandardHandleId.Output); var scrollRectangle = new SMALL_RECT { Top = (short)lines, Left = 0, Bottom = (short)(Console.BufferHeight - 1), Right = (short)Console.BufferWidth }; var destinationOrigin = new COORD { X = 0, Y = 0 }; var fillChar = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor); NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, IntPtr.Zero, destinationOrigin, ref fillChar); }
private void MaybeEmphasize(ref CHAR_INFO charInfo, int i, ConsoleColor foregroundColor, ConsoleColor backgroundColor) { if (i >= _emphasisStart && i < (_emphasisStart + _emphasisLength)) { backgroundColor = _options.EmphasisBackgroundColor; foregroundColor = _options.EmphasisForegroundColor; } else if (_visualSelectionCommandCount > 0 && InRegion(i)) { // We can't quite emulate real console selection because it inverts // based on actual screen colors, our pallete is limited. The choice // to invert only the lower 3 bits to change the color is somewhat // but looks best with the 2 default color schemes - starting PowerShell // from it's shortcut or from a cmd shortcut. foregroundColor = (ConsoleColor)((int)foregroundColor ^ 7); backgroundColor = (ConsoleColor)((int)backgroundColor ^ 7); } charInfo.ForegroundColor = foregroundColor; charInfo.BackgroundColor = backgroundColor; }
public CHAR_INFO[] ReadBufferLines(int top, int count) { var result = new CHAR_INFO[BufferWidth * count]; var handle = NativeMethods.GetStdHandle((uint)StandardHandleId.Output); var readBufferSize = new COORD { X = (short)BufferWidth, Y = (short)count }; var readBufferCoord = new COORD { X = 0, Y = 0 }; var readRegion = new SMALL_RECT { Top = (short)top, Left = 0, Bottom = (short)(top + count), Right = (short)(BufferWidth - 1) }; NativeMethods.ReadConsoleOutput(handle, result, readBufferSize, readBufferCoord, ref readRegion); return(result); }
internal static extern bool ScrollConsoleScreenBuffer ( NakedWin32Handle consoleOutput, ref SMALL_RECT scrollRectangle, ref SMALL_RECT clipRectangle, COORD destinationOrigin, ref CHAR_INFO fill );
public static extern bool ScrollConsoleScreenBuffer(IntPtr hConsoleOutput, ref SMALL_RECT lpScrollRectangle, IntPtr lpClipRectangle, COORD dwDestinationOrigin, ref CHAR_INFO lpFill);
private CHAR_INFO[] CreateCharInfoBuffer(int lines, params object[] items) { var result = new List<CHAR_INFO>(); var fg = Console.ForegroundColor; var bg = Console.BackgroundColor; foreach (var i in items) { var item = i; if (item is char) { result.Add(new CHAR_INFO((char)item, fg, bg)); continue; } if (item is InvertedToken) { fg = (ConsoleColor)((int)fg ^ 7); bg = (ConsoleColor)((int)bg ^ 7); continue; } if (item is NextLineToken) { item = new string(' ', Console.BufferWidth - (result.Count % Console.BufferWidth)); fg = Console.ForegroundColor; bg = Console.BackgroundColor; // Fallthrough to string case. } var str = item as string; if (str != null) { result.AddRange(str.Select(c => new CHAR_INFO(c, fg, bg))); continue; } if (item is TokenClassification) { fg = ForegroundColors[(int)(TokenClassification)item]; bg = BackgroundColors[(int)(TokenClassification)item]; continue; } var tuple = item as Tuple<ConsoleColor, ConsoleColor>; if (tuple != null) { fg = tuple.Item1; bg = tuple.Item2; continue; } throw new ArgumentException("Unexpected type"); } var extraSpacesNeeded = (lines * Console.BufferWidth) - result.Count; if (extraSpacesNeeded > 0) { var space = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor); result.AddRange(Enumerable.Repeat(space, extraSpacesNeeded)); } return result.ToArray(); }
private static void WriteBufferLines(CHAR_INFO[] buffer, ref int top) { var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); int bufferWidth = Console.BufferWidth; int bufferLineCount = buffer.Length / bufferWidth; if ((top + bufferLineCount) > Console.BufferHeight) { var scrollCount = (top + bufferLineCount) - Console.BufferHeight; ScrollBuffer(scrollCount); top -= scrollCount; } var bufferSize = new COORD { X = (short) bufferWidth, Y = (short) bufferLineCount }; var bufferCoord = new COORD {X = 0, Y = 0}; var writeRegion = new SMALL_RECT { Top = (short) top, Left = 0, Bottom = (short) (top + bufferLineCount - 1), Right = (short) bufferWidth }; NativeMethods.WriteConsoleOutput(handle, buffer, bufferSize, bufferCoord, ref writeRegion); }
private static void SetPrompt(string prompt) { if (string.IsNullOrEmpty(prompt)) return; var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); var lineCount = 1 + prompt.Count(c => c == '\n'); if (lineCount > 1) { var options = new SetPSReadlineOption {ExtraPromptLineCount = lineCount - 1}; PSConsoleReadLine.SetOptions(options); } int bufferWidth = Console.BufferWidth; var consoleBuffer = new CHAR_INFO[lineCount * bufferWidth]; int j = 0; for (int i = 0; i < prompt.Length; i++, j++) { if (prompt[i] == '\n') { for (; j % Console.BufferWidth != 0; j++) { consoleBuffer[j] = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor); } Console.CursorTop += 1; Console.CursorLeft = 0; j -= 1; // We don't actually write the newline } else { consoleBuffer[j] = new CHAR_INFO(prompt[i], Console.ForegroundColor, Console.BackgroundColor); Console.CursorLeft += 1; } } var bufferSize = new COORD { X = (short) bufferWidth, Y = (short) lineCount }; var bufferCoord = new COORD {X = 0, Y = 0}; var writeRegion = new SMALL_RECT { Top = 0, Left = 0, Bottom = (short) (lineCount - 1), Right = (short) bufferWidth }; NativeMethods.WriteConsoleOutput(handle, consoleBuffer, bufferSize, bufferCoord, ref writeRegion); }
private static void ScrollBuffer(int lines) { var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); var scrollRectangle = new SMALL_RECT { Top = (short) lines, Left = 0, Bottom = (short) (lines + Console.BufferHeight - 1), Right = (short)Console.BufferWidth }; var destinationOrigin = new COORD {X = 0, Y = 0}; var fillChar = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor); NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, IntPtr.Zero, destinationOrigin, ref fillChar); }
private static CHAR_INFO[] ReadBufferLines(int top, int count) { var result = new CHAR_INFO[Console.BufferWidth * count]; var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); var readBufferSize = new COORD { X = (short)Console.BufferWidth, Y = (short)count}; var readBufferCoord = new COORD {X = 0, Y = 0}; var readRegion = new SMALL_RECT { Top = (short)top, Left = 0, Bottom = (short)(top + count), Right = (short)(Console.BufferWidth - 1) }; NativeMethods.ReadConsoleOutput(handle, result, readBufferSize, readBufferCoord, ref readRegion); return result; }
private void ReallyRender() { var text = ParseInput(); int statusLineCount = GetStatusLineCount(); int j = _initialX + (_bufferWidth * Options.ExtraPromptLineCount); var backgroundColor = _initialBackgroundColor; var foregroundColor = _initialForegroundColor; bool afterLastToken = false; int totalBytes = j; int bufferWidth = _console.BufferWidth; var tokenStack = new Stack <SavedTokenState>(); tokenStack.Push(new SavedTokenState { Tokens = _tokens, Index = 0, BackgroundColor = _initialBackgroundColor, ForegroundColor = _initialForegroundColor }); int bufferLineCount; try { _console.StartRender(); bufferLineCount = ConvertOffsetToCoordinates(text.Length).Y - _initialY + 1 + statusLineCount; if (_consoleBuffer.Length != bufferLineCount * bufferWidth) { var newBuffer = new CHAR_INFO[bufferLineCount * bufferWidth]; Array.Copy(_consoleBuffer, newBuffer, _initialX + (Options.ExtraPromptLineCount * _bufferWidth)); if (_consoleBuffer.Length > bufferLineCount * bufferWidth) { int consoleBufferOffset = ConvertOffsetToConsoleBufferOffset(text.Length, _initialX + (Options.ExtraPromptLineCount * _bufferWidth)); // Need to erase the extra lines that we won't draw again for (int i = consoleBufferOffset; i < _consoleBuffer.Length; i++) { _consoleBuffer[i] = _space; } _console.WriteBufferLines(_consoleBuffer, ref _initialY); } _consoleBuffer = newBuffer; } for (int i = 0; i < text.Length; i++) { totalBytes = totalBytes % bufferWidth; if (!afterLastToken) { // Figure out the color of the character - if it's in a token, // use the tokens color otherwise use the initial color. var state = tokenStack.Peek(); var token = state.Tokens[state.Index]; if (i == token.Extent.EndOffset) { if (token == state.Tokens[state.Tokens.Length - 1]) { tokenStack.Pop(); if (tokenStack.Count == 0) { afterLastToken = true; token = null; foregroundColor = _initialForegroundColor; backgroundColor = _initialBackgroundColor; } else { state = tokenStack.Peek(); } } if (!afterLastToken) { foregroundColor = state.ForegroundColor; backgroundColor = state.BackgroundColor; token = state.Tokens[++state.Index]; } } if (!afterLastToken && i == token.Extent.StartOffset) { GetTokenColors(token, out foregroundColor, out backgroundColor); var stringToken = token as StringExpandableToken; if (stringToken != null) { // We might have nested tokens. if (stringToken.NestedTokens != null && stringToken.NestedTokens.Any()) { var tokens = new Token[stringToken.NestedTokens.Count + 1]; stringToken.NestedTokens.CopyTo(tokens, 0); // NestedTokens doesn't have an "EOS" token, so we use // the string literal token for that purpose. tokens[tokens.Length - 1] = stringToken; tokenStack.Push(new SavedTokenState { Tokens = tokens, Index = 0, BackgroundColor = backgroundColor, ForegroundColor = foregroundColor }); if (i == tokens[0].Extent.StartOffset) { GetTokenColors(tokens[0], out foregroundColor, out backgroundColor); } } } } } var charToRender = text[i]; if (charToRender == '\n') { while ((j % bufferWidth) != 0) { _consoleBuffer[j++] = _space; } for (int k = 0; k < Options.ContinuationPrompt.Length; k++, j++) { _consoleBuffer[j].UnicodeChar = Options.ContinuationPrompt[k]; _consoleBuffer[j].ForegroundColor = Options.ContinuationPromptForegroundColor; _consoleBuffer[j].BackgroundColor = Options.ContinuationPromptBackgroundColor; } } else { int size = LengthInBufferCells(charToRender); totalBytes += size; //if there is no enough space for the character at the edge, fill in spaces at the end and //put the character to next line. int filling = totalBytes > bufferWidth ? (totalBytes - bufferWidth) % size : 0; for (int f = 0; f < filling; f++) { _consoleBuffer[j++] = _space; totalBytes++; } if (char.IsControl(charToRender)) { _consoleBuffer[j].UnicodeChar = '^'; MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); _consoleBuffer[j].UnicodeChar = (char)('@' + charToRender); MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); } else if (size > 1) { _consoleBuffer[j].UnicodeChar = charToRender; _consoleBuffer[j].Attributes = (ushort)(_consoleBuffer[j].Attributes | (uint)CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE); MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); _consoleBuffer[j].UnicodeChar = charToRender; _consoleBuffer[j].Attributes = (ushort)(_consoleBuffer[j].Attributes | (uint)CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE); MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); } else { _consoleBuffer[j].UnicodeChar = charToRender; MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); } } } } finally { _console.EndRender(); } for (; j < (_consoleBuffer.Length - (statusLineCount * _bufferWidth)); j++) { _consoleBuffer[j] = _space; } if (_statusLinePrompt != null) { foregroundColor = _statusIsErrorMessage ? Options.ErrorForegroundColor : _console.ForegroundColor; backgroundColor = _statusIsErrorMessage ? Options.ErrorBackgroundColor : _console.BackgroundColor; for (int i = 0; i < _statusLinePrompt.Length; i++, j++) { _consoleBuffer[j].UnicodeChar = _statusLinePrompt[i]; _consoleBuffer[j].ForegroundColor = foregroundColor; _consoleBuffer[j].BackgroundColor = backgroundColor; } for (int i = 0; i < _statusBuffer.Length; i++, j++) { _consoleBuffer[j].UnicodeChar = _statusBuffer[i]; _consoleBuffer[j].ForegroundColor = foregroundColor; _consoleBuffer[j].BackgroundColor = backgroundColor; } for (; j < _consoleBuffer.Length; j++) { _consoleBuffer[j] = _space; } } bool rendered = false; if (_parseErrors.Length > 0) { int promptChar = _initialX - 1 + (_bufferWidth * Options.ExtraPromptLineCount); while (promptChar >= 0) { var c = (char)_consoleBuffer[promptChar].UnicodeChar; if (char.IsWhiteSpace(c)) { promptChar -= 1; continue; } ConsoleColor prevColor = _consoleBuffer[promptChar].ForegroundColor; _consoleBuffer[promptChar].ForegroundColor = ConsoleColor.Red; _console.WriteBufferLines(_consoleBuffer, ref _initialY); rendered = true; _consoleBuffer[promptChar].ForegroundColor = prevColor; break; } } if (!rendered) { _console.WriteBufferLines(_consoleBuffer, ref _initialY); } PlaceCursor(); if ((_initialY + bufferLineCount) > (_console.WindowTop + _console.WindowHeight)) { _console.WindowTop = _initialY + bufferLineCount - _console.WindowHeight; } _lastRenderTime.Restart(); }
public static extern bool WriteConsoleOutput(IntPtr consoleOutput, CHAR_INFO[] buffer, COORD bufferSize, COORD bufferCoord, ref SMALL_RECT writeRegion);
internal static extern bool WriteConsoleOutput ( NakedWin32Handle consoleOutput, CHAR_INFO[] buffer, COORD bufferSize, COORD bufferCoord, ref SMALL_RECT writeRegion );
/// <summary> /// Wrap Win32 ScrollConsoleScreenBuffer /// </summary> /// <param name="consoleHandle"> /// /// handle for the console where screen buffer is scrolled /// /// </param> /// <param name="scrollRectangle"> /// /// area to be scrolled /// /// </param> /// <param name="clipRectangle"> /// /// area to be updated after scrolling /// /// </param> /// <param name="destOrigin"> /// /// location to which the top left corner of scrollRectangle move /// /// </param> /// <param name="fill"> /// /// character and attribute to fill the area vacated by the scroll /// /// </param> /// <exception cref="HostException"> /// If Win32's ScrollConsoleScreenBuffer fails /// </exception> internal static void ScrollConsoleScreenBuffer ( ConsoleHandle consoleHandle, SMALL_RECT scrollRectangle, SMALL_RECT clipRectangle, COORD destOrigin, CHAR_INFO fill ) { Dbg.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid"); Dbg.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed"); bool result = NativeMethods.ScrollConsoleScreenBuffer( consoleHandle.DangerousGetHandle(), ref scrollRectangle, ref clipRectangle, destOrigin, ref fill); if (result == false) { int err = Marshal.GetLastWin32Error(); HostException e = CreateHostException(err, "ScrollConsoleScreenBuffer", ErrorCategory.WriteError, ConsoleControlStrings.ScrollConsoleScreenBufferExceptionTemplate); throw e; } }
private static void ReadConsoleOutputPlain ( ConsoleHandle consoleHandle, Coordinates origin, Rectangle contentsRegion, ref BufferCell[,] contents ) { int rows = contentsRegion.Bottom - contentsRegion.Top + 1; int cols = contentsRegion.Right - contentsRegion.Left + 1; if ((rows <= 0) || cols <= 0) { tracer.WriteLine("invalid contents region"); return; } int bufferLimit = 2 * 1024; // Limit is 8K bytes as each CHAR_INFO takes 4 bytes COORD bufferCoord; bufferCoord.X = 0; bufferCoord.Y = 0; // keeps track of which screen area read SMALL_RECT readRegion; readRegion.Top = (short)origin.Y; int rowsRemaining = rows; while (rowsRemaining > 0) { // Iteration of columns is nested inside iteration of rows. // If the size of contents exceeds the buffer limit, reading is // done in blocks of size equal to the bufferlimit from left to right // then top to bottom. // For each iteration of rows, // - readRegion.Left and bufferSize.X are reset // - rowsRemaining, readRegion.Top, readRegion.Bottom, and bufferSize.Y // are updated // For each iteration of columns, // - readRegion.Left, readRegion.Right and bufferSize.X are updated readRegion.Left = (short)origin.X; COORD bufferSize; bufferSize.X = (short)Math.Min(cols, bufferLimit); bufferSize.Y = (short)Math.Min ( rowsRemaining, bufferLimit / bufferSize.X ); readRegion.Bottom = (short)(readRegion.Top + bufferSize.Y - 1); // atContentsRow is at which row of contents a particular iteration is operating int atContentsRow = rows - rowsRemaining + contentsRegion.Top; // number of columns yet to be read int colsRemaining = cols; while (colsRemaining > 0) { readRegion.Right = (short)(readRegion.Left + bufferSize.X - 1); // Now readRegion and bufferSize are updated. // Call NativeMethods.ReadConsoleOutput CHAR_INFO[] characterBuffer = new CHAR_INFO[bufferSize.Y * bufferSize.X]; bool result = NativeMethods.ReadConsoleOutput( consoleHandle.DangerousGetHandle(), characterBuffer, bufferSize, bufferCoord, ref readRegion); if (result == false) { // When WriteConsoleOutput fails, half bufferLimit if (bufferLimit < 2) { int err = Marshal.GetLastWin32Error(); HostException e = CreateHostException(err, "ReadConsoleOutput", ErrorCategory.ReadError, ConsoleControlStrings.ReadConsoleOutputExceptionTemplate); throw e; } // if cols == colsRemaining, nothing is guaranteed read in this pass and // the unread area is still rectangular bufferLimit /= 2; if (cols == colsRemaining) { bufferSize.Y = 0; break; } else { // some areas have been read. This could only happen when the number of columns // to write is larger than bufferLimit. In that case, the algorithm reads one row // at a time => bufferSize.Y == 1. Then, we can safely leave bufferSize.Y unchanged // to retry with a smaller bufferSize.X. Dbg.Assert(bufferSize.Y == 1, string.Format(CultureInfo.InvariantCulture, "bufferSize.Y should be 1, but is {0}", bufferSize.Y)); bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit); continue; } } // atContentsCol is at which column of contents a particular iteration is operating int atContentsCol = cols - colsRemaining + contentsRegion.Left; // copy characterBuffer to contents; int characterBufferIndex = 0; for (int r = atContentsRow; r < bufferSize.Y + atContentsRow; r++) { for (int c = atContentsCol; c < bufferSize.X + atContentsCol; c++, characterBufferIndex++) { contents[r, c].Character = (char) characterBuffer[characterBufferIndex].UnicodeChar; ConsoleColor fgColor, bgColor; WORDToColor(characterBuffer[characterBufferIndex].Attributes, out fgColor, out bgColor); contents[r, c].ForegroundColor = fgColor; contents[r, c].BackgroundColor = bgColor; } } colsRemaining -= bufferSize.X; readRegion.Left += bufferSize.X; bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit); } // column iteration rowsRemaining -= bufferSize.Y; readRegion.Top += bufferSize.Y; } // row iteration // The following nested loop set the value of the empty cells in contents: // character to ' ' // foreground color to console's foreground color // background color to console's background color int rowIndex = contents.GetLowerBound(0); int rowEnd = contents.GetUpperBound(0); int colBegin = contents.GetLowerBound(1); int colEnd = contents.GetUpperBound(1); CONSOLE_SCREEN_BUFFER_INFO bufferInfo = GetConsoleScreenBufferInfo(consoleHandle); ConsoleColor foreground = 0; ConsoleColor background = 0; WORDToColor( bufferInfo.Attributes, out foreground, out background ); while (rowIndex <= rowEnd) { int colIndex = colBegin; while (true) { // if contents[rowIndex,colIndex] is in contentsRegion, hence a non-empty cell, // move colIndex to one past the right end of contentsRegion if (contentsRegion.Top <= rowIndex && rowIndex <= contentsRegion.Bottom && contentsRegion.Left <= colIndex && colIndex <= contentsRegion.Right) { colIndex = contentsRegion.Right + 1; } // colIndex past contents last column if (colIndex > colEnd) { break; } contents[rowIndex, colIndex].Character = ' '; contents[rowIndex, colIndex].ForegroundColor = foreground; contents[rowIndex, colIndex].BackgroundColor = background; colIndex++; } rowIndex++; } }
private static bool ReadConsoleOutputCJKSmall ( ConsoleHandle consoleHandle, uint codePage, Coordinates origin, Rectangle contentsRegion, ref BufferCell[,] contents ) { COORD bufferSize; bufferSize.X = (short)(contentsRegion.Right - contentsRegion.Left + 1); bufferSize.Y = (short)(contentsRegion.Bottom - contentsRegion.Top + 1); COORD bufferCoord; bufferCoord.X = 0; bufferCoord.Y = 0; CHAR_INFO[] characterBuffer = new CHAR_INFO[bufferSize.X * bufferSize.Y]; SMALL_RECT readRegion; readRegion.Left = (short)origin.X; readRegion.Top = (short)origin.Y; readRegion.Right = (short)(origin.X + bufferSize.X - 1); readRegion.Bottom = (short)(origin.Y + bufferSize.Y - 1); // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to // get the error code. #pragma warning disable 56523 bool result = NativeMethods.ReadConsoleOutput( consoleHandle.DangerousGetHandle(), characterBuffer, bufferSize, bufferCoord, ref readRegion); if (!result) { return false; } int characterBufferIndex = 0; for (int r = contentsRegion.Top; r <= contentsRegion.Bottom; r++) { for (int c = contentsRegion.Left; c <= contentsRegion.Right; c++, characterBufferIndex++) { ConsoleColor fgColor, bgColor; contents[r, c].Character = (char)characterBuffer[characterBufferIndex].UnicodeChar; WORDToColor(characterBuffer[characterBufferIndex].Attributes, out fgColor, out bgColor); contents[r, c].ForegroundColor = fgColor; contents[r, c].BackgroundColor = bgColor; // Set the attributes of the buffercells to be the same as that of the // incoming CHAR_INFO. In case where the CHAR_INFO character is a // trailing byte set the Character of BufferCell to 0. This is done // because at a lot of places this check is being done. Having a trailing // character to be 0 is by design. if ((characterBuffer[characterBufferIndex].Attributes & (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE) == (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE) { contents[r, c].BufferCellType = BufferCellType.Leading; } else if ((characterBuffer[characterBufferIndex].Attributes & (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE) == (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE) { contents[r, c].Character = (char)0; contents[r, c].BufferCellType = BufferCellType.Trailing; } else { int charLength = LengthInBufferCells(contents[r, c].Character); if (charLength == 2) { // When it's RasterFont, the "Leading_byte"/"Trailing_byte" are not effective, we // need to decide the leading byte by checking the char length. contents[r, c].BufferCellType = BufferCellType.Leading; c++; contents[r, c].Character = (char)0; contents[r, c].ForegroundColor = fgColor; contents[r, c].BackgroundColor = bgColor; contents[r, c].BufferCellType = BufferCellType.Trailing; } else { contents[r, c].BufferCellType = BufferCellType.Complete; } } } } return true; }
private static void WriteConsoleOutputPlain(ConsoleHandle consoleHandle, Coordinates origin, BufferCell[,] contents) { int rows = contents.GetLength(0); int cols = contents.GetLength(1); if ((rows <= 0) || cols <= 0) { tracer.WriteLine("contents passed in has 0 rows and columns"); return; } int bufferLimit = 2 * 1024; // Limit is 8K bytes as each CHAR_INFO takes 4 bytes COORD bufferCoord; bufferCoord.X = 0; bufferCoord.Y = 0; // keeps track of which screen area write SMALL_RECT writeRegion; writeRegion.Top = (short)origin.Y; int rowsRemaining = rows; while (rowsRemaining > 0) { // Iteration of columns is nested inside iteration of rows. // If the size of contents exceeds the buffer limit, writing is // done in blocks of size equal to the bufferlimit from left to right // then top to bottom. // For each iteration of rows, // - writeRegion.Left and bufferSize.X are reset // - rowsRemaining, writeRegion.Top, writeRegion.Bottom, and bufferSize.Y // are updated // For each iteration of columns, // - writeRegion.Left, writeRegion.Right and bufferSize.X are updated writeRegion.Left = (short)origin.X; COORD bufferSize; bufferSize.X = (short)Math.Min(cols, bufferLimit); bufferSize.Y = (short)Math.Min ( rowsRemaining, bufferLimit / bufferSize.X ); writeRegion.Bottom = (short)(writeRegion.Top + bufferSize.Y - 1); // atRow is at which row of contents a particular iteration is operating int atRow = rows - rowsRemaining + contents.GetLowerBound(0); // number of columns yet to be written int colsRemaining = cols; while (colsRemaining > 0) { writeRegion.Right = (short)(writeRegion.Left + bufferSize.X - 1); // atCol is at which column of contents a particular iteration is operating int atCol = cols - colsRemaining + contents.GetLowerBound(1); CHAR_INFO[] characterBuffer = new CHAR_INFO[bufferSize.Y * bufferSize.X]; // copy characterBuffer to contents; for (int r = atRow, characterBufferIndex = 0; r < bufferSize.Y + atRow; r++) { for (int c = atCol; c < bufferSize.X + atCol; c++, characterBufferIndex++) { characterBuffer[characterBufferIndex].UnicodeChar = (ushort)contents[r, c].Character; characterBuffer[characterBufferIndex].Attributes = ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor); } } // Now writeRegion, bufferSize and characterBuffer are updated. // Call NativeMethods.WriteConsoleOutput bool result = NativeMethods.WriteConsoleOutput( consoleHandle.DangerousGetHandle(), characterBuffer, bufferSize, bufferCoord, ref writeRegion); if (result == false) { // When WriteConsoleOutput fails, half bufferLimit if (bufferLimit < 2) { int err = Marshal.GetLastWin32Error(); HostException e = CreateHostException(err, "WriteConsoleOutput", ErrorCategory.WriteError, ConsoleControlStrings.WriteConsoleOutputExceptionTemplate); throw e; } bufferLimit /= 2; if (cols == colsRemaining) { // if cols == colsRemaining, nothing is guaranteed written in this pass and // the unwritten area is still rectangular bufferSize.Y = 0; break; } else { // some areas have been written. This could only happen when the number of columns // to write is larger than bufferLimit. In that case, the algorithm writes one row // at a time => bufferSize.Y == 1. Then, we can safely leave bufferSize.Y unchanged // to retry with a smaller bufferSize.X. Dbg.Assert(bufferSize.Y == 1, string.Format(CultureInfo.InvariantCulture, "bufferSize.Y should be 1, but is {0}", bufferSize.Y)); bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit); continue; } } colsRemaining -= bufferSize.X; writeRegion.Left += bufferSize.X; bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit); } // column iteration rowsRemaining -= bufferSize.Y; writeRegion.Top += bufferSize.Y; } // row iteration }
private static void WriteConsoleOutputCJK(ConsoleHandle consoleHandle, Coordinates origin, Rectangle contentsRegion, BufferCell[,] contents, BufferCellArrayRowType rowType) { Dbg.Assert(origin.X >= 0 && origin.Y >= 0, "origin must be within the output buffer"); int rows = contentsRegion.Bottom - contentsRegion.Top + 1; int cols = contentsRegion.Right - contentsRegion.Left + 1; CONSOLE_FONT_INFO_EX fontInfo = GetConsoleFontInfo(consoleHandle); int fontType = fontInfo.FontFamily & NativeMethods.FontTypeMask; bool trueTypeInUse = (fontType & NativeMethods.TrueTypeFont) == NativeMethods.TrueTypeFont; int bufferLimit = 2 * 1024; // Limit is 8K bytes as each CHAR_INFO takes 4 bytes COORD bufferCoord; bufferCoord.X = 0; bufferCoord.Y = 0; // keeps track of which screen area write SMALL_RECT writeRegion; writeRegion.Top = (short)origin.Y; int rowsRemaining = rows; while (rowsRemaining > 0) { // Iteration of columns is nested inside iteration of rows. // If the size of contents exceeds the buffer limit, writing is // done in blocks of size equal to the bufferlimit from left to right // then top to bottom. // For each iteration of rows, // - writeRegion.Left and bufferSize.X are reset // - rowsRemaining, writeRegion.Top, writeRegion.Bottom, and bufferSize.Y // are updated // For each iteration of columns, // - writeRegion.Left, writeRegion.Right and bufferSize.X are updated writeRegion.Left = (short)origin.X; COORD bufferSize; bufferSize.X = (short)Math.Min(cols, bufferLimit); bufferSize.Y = (short)Math.Min ( rowsRemaining, bufferLimit / bufferSize.X ); writeRegion.Bottom = (short)(writeRegion.Top + bufferSize.Y - 1); // atRow is at which row of contents a particular iteration is operating int atRow = rows - rowsRemaining + contentsRegion.Top; // number of columns yet to be written int colsRemaining = cols; while (colsRemaining > 0) { writeRegion.Right = (short)(writeRegion.Left + bufferSize.X - 1); // atCol is at which column of contents a particular iteration is operating int atCol = cols - colsRemaining + contentsRegion.Left; // if this is not the last column iteration && // the leftmost BufferCell is a leading cell, don't write that cell if (colsRemaining > bufferSize.X && contents[atRow, atCol + bufferSize.X - 1].BufferCellType == BufferCellType.Leading) { bufferSize.X--; writeRegion.Right--; } CHAR_INFO[] characterBuffer = new CHAR_INFO[bufferSize.Y * bufferSize.X]; // copy characterBuffer to contents; int characterBufferIndex = 0; bool lastCharIsLeading = false; BufferCell lastLeadingCell = new BufferCell(); for (int r = atRow; r < bufferSize.Y + atRow; r++) { for (int c = atCol; c < bufferSize.X + atCol; c++, characterBufferIndex++) { if (contents[r, c].BufferCellType == BufferCellType.Complete) { characterBuffer[characterBufferIndex].UnicodeChar = (ushort)contents[r, c].Character; characterBuffer[characterBufferIndex].Attributes = (ushort)(ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor)); lastCharIsLeading = false; } else if (contents[r, c].BufferCellType == BufferCellType.Leading) { characterBuffer[characterBufferIndex].UnicodeChar = (ushort)contents[r, c].Character; characterBuffer[characterBufferIndex].Attributes = (ushort)(ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor) | (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE); lastCharIsLeading = true; lastLeadingCell = contents[r, c]; } else if (contents[r, c].BufferCellType == BufferCellType.Trailing) { // The FontFamily is a 8-bit integer. The low-order bit (bit 0) specifies the pitch of the font. // If it is 1, the font is variable pitch (or proportional). If it is 0, the font is fixed pitch // (or monospace). Bits 1 and 2 specify the font type. If both bits are 0, the font is a raster font; // if bit 1 is 1 and bit 2 is 0, the font is a vector font; if bit 1 is 0 and bit 2 is set, or if both // bits are 1, the font is true type. Bit 3 is 1 if the font is a device font; otherwise, it is 0. // We only care about the bit 1 and 2, which indicate the font type. // There are only two font type defined for the Console, at // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\. // Console\Nls --- national language supports // Console\RasterFonts --- raster type font // Console\TrueTypeFont --- true type font // For CJK characters, if it's TrueType, we need to output the trailing character marked with "Trailing_byte" // attribute. But if it's RasterFont, we ignore the trailing character, and the "Leading_byte"/"Trailing_byte" // attributes are not effective at all when reading the character from the console buffer. if (lastCharIsLeading && trueTypeInUse) { // For TrueType Font, we output the trailing byte with "Trailing_byte" attribute characterBuffer[characterBufferIndex].UnicodeChar = lastLeadingCell.Character; characterBuffer[characterBufferIndex].Attributes = (ushort)(ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor) | (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE); } else { // We don't output anything for this cell if Raster font is in use, or if the last cell is not a leading byte characterBufferIndex--; } lastCharIsLeading = false; } } } // Now writeRegion, bufferSize and characterBuffer are updated. // Call NativeMethods.WriteConsoleOutput bool result; if ((rowType & BufferCellArrayRowType.RightLeading) != 0 && colsRemaining == bufferSize.X) { COORD bSize = bufferSize; bSize.X++; SMALL_RECT wRegion = writeRegion; wRegion.Right++; // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to // get the error code. #pragma warning disable 56523 result = NativeMethods.WriteConsoleOutput( consoleHandle.DangerousGetHandle(), characterBuffer, bSize, bufferCoord, ref wRegion); } else { // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to // get the error code. #pragma warning disable 56523 result = NativeMethods.WriteConsoleOutput( consoleHandle.DangerousGetHandle(), characterBuffer, bufferSize, bufferCoord, ref writeRegion); } if (result == false) { // When WriteConsoleOutput fails, half bufferLimit if (bufferLimit < 2) { int err = Marshal.GetLastWin32Error(); HostException e = CreateHostException(err, "WriteConsoleOutput", ErrorCategory.WriteError, ConsoleControlStrings.WriteConsoleOutputExceptionTemplate); throw e; } bufferLimit /= 2; if (cols == colsRemaining) { // if cols == colsRemaining, nothing is guaranteed written in this pass and // the unwritten area is still rectangular bufferSize.Y = 0; break; } else { // some areas have been written. This could only happen when the number of columns // to write is larger than bufferLimit. In that case, the algorithm writes one row // at a time => bufferSize.Y == 1. Then, we can safely leave bufferSize.Y unchanged // to retry with a smaller bufferSize.X. Dbg.Assert(bufferSize.Y == 1, string.Format(CultureInfo.InvariantCulture, "bufferSize.Y should be 1, but is {0}", bufferSize.Y)); bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit); continue; } } colsRemaining -= bufferSize.X; writeRegion.Left += bufferSize.X; bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit); } // column iteration rowsRemaining -= bufferSize.Y; writeRegion.Top += bufferSize.Y; } // row iteration }
private void Initialize(Runspace runspace, EngineIntrinsics engineIntrinsics) { _engineIntrinsics = engineIntrinsics; _runspace = runspace; if (!_delayedOneTimeInitCompleted) { DelayedOneTimeInitialize(); _delayedOneTimeInitCompleted = true; } _buffer.Clear(); _edits = new List <EditItem>(); _undoEditIndex = 0; _editGroupStart = -1; _current = 0; _mark = 0; _emphasisStart = -1; _emphasisLength = 0; _tokens = null; _parseErrors = null; _inputAccepted = false; _initialX = _console.CursorLeft; _initialY = _console.CursorTop - Options.ExtraPromptLineCount; _initialBackgroundColor = _console.BackgroundColor; _initialForegroundColor = _console.ForegroundColor; _space = new CHAR_INFO(' ', _initialForegroundColor, _initialBackgroundColor); _bufferWidth = _console.BufferWidth; _killCommandCount = 0; _yankCommandCount = 0; _yankLastArgCommandCount = 0; _tabCommandCount = 0; _visualSelectionCommandCount = 0; _statusIsErrorMessage = false; _consoleBuffer = ReadBufferLines(_initialY, 1 + Options.ExtraPromptLineCount); _lastRenderTime = Stopwatch.StartNew(); _killCommandCount = 0; _yankCommandCount = 0; _yankLastArgCommandCount = 0; _tabCommandCount = 0; _recallHistoryCommandCount = 0; _visualSelectionCommandCount = 0; _hashedHistory = null; if (_getNextHistoryIndex > 0) { _currentHistoryIndex = _getNextHistoryIndex; UpdateFromHistory(moveCursor: true); _getNextHistoryIndex = 0; if (_searchHistoryCommandCount > 0) { _searchHistoryPrefix = ""; if (Options.HistoryNoDuplicates) { _hashedHistory = new Dictionary <string, int>(); } } } else { _searchHistoryCommandCount = 0; } }
public static extern bool ScrollConsoleScreenBuffer(IntPtr hConsoleOutput, ref SMALL_RECT lpScrollRectangle, IntPtr lpClipRectangle, COORD dwDestinationOrigin, ref CHAR_INFO lpFill);