Skip to content

Commit

Permalink
add controls for cursor show/blink
Browse files Browse the repository at this point in the history
  • Loading branch information
doubleyewdee committed Nov 22, 2018
1 parent f042295 commit 95839d7
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 38 deletions.
124 changes: 107 additions & 17 deletions src/ConsoleBuffer/Buffer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ConsoleBuffer
namespace ConsoleBuffer
{
using System;
using System.Collections.Generic;
Expand All @@ -15,22 +15,22 @@ public sealed class Buffer : INotifyPropertyChanged
private short cursorX;
private short cursorY;
private int currentChar;
/// <summary>
/// we store X/Y as 0-offset indexes for convenience. escape codes will pass these around as 1-offset (top left is 1,1)
/// and we'll translate that nonsense where we have to.
/// </summary>
public (short X, short Y) CursorPosition => (this.cursorX, this.cursorY);
public bool CursorVisible { get; private set; }
public bool CursorBlink { get; private set; }

private short bufferTopVisibleLine
{
get
{
return (short)Math.Max(0, this.lines.Size - this.Height);
}
}
private short currentLine
private int topVisibleLine;
private int bottomVisibleLine;

private int CurrentLine
{
get
{
return (short)(this.bufferTopVisibleLine + this.CursorPosition.Y);
return this.topVisibleLine + this.CursorPosition.Y;
}
}

Expand All @@ -44,7 +44,14 @@ public Buffer(short width, short height)
this.Width = width;
this.Height = height;
this.CursorVisible = this.CursorBlink = true;
this.lines.PushBack(new Line());
this.cursorX = this.cursorY = 0;

for (var y = 0; y < this.Height; ++y)
{
this.lines.PushBack(new Line());
}
this.topVisibleLine = 0;
this.bottomVisibleLine = this.Height - 1;
}

public void Append(byte[] bytes, int length)
Expand All @@ -58,8 +65,20 @@ public void Append(byte[] bytes, int length)
switch (this.parser.Append(this.currentChar))
{
case ParserAppendResult.Render:
this.lines[this.currentLine].Set(this.cursorX, new Character { Glyph = this.currentChar });
this.cursorX = (short)Math.Min(this.Width - 1, this.cursorX + 1);
this.lines[this.CurrentLine].Set(this.cursorX, new Character { Glyph = this.currentChar });
++this.cursorX;
if (this.cursorX == this.Width)
{
this.cursorX = 0;
if (this.cursorY == this.Height - 1)
{
this.ScrollDown();
}
else
{
++this.cursorY;
}
}
break;
case ParserAppendResult.Complete:
this.ExecuteParserCommand();
Expand Down Expand Up @@ -102,10 +121,13 @@ private void ExecuteParserCommand()
this.OnPropertyChanged("Title");
}
break;
case Commands.ControlSequence csiCommand:
this.HandleControlSequence(csiCommand);
break;
case Commands.Unsupported unsupported:
break;
default:
throw new InvalidOperationException("Unknown command type passed.");
throw new InvalidOperationException($"Unknown command type passed: {this.parser.Command?.GetType()}.");
}
}

Expand All @@ -127,9 +149,9 @@ private void HandleControlCharacter(Commands.ControlCharacter.ControlCode code)
break;
case Commands.ControlCharacter.ControlCode.FF: // NB: could clear screen with this if we were so inclined. apparently xterm treats this as LF though, let's emulate.
case Commands.ControlCharacter.ControlCode.LF:
if (this.currentLine == this.lines.Size - 1)
if (this.CurrentLine == this.bottomVisibleLine)
{
this.lines.PushBack(new Line());
this.ScrollDown();
}

this.cursorY = (short)Math.Min(this.Height - 1, this.cursorY + 1);
Expand All @@ -145,6 +167,74 @@ private void HandleControlCharacter(Commands.ControlCharacter.ControlCode code)
}
}

private void HandleControlSequence(Commands.ControlSequence cmd)
{
switch (cmd)
{
case Commands.EraseInDisplay eid:
switch (eid.Direction)
{
case Commands.EraseInDisplay.Parameter.All:
for (var y = this.topVisibleLine; y <= this.bottomVisibleLine; ++y)
{
this.lines[y].Clear();
}
break;
case Commands.EraseInDisplay.Parameter.Above:
for (var y = this.topVisibleLine; y < this.CurrentLine; ++y)
{
this.lines[y].Clear();
}
break;
case Commands.EraseInDisplay.Parameter.Below:
for (var y = this.CurrentLine; y < this.bottomVisibleLine; ++y)
{
this.lines[y].Clear();
}
break;
}
break;
case Commands.SetMode sm:
switch (sm.Setting)
{
case Commands.SetMode.Parameter.CursorBlink:
this.CursorBlink = sm.Set;
break;
case Commands.SetMode.Parameter.CursorShow:
this.CursorVisible = sm.Set;
break;
}
break;
default:
throw new InvalidOperationException($"Unknown CSI command type {cmd.GetType()}.");
}
}

/// <summary>
/// Scroll the visible buffer down, adding new lines if needed.
/// If we're at the bottom of the buffer we will replace lines from the top with new, blank lines.
/// </summary>
private void ScrollDown(int lines = 1)
{
while (lines > 0)
{
--lines;
if (this.bottomVisibleLine == this.lines.Capacity - 1)
{
this.lines.PushBack(new Line()); // will force an old line from the buffer;
}
else
{
++this.topVisibleLine;
++this.bottomVisibleLine;
if (this.lines.Size <= this.bottomVisibleLine)
{
this.lines.PushBack(new Line());
}
}
}
}

