Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds true color support #1628

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -958,12 +958,22 @@ public static bool Is_WSL_Platform ()
return false;
}
var result = BashRunner.Run ("uname -a", runCurses: false);
if (result.Contains ("microsoft") && result.Contains ("WSL")) {
if (result.ToLower ().Contains ("microsoft") && (Environment.GetEnvironmentVariable ("WSL_DISTRO_NAME") != null)) {
return true;
}
return false;
}

public static bool CanColorTermTrueColor ()
{
if (Environment.GetEnvironmentVariable ("COLORTERM") is string value) {
value = value.ToLower ();
return value.Contains ("truecolor") || value.Contains ("24bit");
}

return false;
}

static int MapColor (Color color)
{
switch (color) {
Expand Down
70 changes: 54 additions & 16 deletions Terminal.Gui/ConsoleDrivers/NetDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ internal class NetWinVTConsole {
IntPtr InputHandle, OutputHandle, ErrorHandle;
uint originalInputConsoleMode, originalOutputConsoleMode, originalErrorConsoleMode;

public bool SupportTrueColor { get; } = (Environment.OSVersion.Version.Build >= 14931);

public NetWinVTConsole ()
{
InputHandle = GetStdHandle (STD_INPUT_HANDLE);
Expand Down Expand Up @@ -1162,6 +1164,9 @@ public struct InputResult {
}

internal class NetDriver : ConsoleDriver {

Attribute [] OutputAttributeBuffer;

const int COLOR_BLACK = 30;
const int COLOR_RED = 31;
const int COLOR_GREEN = 32;
Expand Down Expand Up @@ -1192,6 +1197,9 @@ internal class NetDriver : ConsoleDriver {
public override IClipboard Clipboard { get; }
public override int [,,] Contents => contents;

readonly bool supportsTrueColorOutput;
public override bool SupportsTrueColorOutput => supportsTrueColorOutput;

int largestWindowHeight;

public NetDriver ()
Expand All @@ -1200,6 +1208,7 @@ public NetDriver ()
if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
IsWinPlatform = true;
NetWinConsole = new NetWinVTConsole ();
supportsTrueColorOutput = NetWinConsole.SupportTrueColor;
}
//largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight);
largestWindowHeight = Console.BufferHeight;
Expand All @@ -1213,7 +1222,9 @@ public NetDriver ()
} else {
Clipboard = new CursesClipboard ();
}
supportsTrueColorOutput = CursesDriver.CanColorTermTrueColor ();
}
UseTrueColor = true;
}

// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
Expand All @@ -1239,6 +1250,7 @@ public override void AddRune (Rune rune)
rune = MakePrintable (rune);
var runeWidth = Rune.ColumnWidth (rune);
var validClip = IsValidContent (ccol, crow, Clip);
var position = crow * Cols + ccol;

if (validClip) {
if (runeWidth < 2 && ccol > 0
Expand All @@ -1258,6 +1270,7 @@ public override void AddRune (Rune rune)
} else {
contents [crow, ccol, 0] = (int)(uint)rune;
}
OutputAttributeBuffer [position] = currentAttribute;
contents [crow, ccol, 1] = currentAttribute;
contents [crow, ccol, 2] = 1;

Expand All @@ -1267,6 +1280,7 @@ public override void AddRune (Rune rune)
ccol++;
if (runeWidth > 1) {
if (validClip && ccol < Clip.Right) {
OutputAttributeBuffer [position] = currentAttribute;
contents [crow, ccol, 1] = currentAttribute;
contents [crow, ccol, 2] = 0;
}
Expand Down Expand Up @@ -1391,6 +1405,9 @@ public override void ResizeScreen ()
$";{Rows};{Cols}w");
}
}

OutputAttributeBuffer = new Attribute [Rows * Cols];

Clip = new Rect (0, 0, Cols, Rows);
Console.Out.Write ("\x1b[3J");
Console.Out.Flush ();
Expand All @@ -1406,6 +1423,9 @@ public override void UpdateOffScreen ()
try {
for (int row = 0; row < rows; row++) {
for (int c = 0; c < cols; c++) {
int position = row * cols + c;
OutputAttributeBuffer [position] = Colors.TopLevel.Normal;

contents [row, c, 0] = ' ';
contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
contents [row, c, 2] = 0;
Expand All @@ -1427,7 +1447,7 @@ public override void Refresh ()
UpdateCursor ();
}

int redrawAttr = -1;
Attribute redrawAttr = null;

public override void UpdateScreen ()
{
Expand Down Expand Up @@ -1478,7 +1498,7 @@ public override void UpdateScreen ()
if (lastCol == -1)
lastCol = col;

var attr = contents [row, col, 1];
var attr = OutputAttributeBuffer [row * cols + col];
if (attr != redrawAttr) {
output.Append (WriteAttributes (attr));
}
Expand All @@ -1502,24 +1522,42 @@ void SetVirtualCursorPosition (int lastCol, int row)
Console.Out.Flush ();
}

System.Text.StringBuilder WriteAttributes (int attr)
System.Text.StringBuilder WriteAttributes (Attribute attr)
{
const string CSI = "\x1b[";
int bg = 0;
int fg = 0;
System.Text.StringBuilder sb = new System.Text.StringBuilder ();

redrawAttr = attr;
IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
.OfType<ConsoleColor> ()
.Select (s => (int)s);
if (values.Contains (attr & 0xffff)) {
bg = MapColors ((ConsoleColor)(attr & 0xffff), false);
}
if (values.Contains ((attr >> 16) & 0xffff)) {
fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff));
if ((UseTrueColor) && (attr is TrueColorAttribute tca)) {
sb.Append (new [] { '\x1b', '[', '3', '8', ';', '2', ';' });
sb.Append (tca.TrueColorForeground.Red);
sb.Append (';');
sb.Append (tca.TrueColorForeground.Green);
sb.Append (';');
sb.Append (tca.TrueColorForeground.Blue);
sb.Append (new [] { ';', '4', '8', ';', '2', ';' });
sb.Append (tca.TrueColorBackground.Red);
sb.Append (';');
sb.Append (tca.TrueColorBackground.Green);
sb.Append (';');
sb.Append (tca.TrueColorBackground.Blue);
sb.Append ('m');
} else {
const string CSI = "\x1b[";
int bg = 0;
int fg = 0;

IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
.OfType<ConsoleColor> ()
.Select (s => (int)s);
if (values.Contains (attr & 0xffff)) {
bg = MapColors ((ConsoleColor)(attr & 0xffff), false);
}
if (values.Contains ((attr >> 16) & 0xffff)) {
fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff));
}
sb.Append ($"{CSI}{bg};{fg}m");
}
sb.Append ($"{CSI}{bg};{fg}m");

redrawAttr = attr;

return sb;
}
Expand Down
Loading