Skip to content

Commit

Permalink
add commands for character erasing/cursor movement
Browse files Browse the repository at this point in the history
  • Loading branch information
doubleyewdee committed Nov 22, 2018
1 parent 1c5900c commit 3ba087e
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 49 deletions.
63 changes: 44 additions & 19 deletions src/ConsoleBuffer/Buffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private int CurrentLine
{
get
{
return this.topVisibleLine + this.CursorPosition.Y;
return this.topVisibleLine + this.cursorY;
}
}

Expand All @@ -48,7 +48,7 @@ public Buffer(short width, short height)

for (var y = 0; y < this.Height; ++y)
{
this.lines.PushBack(new Line());
this.lines.PushBack(new Line(null));
}
this.topVisibleLine = 0;
this.bottomVisibleLine = this.Height - 1;
Expand Down Expand Up @@ -180,19 +180,40 @@ private void HandleControlSequence(Commands.ControlSequence cmd)
{
switch (cmd)
{
case Commands.CursorMove cu:
switch (cu.Direction)
{
case Commands.CursorMove.CursorDirection.Up:
this.cursorY = (short)Math.Max(0, this.cursorY - cu.Count);
break;
case Commands.CursorMove.CursorDirection.Down:
this.cursorY = (short)Math.Min(this.Height - 1, this.cursorY + cu.Count);
break;
case Commands.CursorMove.CursorDirection.Backward:
this.cursorX = (short)Math.Max(0, this.cursorX - cu.Count);
break;
case Commands.CursorMove.CursorDirection.Forward:
this.cursorX = (short)Math.Min(this.Width - 1, this.cursorX + cu.Count);
break;
}
break;
case Commands.EraseCharacter ec:
for (var c = 0; c < ec.Count; ++c)
{
this.lines[this.CurrentLine].SetGlyph(this.cursorX, 0x20);
++this.cursorX;
if (this.cursorX == this.Width)
{
// we won't advance beyond the end of viewable space when erasing.
if (this.cursorY < this.Height - 1)
// we won't advance beyond the end of viewable space when erasing, otherwise
// move on to the next line.
if (this.cursorY == this.Height - 1)
{
++this.cursorY;
this.cursorX = 0;
this.cursorX = (short)(this.Width - 1);
break;
}

++this.cursorY;
this.cursorX = 0;
}
}
break;
Expand Down Expand Up @@ -246,6 +267,16 @@ private void HandleControlSequence(Commands.ControlSequence cmd)
this.lines[this.CurrentLine].SetGlyph(x, 0x20);
}
break;
case Commands.SetCursorPosition cursorPos:
if (cursorPos.PosX > -1)
{
this.cursorX = (short)Math.Min(this.Width - 1, cursorPos.PosX);
}
if (cursorPos.PosY > -1)
{
this.cursorY = (short)Math.Min(this.Height - 1, cursorPos.PosY);
}
break;
case Commands.SetMode sm:
switch (sm.Setting)
{
Expand Down Expand Up @@ -273,15 +304,15 @@ private void ScrollDown(int lines = 1)
--lines;
if (this.bottomVisibleLine == this.lines.Capacity - 1)
{
this.lines.PushBack(new Line()); // will force an old line from the buffer;
this.lines.PushBack(new Line(this.lines[this.bottomVisibleLine])); // will force an old line from the buffer;
}
else
{
++this.topVisibleLine;
++this.bottomVisibleLine;
if (this.lines.Size <= this.bottomVisibleLine)
{
this.lines.PushBack(new Line());
this.lines.PushBack(new Line(this.lines[this.bottomVisibleLine - 1]));
}
}
}
Expand All @@ -297,17 +328,11 @@ public void Render(IRenderTarget target)
for (var y = 0; y < this.Height; ++y)
{
var renderLine = this.topVisibleLine + y;
var line = renderLine < this.lines.Size ? this.lines[renderLine] : Line.Empty;
short x = 0;
foreach (var c in line)
{
target.RenderCharacter(c, x, y);
++x;
}
while (x < this.Width)
{
target.RenderCharacter(new Character { Glyph = ' ' }, x, y);
++x;
var line = this.lines[renderLine];

for (var x = 0; x < this.Width; ++x)
{
target.RenderCharacter(line.Get(x), x, y);
}
}
}
Expand Down
28 changes: 25 additions & 3 deletions src/ConsoleBuffer/Commands/ControlSequence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,34 @@ namespace ConsoleBuffer.Commands
{
using System;
using System.Collections.Generic;
using System.Diagnostics;

public abstract class ControlSequence : Base
{
public static Base Create(char command, string bufferData)
{
//Trace.WriteLine($"Parsing sequence ^[[{bufferData}{command}");
switch (command)
{
case 'A':
case 'B':
case 'C':
case 'D':
return new CursorMove(bufferData, command);
case 'G':
case 'd':
case 'H':
case 'f':
return new SetCursorPosition(bufferData, command);
case 'h':
case 'l':
return new SetMode(bufferData, command == 'h');
case 'H':
return new EraseCharacter(bufferData);
case 'J':
return new EraseIn(bufferData, EraseIn.EraseType.Display);
case 'K':
return new EraseIn(bufferData, EraseIn.EraseType.Line);
case 'X':
return new EraseCharacter(bufferData);
}
return new Unsupported($"^[[{bufferData}{command}");
}
Expand All @@ -40,7 +52,17 @@ protected override void Parse(string bufferData)
startIndex = 1;
}

this.Parameters = bufferData.Substring(startIndex).Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
this.Parameters = bufferData.Substring(startIndex).Split(new[] { ';' });
}

protected int ParameterToNumber(int offset, int defaultValue = 0, int maxValue = short.MaxValue)
{
if (this.Parameters.Count > offset && ushort.TryParse(this.Parameters[offset], out var paramValue))
{
return Math.Max(defaultValue, Math.Min(paramValue, maxValue));
}

return defaultValue;
}
}
}
28 changes: 28 additions & 0 deletions src/ConsoleBuffer/Commands/CursorMove.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace ConsoleBuffer.Commands
{
using System;

public sealed class CursorMove : ControlSequence
{
public enum CursorDirection
{
Up = 0,
Down,
Forward,
Backward,
}

public CursorDirection Direction { get; private set; }
public int Count { get; private set; }

public CursorMove(string bufferData, char cmd) : base(bufferData)
{
if (cmd < 'A' || cmd > 'D')
{
throw new ArgumentOutOfRangeException(nameof(cmd));
}
this.Direction = (CursorDirection)(cmd - 'A');
this.Count = this.ParameterToNumber(0, defaultValue: 1);
}
}
}
7 changes: 1 addition & 6 deletions src/ConsoleBuffer/Commands/EraseCharacter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ public sealed class EraseCharacter : ControlSequence

