public virtual void Render(TargetBase target) { if (!Show) { return; } IRenderableScreenCopy screenCopy = this.screen.GetScreenCopy(); var context2D = target.DeviceManager.ContextDirect2D; context2D.BeginDraw(); context2D.Transform = Matrix.Identity; context2D.Clear(TerminalBackgroundColor); RectangleF rect = new RectangleF(); var lines = screenCopy.Cells; for (int y = 0; y < lines.Count(); y++) { var cols = lines[y]; rect.Top = y * CellHeight; rect.Bottom = rect.Top + CellHeight; for (int x = 0; x < cols.Count(); x++) { var cell = cols[x]; rect.Left = x * CellWidth; rect.Right = rect.Left + CellWidth; bool isCursor = !screenCopy.CursorHidden && y == screenCopy.CursorRow && x == screenCopy.CursorColumn; this.DrawCell(target, rect, cell, isCursor, screenCopy.HasFocus); } } context2D.EndDraw(); }
/// <summary> /// Generates the RTF for the currently displayed terminal screen content. /// </summary> /// <param name="screenCopy">The currently displayed terminal screen content.</param> /// <returns>The generate RTF async operation.</returns> private async Task<Tuple<InMemoryRandomAccessStream, float>> GenerateRtf(IRenderableScreenCopy screenCopy) { int rightmostNonSpace = -1; int columnCount = -1; Encoding codepage1252 = Encoding.GetEncoding("Windows-1252"); var rtfStream = new InMemoryRandomAccessStream(); using (DataWriter rtf = new DataWriter(rtfStream)) { rtf.WriteString(@"{"); rtf.WriteString(@"\rtf1"); rtf.WriteString(@"\ansi"); rtf.WriteString(@"\ansicpg1252"); rtf.WriteString(@"{\fonttbl{\f0\fmodern " + this.screenDisplay.ColorTheme.FontFamily + ";}}"); rtf.WriteString(Environment.NewLine); rtf.WriteString(@"{\colortbl ;"); var colorTable = this.screenDisplay.ColorTheme.ColorTable; for (ScreenColor screenColor = ScreenColor.DefaultBackground; screenColor <= ScreenColor.WhiteBright; screenColor++) { var color = colorTable[screenColor]; rtf.WriteString(@"\red" + color.R + @"\green" + color.G + @"\blue" + color.B + ";"); } rtf.WriteString(@"}"); rtf.WriteString(Environment.NewLine); int fontSize = (int)(ScreenDisplay.BaseLogicalFontMetrics[this.screenDisplay.ColorTheme.FontFamily].FontSize * (1 + (ScreenDisplay.FontSizeScalingFactor * (float)this.screenDisplay.ColorTheme.FontSize))); rtf.WriteString(@"\pard\ltrpar\f0\fs" + fontSize); rtf.WriteString(Environment.NewLine); StringBuilder formatCodes = new StringBuilder(); for (int y = 0; y < screenCopy.Cells.Length; y++) { var line = screenCopy.Cells[y]; string lineString = new string(line.Select(c => c.Character).ToArray()); var hyperlinkMatches = hyperlinkRegex.Value.Matches(lineString).Cast<Match>(); for (int x = 0; x < line.Length; x++) { Match startingMatch = hyperlinkMatches.Where(m => m.Index == x).SingleOrDefault(); if (startingMatch != null) { rtf.WriteString(@"{\field{\*\fldinst HYPERLINK """ + RtfEscape(startingMatch.Value) + @"""}{\fldrslt "); } if (x == 0 || line[x - 1].BackgroundColor != line[x].BackgroundColor) { formatCodes.Append(@"\chshdng0\chcbpat" + (line[x].BackgroundColor - ScreenColor.DefaultBackground + 1)); } if (x == 0 || line[x - 1].ForegroundColor != line[x].ForegroundColor) { formatCodes.Append(@"\cf" + (line[x].ForegroundColor - ScreenColor.DefaultBackground + 1)); } if (x == 0 || line[x - 1].Modifications.HasFlag(ScreenCellModifications.Bold) != line[x].Modifications.HasFlag(ScreenCellModifications.Bold)) { formatCodes.Append(@"\b"); if (!line[x].Modifications.HasFlag(ScreenCellModifications.Bold)) { formatCodes.Append("0"); } } if (x == 0 || line[x - 1].Modifications.HasFlag(ScreenCellModifications.Underline) != line[x].Modifications.HasFlag(ScreenCellModifications.Underline)) { formatCodes.Append(@"\ul"); if (!line[x].Modifications.HasFlag(ScreenCellModifications.Underline)) { formatCodes.Append("0"); } } if (formatCodes.Length > 0) { rtf.WriteString(formatCodes.ToString()); formatCodes.Clear(); rtf.WriteString(" "); } if (line[x].Character == CjkWidth.UCSWIDE) { // don't write this } else if (line[x].Character == codepage1252.GetChars(codepage1252.GetBytes(new[] { line[x].Character }))[0]) { rtf.WriteBytes(codepage1252.GetBytes(RtfEscape(line[x].Character.ToString()))); } else { rtf.WriteString(@"\u" + ((int)line[x].Character).ToString() + "?"); } Match endingMatch = hyperlinkMatches.Where(m => m.Index + m.Length == x + 1).SingleOrDefault(); if (endingMatch != null) { rtf.WriteString("}}"); } if (x + 1 >= line.Length) { if (line[x].Modifications.HasFlag(ScreenCellModifications.Bold)) { formatCodes.Append(@"\b0"); } if (line[x].Modifications.HasFlag(ScreenCellModifications.Underline)) { formatCodes.Append(@"\ul0"); } } if (line[x].Character != 0x0020) { rightmostNonSpace = Math.Max(rightmostNonSpace, x); } } if (formatCodes.Length > 0) { rtf.WriteString(formatCodes.ToString()); formatCodes.Clear(); } if (y + 1 < screenCopy.Cells.Length) { rtf.WriteString(@"\par" + Environment.NewLine); } columnCount = Math.Max(columnCount, line.Length); } rtf.WriteString(@"}"); await rtf.StoreAsync(); await rtf.FlushAsync(); rtf.DetachStream(); } rtfStream.Seek(0); float widthRatio = rightmostNonSpace >= 0 ? (rightmostNonSpace + 1f) / columnCount : -1f; return new Tuple<InMemoryRandomAccessStream, float>(rtfStream, widthRatio); }
/// <summary> /// Renders the screen. /// </summary> /// <param name="target">The Direct2D drawing target.</param> public virtual void Render(TargetBase target) { Point drawingPosition = new Point(0, 0); SurfaceImageSourceTarget surfaceImageSourceTarget = target as SurfaceImageSourceTarget; if (surfaceImageSourceTarget != null) { drawingPosition = surfaceImageSourceTarget.DrawingPosition; } IRenderableScreenCopy screenCopy = this.screen.GetScreenCopy(); var context2D = target.DeviceManager.ContextDirect2D; context2D.BeginDraw(); context2D.Transform = Matrix.Identity; context2D.Clear(this.GetColor(ScreenColor.DefaultBackground)); // 1. Paint backgrounds { RectangleF rect = new RectangleF(); var lines = screenCopy.Cells; for (int y = 0; y < lines.Length; y++) { var cols = lines[y]; rect.Top = drawingPosition.Y + (y * this.physicalFontMetrics.CellHeight); rect.Bottom = rect.Top + this.physicalFontMetrics.CellHeight; ScreenColor currentBackgroundColor = cols.Length > 0 ? cols[0].BackgroundColor : ScreenColor.DefaultBackground; ScreenColor cellBackgroundColor; int blockStart = 0; for (int x = 0; x <= cols.Length; x++) // loop once above the upper bound { var cell = cols[x < cols.Length ? x : x - 1]; bool isCursor = !screenCopy.CursorHidden && y == screenCopy.CursorRow && x == screenCopy.CursorColumn; cellBackgroundColor = isCursor ? ScreenColor.CursorBackground : cell.BackgroundColor; if (cellBackgroundColor != currentBackgroundColor || x == cols.Length) { rect.Left = drawingPosition.X + (blockStart * this.physicalFontMetrics.CellWidth); rect.Right = drawingPosition.X + (x * this.physicalFontMetrics.CellWidth); Brush backgroundBrush = this.GetBrush(context2D, this.GetColor(currentBackgroundColor)); if (currentBackgroundColor == ScreenColor.CursorBackground && !screenCopy.HasFocus) { rect.Right = rect.Right - 1.0f; context2D.DrawRectangle(rect, backgroundBrush); } else { context2D.FillRectangle(rect, backgroundBrush); } blockStart = x; currentBackgroundColor = cellBackgroundColor; } } } } // 2. Paint foregrounds { RectangleF rect = new RectangleF(); var lines = screenCopy.Cells; for (int y = 0; y < lines.Length; y++) { var cols = lines[y]; rect.Top = drawingPosition.Y + (y * this.physicalFontMetrics.CellHeight); rect.Bottom = rect.Top + this.physicalFontMetrics.CellHeight; ScreenColor currentForegroundColor = cols.Length > 0 ? cols[0].ForegroundColor : ScreenColor.DefaultForeground; ScreenCellModifications currentCellModifications = cols.Length > 0 ? cols[0].Modifications : ScreenCellModifications.None; bool currentCellUCSWIDE = cols.Length > 0 ? cols[0].Character == CjkWidth.UCSWIDE : false; ScreenColor cellForegroundColor; int blockStart = 0; for (int x = 0; x <= cols.Length; x++) // loop once above the upper bound { var cell = cols[x < cols.Length ? x : x - 1]; bool isCursor = !screenCopy.CursorHidden && y == screenCopy.CursorRow && x == screenCopy.CursorColumn; cellForegroundColor = isCursor && screenCopy.HasFocus ? ScreenColor.CursorForeground : cell.ForegroundColor; if (currentCellUCSWIDE || cellForegroundColor != currentForegroundColor || cell.Modifications != currentCellModifications || x == cols.Length) { rect.Left = drawingPosition.X + (blockStart * this.physicalFontMetrics.CellWidth); rect.Right = drawingPosition.X + (x * this.physicalFontMetrics.CellWidth); Brush foregroundBrush = this.GetBrush(context2D, this.GetColor(currentForegroundColor)); TextFormat textFormat = this.textFormatNormal; if (currentCellModifications.HasFlag(ScreenCellModifications.Bold)) { textFormat = this.textFormatBold; } string text = new string(cols.Skip(blockStart).Take(x - blockStart).Select(c => char.IsWhiteSpace(c.Character) ? ' ' : c.Character).Where(ch => ch != CjkWidth.UCSWIDE).ToArray()).TrimEnd(); if (text.Length > 0) { context2D.DrawText(text, textFormat, rect, foregroundBrush, DrawTextOptions.Clip); } if (currentCellModifications.HasFlag(ScreenCellModifications.Underline)) { var point1 = new Vector2(rect.Left, rect.Bottom - 1.0f) + drawingPosition; var point2 = new Vector2(rect.Right, rect.Bottom - 1.0f) + drawingPosition; context2D.DrawLine(point1, point2, foregroundBrush); } blockStart = x; currentForegroundColor = cellForegroundColor; } currentCellUCSWIDE = cell.Character == CjkWidth.UCSWIDE; } } } context2D.EndDraw(); }
/// <summary> /// Generates the RTF for the currently displayed terminal screen content. /// </summary> /// <param name="screenCopy">The currently displayed terminal screen content.</param> /// <returns>The generate RTF async operation.</returns> private async Task <Tuple <InMemoryRandomAccessStream, float> > GenerateRtf(IRenderableScreenCopy screenCopy) { int rightmostNonSpace = -1; int columnCount = -1; Encoding codepage1252 = Encoding.GetEncoding("Windows-1252"); var rtfStream = new InMemoryRandomAccessStream(); using (DataWriter rtf = new DataWriter(rtfStream)) { rtf.WriteString(@"{"); rtf.WriteString(@"\rtf1"); rtf.WriteString(@"\ansi"); rtf.WriteString(@"\ansicpg1252"); rtf.WriteString(@"{\fonttbl{\f0\fmodern " + this.screenDisplay.ColorTheme.FontFamily + ";}}"); rtf.WriteString(Environment.NewLine); rtf.WriteString(@"{\colortbl ;"); var colorTable = this.screenDisplay.ColorTheme.ColorTable; for (ScreenColor screenColor = ScreenColor.DefaultBackground; screenColor <= ScreenColor.WhiteBright; screenColor++) { var color = colorTable[screenColor]; rtf.WriteString(@"\red" + color.R + @"\green" + color.G + @"\blue" + color.B + ";"); } rtf.WriteString(@"}"); rtf.WriteString(Environment.NewLine); int fontSize = (int)(ScreenDisplay.BaseLogicalFontMetrics[this.screenDisplay.ColorTheme.FontFamily].FontSize * (1 + (ScreenDisplay.FontSizeScalingFactor * (float)this.screenDisplay.ColorTheme.FontSize))); rtf.WriteString(@"\pard\ltrpar\f0\fs" + fontSize); rtf.WriteString(Environment.NewLine); StringBuilder formatCodes = new StringBuilder(); for (int y = 0; y < screenCopy.Cells.Length; y++) { var line = screenCopy.Cells[y]; string lineString = new string(line.Select(c => c.Character).ToArray()); var hyperlinkMatches = hyperlinkRegex.Value.Matches(lineString).Cast <Match>(); for (int x = 0; x < line.Length; x++) { Match startingMatch = hyperlinkMatches.Where(m => m.Index == x).SingleOrDefault(); if (startingMatch != null) { rtf.WriteString(@"{\field{\*\fldinst HYPERLINK """ + RtfEscape(startingMatch.Value) + @"""}{\fldrslt "); } if (x == 0 || line[x - 1].BackgroundColor != line[x].BackgroundColor) { formatCodes.Append(@"\chshdng0\chcbpat" + (line[x].BackgroundColor - ScreenColor.DefaultBackground + 1)); } if (x == 0 || line[x - 1].ForegroundColor != line[x].ForegroundColor) { formatCodes.Append(@"\cf" + (line[x].ForegroundColor - ScreenColor.DefaultBackground + 1)); } if (x == 0 || line[x - 1].Modifications.HasFlag(ScreenCellModifications.Bold) != line[x].Modifications.HasFlag(ScreenCellModifications.Bold)) { formatCodes.Append(@"\b"); if (!line[x].Modifications.HasFlag(ScreenCellModifications.Bold)) { formatCodes.Append("0"); } } if (x == 0 || line[x - 1].Modifications.HasFlag(ScreenCellModifications.Underline) != line[x].Modifications.HasFlag(ScreenCellModifications.Underline)) { formatCodes.Append(@"\ul"); if (!line[x].Modifications.HasFlag(ScreenCellModifications.Underline)) { formatCodes.Append("0"); } } if (formatCodes.Length > 0) { rtf.WriteString(formatCodes.ToString()); formatCodes.Clear(); rtf.WriteString(" "); } if (line[x].Character == CjkWidth.UCSWIDE) { // don't write this } else if (line[x].Character == codepage1252.GetChars(codepage1252.GetBytes(new[] { line[x].Character }))[0]) { rtf.WriteBytes(codepage1252.GetBytes(RtfEscape(line[x].Character.ToString()))); } else { rtf.WriteString(@"\u" + ((int)line[x].Character).ToString() + "?"); } Match endingMatch = hyperlinkMatches.Where(m => m.Index + m.Length == x + 1).SingleOrDefault(); if (endingMatch != null) { rtf.WriteString("}}"); } if (x + 1 >= line.Length) { if (line[x].Modifications.HasFlag(ScreenCellModifications.Bold)) { formatCodes.Append(@"\b0"); } if (line[x].Modifications.HasFlag(ScreenCellModifications.Underline)) { formatCodes.Append(@"\ul0"); } } if (line[x].Character != 0x0020) { rightmostNonSpace = Math.Max(rightmostNonSpace, x); } } if (formatCodes.Length > 0) { rtf.WriteString(formatCodes.ToString()); formatCodes.Clear(); } if (y + 1 < screenCopy.Cells.Length) { rtf.WriteString(@"\par" + Environment.NewLine); } columnCount = Math.Max(columnCount, line.Length); } rtf.WriteString(@"}"); await rtf.StoreAsync(); await rtf.FlushAsync(); rtf.DetachStream(); } rtfStream.Seek(0); float widthRatio = rightmostNonSpace >= 0 ? (rightmostNonSpace + 1f) / columnCount : -1f; return(new Tuple <InMemoryRandomAccessStream, float>(rtfStream, widthRatio)); }