Merge pull request #50 from Microsoft/osc-color

Add support for using ColorTool in WSL
This commit is contained in:
Michael Niksa 2018-10-02 10:24:38 -07:00 committed by GitHub
commit bb5088ae6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 334 additions and 154 deletions

View file

@ -26,6 +26,16 @@ namespace ColorTool
public short Bottom;
}
[Flags]
public enum ConsoleOutputModes : uint
{
ENABLE_PROCESSED_OUTPUT = 0x0001,
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002,
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004,
DISABLE_NEWLINE_AUTO_RETURN = 0x0008,
ENABLE_LVB_GRID_WORLDWIDE = 0x0010,
}
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO_EX
{
@ -48,6 +58,8 @@ namespace ColorTool
}
}
public static int STD_OUTPUT_HANDLE = -11;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
@ -58,17 +70,26 @@ namespace ColorTool
public static extern bool SetConsoleScreenBufferInfoEx(IntPtr ConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO_EX ConsoleScreenBufferInfoEx);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
public static extern bool WriteConsole(
IntPtr hConsoleOutput,
string lpBuffer,
uint nNumberOfCharsToWrite,
out uint lpNumberOfCharsWritten,
IntPtr lpReserved
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetConsoleMode(IntPtr handle, out int mode);
public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
////////////////////////////////////////////////////////////////////////
public static uint RGB(int r, int g, int b)
{
return (uint)r + (((uint)g) << 8) + (((uint)b) << 16);
}
public const int COLOR_TABLE_SIZE = 16;
}
}

View file

