From 560e6846593d795c3bc1e2b787e8f3d2e55c9718 Mon Sep 17 00:00:00 2001 From: Chip Locke Date: Wed, 21 Nov 2018 03:35:11 -0800 Subject: [PATCH] move to lower level renderer (no more textbox) --- src/ConsoleBuffer/Buffer.cs | 20 +++--- src/ConsoleBuffer/ConsoleWrapper.cs | 2 + src/ConsoleBuffer/IRenderTarget.cs | 2 +- src/condo/MainWindow.xaml | 4 +- src/condo/MainWindow.xaml.cs | 41 ++++-------- src/condo/Screen.cs | 96 +++++++++++++++++++++++++++++ src/condo/condo.csproj | 1 + 7 files changed, 128 insertions(+), 38 deletions(-) create mode 100644 src/condo/Screen.cs diff --git a/src/ConsoleBuffer/Buffer.cs b/src/ConsoleBuffer/Buffer.cs index 07c0258..66d33c3 100644 --- a/src/ConsoleBuffer/Buffer.cs +++ b/src/ConsoleBuffer/Buffer.cs @@ -124,7 +124,13 @@ private void HandleControlCharacter(ControlCharacterCommand.ControlCode code) break; case ControlCharacterCommand.ControlCode.FF: // NB: could clear screen with this if we were so inclined. apparently xterm treats this as LF though, let's emulate. case ControlCharacterCommand.ControlCode.LF: - if (this.currentLine == this.lines.Count - 1) + if (this.currentLine == short.MaxValue) + { + // XXX: perf nightmare, need to turn lines into a circular buffer probs. + this.lines.RemoveAt(0); + this.lines.Add(new Line()); + } + else if (this.currentLine == this.lines.Count - 1) { this.lines.Add(new Line()); } @@ -149,20 +155,20 @@ public void Render(IRenderTarget target) { lock (this.renderLock) { - for (var x = 0; x < this.Height; ++x) + for (var y = 0; y < this.Height; ++y) { - var renderLine = this.bufferTopVisibleLine + x; + var renderLine = this.bufferTopVisibleLine + y; var line = renderLine < this.lines.Count ? this.lines[renderLine] : Line.Empty; - short y = 0; + short x = 0; foreach (var c in line) { target.RenderCharacter(c, x, y); - ++y; + ++x; } - while (y < this.Width) + while (x < this.Width) { target.RenderCharacter(new Character { Glyph = ' ' }, x, y); - ++y; + ++x; } } } diff --git a/src/ConsoleBuffer/ConsoleWrapper.cs b/src/ConsoleBuffer/ConsoleWrapper.cs index 9ac056d..0b5b804 100644 --- a/src/ConsoleBuffer/ConsoleWrapper.cs +++ b/src/ConsoleBuffer/ConsoleWrapper.cs @@ -166,11 +166,13 @@ private void ReadConsoleTask() // this can happen when our parent disposes, safe to bail out silently. return; } + /* catch (Exception ex) // XXX: this is some lousy logging I don't normally recommend, need to kill later. { Logger.Verbose(ex.ToString()); throw; } + */ } } } diff --git a/src/ConsoleBuffer/IRenderTarget.cs b/src/ConsoleBuffer/IRenderTarget.cs index 0b43f4b..0029ee9 100644 --- a/src/ConsoleBuffer/IRenderTarget.cs +++ b/src/ConsoleBuffer/IRenderTarget.cs @@ -5,9 +5,9 @@ public interface IRenderTarget /// /// Instructs the target to render the character 'c' at x/y coordinates. /// - /// Character to render. /// Horizontal offset. /// Vertical offset. + /// Character to render. void RenderCharacter(Character c, int x, int y); } } diff --git a/src/condo/MainWindow.xaml b/src/condo/MainWindow.xaml index 912b935..84f94ca 100644 --- a/src/condo/MainWindow.xaml +++ b/src/condo/MainWindow.xaml @@ -6,7 +6,7 @@ xmlns:local="clr-namespace:condo" mc:Ignorable="d" Title="MainWindow" SizeToContent="WidthAndHeight"> - - + + diff --git a/src/condo/MainWindow.xaml.cs b/src/condo/MainWindow.xaml.cs index 182a216..017a800 100644 --- a/src/condo/MainWindow.xaml.cs +++ b/src/condo/MainWindow.xaml.cs @@ -1,6 +1,7 @@ namespace condo { using System.ComponentModel; + using System.Diagnostics; using System.Globalization; using System.Text; using System.Windows; @@ -13,7 +14,7 @@ /// public partial class MainWindow : Window, IRenderTarget { - private DpiScale dpiInfo; + private Screen screen; private ConsoleWrapper console; private KeyHandler keyHandler; private Character[,] characters; @@ -26,36 +27,24 @@ public MainWindow() System.Diagnostics.Debugger.Launch(); } + private void UpdateContents(object sender, PropertyChangedEventArgs args) { this.console.Buffer.Render(this); - this.Dispatcher.InvokeAsync(() => this.Redraw()); - } - - private Size DetermineSize() - { - DpiScale dpi = this.dpiInfo; - - // because we only ever expect to work with monospace fonts we can extrapolate from any single character. - // lord help if someone gets real excited about proportional font console. - var sampleText = new FormattedText("x", CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, - new Typeface(this.stuff.FontFamily, this.stuff.FontStyle, this.stuff.FontWeight, this.stuff.FontStretch), - this.stuff.FontSize, Brushes.Black, dpi.PixelsPerDip); - - return new Size(sampleText.Width * this.console.Width, sampleText.Height * this.console.Height); + this.Dispatcher.InvokeAsync(() => this.Redraw()); } private void OnLoaded(object sender, RoutedEventArgs e) { - this.dpiInfo = VisualTreeHelper.GetDpi(this); + this.screen = new Screen(); + this.screenCanvas.Children.Add(this.screen); + this.screenCanvas.Width = this.screen.Width; + this.screenCanvas.Height = this.screen.Height; + this.console = TerminalManager.Instance.GetOrCreate(0, "cmd.exe"); this.keyHandler = new KeyHandler(this.console); - var stuffSize = this.DetermineSize(); - this.stuff.Height = stuffSize.Height; - this.stuff.Width = stuffSize.Width; - - this.characters = new Character[this.console.Height, this.console.Width]; + this.characters = new Character[this.console.Width, this.console.Height]; this.Redraw(); this.console.PropertyChanged += this.UpdateContents; @@ -82,17 +71,13 @@ private void HandleClosing(object sender, CancelEventArgs e) private void Redraw() { - var sb = new StringBuilder(); - for (var x = 0; x < this.console.Height; ++x) + for (var x = 0; x < this.console.Width; ++x) { - for (var y = 0; y < this.console.Width; ++y) + for (var y = 0; y < this.console.Height; ++y) { - sb.Append((char)this.characters[x, y].Glyph); + this.screen.SetCellCharacter(x, y, (char)this.characters[x, y].Glyph); } - sb.Append('\n'); } - - this.stuff.Text = sb.ToString(); } public void RenderCharacter(Character c, int x, int y) diff --git a/src/condo/Screen.cs b/src/condo/Screen.cs new file mode 100644 index 0000000..7b620fa --- /dev/null +++ b/src/condo/Screen.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media; + +namespace condo +{ + public sealed class Screen : FrameworkElement + { + private VisualCollection cells; + private DpiScale dpiInfo; + private readonly GlyphTypeface typeface; + private readonly int fontSize = 14; + private readonly double cellWidth, cellHeight; + private readonly Point baselineOrigin; + private readonly Rect cellRectangle; + private int horizontalCells, verticalCells; + + public Screen() : this(80, 25) { } + + public Screen(int width, int height) + { + this.cells = new VisualCollection(this); + if (!new Typeface("Consolas").TryGetGlyphTypeface(out this.typeface)) + { + throw new InvalidOperationException("Could not get desired font."); + } + + this.horizontalCells = width; + this.verticalCells = height; + + this.cellWidth = this.typeface.AdvanceWidths[0] * this.fontSize; + this.cellHeight = this.typeface.Height * this.fontSize; + this.baselineOrigin = new Point(0, this.typeface.Baseline * this.fontSize); + this.cellRectangle = new Rect(new Size(this.cellWidth, this.cellHeight)); + + this.dpiInfo = VisualTreeHelper.GetDpi(this); + this.Resize(); + } + + protected override int VisualChildrenCount => this.cells.Count; + + protected override Visual GetVisualChild(int index) + { + return this.cells[index]; + } + + protected override Size MeasureOverride(Size availableSize) + { + return new Size(this.cellWidth * this.horizontalCells, this.cellHeight * this.verticalCells); + } + + private void Resize() + { + this.cells.Clear(); + for (var y = 0; y < this.verticalCells; ++y) + { + for (var x = 0; x < this.horizontalCells; ++x) + { + var dv = new DrawingVisual(); + dv.Offset = new Vector(x * this.cellWidth, y * this.cellHeight); + this.cells.Add(dv); + } + } + + this.Width = this.horizontalCells * this.cellWidth; + this.Height = this.verticalCells * this.cellHeight; + } + + private DrawingVisual GetCell(int x, int y) + { + return this.cells[x + y * this.horizontalCells] as DrawingVisual; + } + + public void SetCellCharacter(int x, int y, char c) + { + using (var dc = this.GetCell(x, y).RenderOpen()) + { + GlyphRun gr; + try + { + gr = new GlyphRun(this.typeface, 0, false, this.fontSize, (float)this.dpiInfo.PixelsPerDip, new[] { this.typeface.CharacterToGlyphMap[c] }, + this.baselineOrigin, new[] { 0.0 }, new[] { new Point(0, 0) }, null, null, null, null, null); + } + catch (KeyNotFoundException) + { + gr = new GlyphRun(this.typeface, 0, false, this.fontSize, (float)this.dpiInfo.PixelsPerDip, new[] { this.typeface.CharacterToGlyphMap[0] }, + this.baselineOrigin, new[] { 0.0 }, new[] { new Point(0, 0) }, null, null, null, null, null); + } + + dc.DrawRectangle(Brushes.Black, null, new Rect(new Point(0, 0), new Point(this.cellWidth, this.cellHeight))); + dc.DrawGlyphRun(Brushes.Gray, gr); + } + } + } +} diff --git a/src/condo/condo.csproj b/src/condo/condo.csproj index 47577da..d648eec 100644 --- a/src/condo/condo.csproj +++ b/src/condo/condo.csproj @@ -56,6 +56,7 @@ Designer + MSBuild:Compile