/// <summary>
/// Render character-by-character onto the specified target.
/// </summary>
Expand All @@ -154,7 +244,7 @@ public void Render(IRenderTarget target)
{
for (var y = 0; y < this.Height; ++y)
{
var renderLine = this.bufferTopVisibleLine + 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)
Expand Down
16 changes: 11 additions & 5 deletions src/ConsoleBuffer/Commands/ControlSequence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,26 @@ public static Base Create(char command, string bufferData)
{
switch (command)
{
case 'h':
case 'l':
return new SetMode(bufferData, command == 'h');
case 'J':
var cmd = new EraseInDisplay(bufferData);
if (!cmd.IsExtended) // currently no support for selective erase in display
return cmd;
break;
return new EraseInDisplay(bufferData);
}
return new Unsupported($"^[[{bufferData}{command}");
}

protected bool IsExtended { get; private set; }
public bool IsExtended { get; private set; }
protected IList<string> Parameters { get; private set; }
protected ControlSequence(string bufferData) : base(bufferData) { }
protected override void Parse(string bufferData)
{
if (bufferData.Length == 0)
{
this.Parameters = Array.Empty<string>();
return;
}

var startIndex = 0;
if (bufferData[0] == '?')
{
Expand Down
31 changes: 30 additions & 1 deletion src/ConsoleBuffer/Commands/EraseInDisplay.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
using System;

namespace ConsoleBuffer.Commands
{
public sealed class EraseInDisplay : ControlSequence
{
public EraseInDisplay(string bufferData) : base(bufferData) { }
public enum Parameter
{
Below = 0,
Above,
All,
// xterm supports '3' for saved lines.
Unknown,
}

public Parameter Direction { get; private set; }

public EraseInDisplay(string bufferData) : base(bufferData)
{
this.Direction = Parameter.Unknown;
if (this.IsExtended)
{
return; // extended means selective erase which we don't support.
}
if (this.Parameters.Count == 0)
{
this.Direction = Parameter.Below;
}
if (this.Parameters.Count == 1 && uint.TryParse(this.Parameters[0], out var param))
{
param = Math.Min((uint)Parameter.Unknown, param);
this.Direction = (Parameter)param;
}
}
}
}
42 changes: 42 additions & 0 deletions src/ConsoleBuffer/Commands/SetMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace ConsoleBuffer.Commands
{
public sealed class SetMode : ControlSequence
{
/// <summary>
/// True if the command was to set (not reset) the parameter.
/// </summary>
public bool Set { get; private set; }
public enum Parameter
{
CursorShow = 0,
CursorBlink,
Unknown,
}

public Parameter Setting { get; private set; }

public SetMode(string bufferData, bool set) : base(bufferData)
{
this.Set = set;
this.Setting = Parameter.Unknown;

if (!this.IsExtended)
{
return;
}

if (this.Parameters.Count == 1)
{
switch (this.Parameters[0])
{
case "12":
this.Setting = Parameter.CursorBlink;
break;
case "25":
this.Setting = Parameter.CursorShow;
break;
}
}
}
}
}
12 changes: 9 additions & 3 deletions src/ConsoleBuffer/Line.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -28,6 +28,11 @@ public void Set(int pos, Character ch)
this.chars[pos] = ch;
}

public void Clear()
{
this.chars.Clear();
}

private void Extend(int pos)
{
// XXX: not efficient.
Expand All @@ -37,14 +42,15 @@ private void Extend(int pos)
}
}


public IEnumerator<Character> GetEnumerator()
{
return chars.GetEnumerator();
return this.chars.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return chars.GetEnumerator();
return this.chars.GetEnumerator();
}

public override string ToString()
Expand Down
19 changes: 10 additions & 9 deletions src/condo/Screen.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
namespace condo
namespace condo
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using ConsoleBuffer;

public sealed class Screen : FrameworkElement, IRenderTarget
Expand All @@ -24,6 +22,7 @@ public sealed class Screen : FrameworkElement, IRenderTarget
private readonly Rect cellRectangle;
private int horizontalCells, verticalCells;
private Character[,] characters;
bool cursorInverted;
private volatile int shouldRedraw;

private static readonly TimeSpan MaxRedrawFrequency = TimeSpan.FromMilliseconds(10);
Expand Down Expand Up @@ -63,7 +62,6 @@ public Screen(ConsoleWrapper console)
this.Resize();
}

bool cursorBlunk;
private void RenderFrame(object sender, EventArgs e)
{
if (this.redrawWatch.Elapsed >= MaxRedrawFrequency && this.shouldRedraw != 0)
Expand All @@ -74,12 +72,15 @@ private void RenderFrame(object sender, EventArgs e)
this.redrawWatch.Restart();
}

if (this.cursorBlinkWatch.Elapsed >= BlinkFrequency)
if (this.Console.Buffer.CursorVisible)
{
(var x, var y) = this.Console.Buffer.CursorPosition;
this.SetCellCharacter(x, y, (char)this.characters[x, y].Glyph, this.cursorBlunk);
this.cursorBlunk = !this.cursorBlunk;
this.cursorBlinkWatch.Restart();
if (this.cursorBlinkWatch.Elapsed >= BlinkFrequency)
{
this.cursorInverted = this.Console.Buffer.CursorBlink ? !this.cursorInverted : true;
(var x, var y) = this.Console.Buffer.CursorPosition;
this.SetCellCharacter(x, y, (char)this.characters[x, y].Glyph, this.cursorInverted);
this.cursorBlinkWatch.Restart();
}
}
}

Expand Down
Loading

0 comments on commit 95839d7

Please sign in to comment.