@ -85,9 +85,30 @@ namespace ColorTool
DARK_WHITE
};
// Use a Console index in to get a VT index out.
static int[] VT_INDICIES = {
0, // DARK_BLACK
4, // DARK_BLUE
2, // DARK_GREEN
6, // DARK_CYAN
1, // DARK_RED
5, // DARK_MAGENTA
3, // DARK_YELLOW
7, // DARK_WHITE
8+0, // BRIGHT_BLACK
8+4, // BRIGHT_BLUE
8+2, // BRIGHT_GREEN
8+6, // BRIGHT_CYAN
8+1, // BRIGHT_RED
8+5, // BRIGHT_MAGENTA
8+3, // BRIGHT_YELLOW
8+7,// BRIGHT_WHITE
};
static bool quietMode = false;
static bool setDefaults = false;
static bool setProperties = true;
static bool setUnixStyle = false;
static void Usage()
{
@ -102,8 +123,6 @@ namespace ColorTool
static void Version()
{
//System.Reflection.Assembly.GetEntryAssembly();
//AssemblyName.GetAssemblyName(@"c:\path\to\file.dll").Version;
string exePath = System.Reflection.Assembly.GetEntryAssembly().Location;
Version ver = AssemblyName.GetAssemblyName(exePath).Version;
Console.WriteLine("colortool v" + ver);
@ -186,6 +205,102 @@ namespace ColorTool
Console.ForegroundColor = currentForeground;
Console.BackgroundColor = currentBackground;
}
static void PrintTableWithVt()
{
// Save the current background and foreground colors.
string test = " gYw ";
string[] FGs = {
"m",
"1m",
"30m",
"1;30m",
"31m",
"1;31m",
"32m",
"1;32m",
"33m",
"1;33m",
"34m",
"1;34m",
"35m",
"1;35m",
"36m",
"1;36m",
"37m",
"1;37m"
};
string[] BGs = {
"m",
"40m",
"41m",
"42m",
"43m",
"44m",
"45m",
"46m",
"47m"
};
Console.Write("\t");
for (int bg = 0; bg < BGs.Length; bg++)
{
if (bg > 0) Console.Write(" ");
Console.Write(" ");
Console.Write(bg == 0 ? " " : BGs[bg]);
Console.Write(" ");
}
Console.WriteLine();
for (int fg = 0; fg < FGs.Length; fg++)
{
Console.Write("\x1b[m");
if (fg >= 0)
{
Console.Write(FGs[fg] + "\t");
}
if (fg == 0)
{
Console.Write("\x1b[39m");
}
else
{
Console.Write("\x1b[" + FGs[fg]);
}
for (int bg = 0; bg < BGs.Length; bg++)
{
if (bg > 0)
{
Console.Write(" ");
}
if (bg == 0)
{
Console.Write("\x1b[49m");
}
else
{
Console.Write("\x1b[" +BGs[bg]);
}
Console.Write(test);
Console.Write("\x1b[49m");
}
Console.Write("\n");
}
Console.Write("\n");
// Reset foreground and background colors
Console.Write("\x1b[m");
}
private static IntPtr GetStdOutputHandle()
{
return GetStdHandle(STD_OUTPUT_HANDLE);
}
static void PrintSchemes()
{
@ -193,7 +308,7 @@ namespace ColorTool
if (Directory.Exists(schemeDirectory))
{
IntPtr handle = GetStdHandle(-11);
IntPtr handle = GetStdOutputHandle();
GetConsoleMode(handle, out var mode);
SetConsoleMode(handle, mode | 0x4);
@ -239,8 +354,7 @@ namespace ColorTool
static bool SetProperties(ColorScheme colorScheme)
{
CONSOLE_SCREEN_BUFFER_INFO_EX csbiex = CONSOLE_SCREEN_BUFFER_INFO_EX.Create();
int STD_OUTPUT_HANDLE = -11;
IntPtr hOut = GetStdHandle(STD_OUTPUT_HANDLE);
IntPtr hOut = GetStdOutputHandle();
bool success = GetConsoleScreenBufferInfoEx(hOut, ref csbiex);
if (success)
{
@ -267,6 +381,38 @@ namespace ColorTool
return success;
}
static bool SetPropertiesWithVt(ColorScheme colorScheme)
{
IntPtr hOut = GetStdOutputHandle();
uint originalMode;
uint requestedMode;
bool succeeded = GetConsoleMode(hOut, out originalMode);
if (succeeded)
{
requestedMode = originalMode | (uint)ConsoleAPI.ConsoleOutputModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, requestedMode);
}
for (int i = 0; i < colorScheme.colorTable.Length; i++)
{
int vtIndex = VT_INDICIES[i];
Color color = UIntToColor(colorScheme.colorTable[i]);
string s = $"\x1b]4;{vtIndex};rgb:{color.R:X}/{color.G:X}/{color.B:X}\x7";
Console.Write(s);
}
if (!quietMode)
{
PrintTableWithVt();
}
if (succeeded)
{
SetConsoleMode(hOut, originalMode);
}
return true;
}
static bool SetDefaults(ColorScheme colorScheme)
{
//TODO
@ -283,8 +429,7 @@ namespace ColorTool
static bool ExportCurrentAsIni(string outputPath)
{
CONSOLE_SCREEN_BUFFER_INFO_EX csbiex = CONSOLE_SCREEN_BUFFER_INFO_EX.Create();
int STD_OUTPUT_HANDLE = -11;
IntPtr hOut = GetStdHandle(STD_OUTPUT_HANDLE);
IntPtr hOut = GetStdOutputHandle();
bool success = GetConsoleScreenBufferInfoEx(hOut, ref csbiex);
if (success)
{
@ -349,6 +494,11 @@ namespace ColorTool
case "--version":
Version();
return;
case "-x":
case "--xterm":
setUnixStyle = true;
setProperties = true;
break;
case "-o":
case "--output":
if (i+1 < args.Length)
@ -385,7 +535,14 @@ namespace ColorTool
}
if (setProperties)
{
SetProperties(colorScheme);
if (setUnixStyle)
{
SetPropertiesWithVt(colorScheme);
}
else
{
SetProperties(colorScheme);
}
}
}

View file

@ -1,143 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="IniLoadError" xml:space="preserve">
<value>Error loading ini file "{0}"</value>
</data>
<data name="IniParseError" xml:space="preserve">
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="IniLoadError" xml:space="preserve">
<value>Error loading ini file "{0}"</value>
</data>
<data name="IniParseError" xml:space="preserve">
<value>Error loading ini file "{0}"
for key "{1}"
the value "{2}" is invalid</value>
</data>
<data name="InvalidColor" xml:space="preserve">
<value>Invalid Color</value>
</data>
<data name="InvalidNumberOfColors" xml:space="preserve">
<value>Invalid scheme - did not find 16 colors</value>
</data>
<data name="OutputUsage" xml:space="preserve">
<value>Usage: colortool -o &lt;filename&gt;</value>
</data>
<data name="SchemeNotFound" xml:space="preserve">
<value>Could not find or load "{0}"</value>
</data>
<data name="Usage" xml:space="preserve">
the value "{2}" is invalid</value>
</data>
<data name="InvalidColor" xml:space="preserve">
<value>Invalid Color</value>
</data>
<data name="InvalidNumberOfColors" xml:space="preserve">
<value>Invalid scheme - did not find 16 colors</value>
</data>
<data name="OutputUsage" xml:space="preserve">
<value>Usage: colortool -o &lt;filename&gt;</value>
</data>
<data name="SchemeNotFound" xml:space="preserve">
<value>Could not find or load "{0}"</value>
</data>
<data name="Usage" xml:space="preserve">
<value>Usage:
colortool.exe [options] &lt;schemename&gt;
ColorTool is a utility for helping to set the color palette of the Windows Console.
@ -154,14 +154,16 @@ Options:
-q, --quiet : Don't print the color table after applying
-d, --defaults : Apply the scheme to only the defaults in the registry
-b, --both : Apply the scheme to both the current console and the defaults.
-x, --xterm : Set the colors using VT sequences. Used for setting the colors in WSL.
Only works in Windows versions &gt;= 17048.
-s, --schemes : Displays all available schemes
-v, --version : Display the version number
-o, --output &lt;filename&gt; : output the current color table to an file (in .ini format)
Available importers:
{0}</value>
</data>
<data name="WroteToDefaults" xml:space="preserve">
<value>Wrote selected scheme to the defaults.</value>
</data>
{0}</value>
</data>
<data name="WroteToDefaults" xml:space="preserve">
<value>Wrote selected scheme to the defaults.</value>
</data>
</root>