public EraseCharacter(string bufferData) : base(bufferData)
{
this.Count = 1;
// NB: if the user provides an invalid parameter we still choose to erase one character.
if (this.Parameters.Count == 1 && ushort.TryParse(this.Parameters[0], out var count))
{
this.Count = Math.Max(1, (int)count); // 0 means scroll 1.
}
this.Count = this.ParameterToNumber(0, defaultValue: 1);
}
}
}
33 changes: 33 additions & 0 deletions src/ConsoleBuffer/Commands/SetCursorPosition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace ConsoleBuffer.Commands
{
public sealed class SetCursorPosition : ControlSequence
{
/// <summary>
/// Will be 0 for left-most position, -1 if X axis should not be applied.
/// </summary>
public int PosX { get; private set; }
/// <summary>
/// Will be 0 for top-most position, -1 if Y axis should not be applied.
/// </summary>
public int PosY { get; private set; }

public SetCursorPosition(string bufferData, char cmd) : base(bufferData)
{
this.PosX = this.PosY = -1;
switch (cmd)
{
case 'G':
this.PosX = this.ParameterToNumber(0, defaultValue: 1) - 1;
break;
case 'd':
this.PosY = this.ParameterToNumber(0, defaultValue: 1) - 1;
break;
case 'H':
case 'f':
this.PosY = this.ParameterToNumber(0, defaultValue: 1) - 1;
this.PosX = this.ParameterToNumber(1, defaultValue: 1) - 1;
break;
}
}
}
}
67 changes: 47 additions & 20 deletions src/ConsoleBuffer/Line.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleBuffer
{
using System.Collections;
using System.Collections.Generic;
using System.Text;

public sealed class Line : IEnumerable<Character>
{
public static readonly Line Empty = new Line();
private readonly List<Character> chars;

public Line(Line previous)
{
var hintSize = 80;
// our first character should inherit attributes of the last line's character.
var lastCh = new Character();
if (previous != null)
{
lastCh = previous.chars[previous.chars.Count - 1];
hintSize = previous.chars.Count;
}
lastCh.Glyph = 0x20;

private readonly List<Character> chars = new List<Character>();
public int Length => this.chars.Count;
this.chars = new List<Character>(hintSize)
{
lastCh
};
}

/// <summary>
/// Set a character value at a specified position. If the line is not long enough it is extended with blanks.
Expand All @@ -21,11 +33,25 @@ public sealed class Line : IEnumerable<Character>
/// <param name="ch">Character value.</param>
public void Set(int pos, Character ch)
{
if (this.chars.Count <= pos)
this.Extend(pos);
this.chars[pos] = ch;
}

/// <summary>
/// Get a character value at the specified position. If the line is not long enough an empty (space) character with the properties of the last character is returned.
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
public Character Get(int pos)
{
if (pos < this.chars.Count)
{
this.Extend(pos);
return this.chars[pos];
}
this.chars[pos] = ch;

var ch = this.chars[this.chars.Count - 1];
ch.Glyph = 0x20;
return ch;
}

/// <summary>
Expand All @@ -35,10 +61,7 @@ public void Set(int pos, Character ch)
/// <param name="glyph">Value to store.</param>
public void SetGlyph(int pos, int glyph)
{
if (this.chars.Count <= pos)
{
this.Extend(pos);
}
this.Extend(pos);

var current = this.chars[pos];
current.Glyph = glyph;
Expand All @@ -47,15 +70,19 @@ public void SetGlyph(int pos, int glyph)

public void Clear()
{
this.chars.Clear();
for (var x = 0; x < this.chars.Count; ++x)
{
this.SetGlyph(x, 0x20);
}
}

private void Extend(int pos)
{
// XXX: not efficient.
var newChar = this.chars[this.chars.Count - 1];
newChar.Glyph = 0x20;
while (this.chars.Count <= pos)
{
this.chars.Add(new Character { Glyph = ' ' });
this.chars.Add(newChar);
}
}

Expand Down
Loading

0 comments on commit 3ba087e

Please sign in to comment.