diff --git a/tools/ColorTool/ColorTool/ColorScheme.cs b/tools/ColorTool/ColorTool/ColorScheme.cs index f930f31e548..62c0eb47d1a 100644 --- a/tools/ColorTool/ColorTool/ColorScheme.cs +++ b/tools/ColorTool/ColorTool/ColorScheme.cs @@ -1,31 +1,74 @@ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft. All rights reserved. // Licensed under the terms described in the LICENSE file in the root of this project. // using System; +using System.Drawing; using System.Linq; namespace ColorTool { + /// + /// Represents a colorscheme that can be applied to a console. + /// public class ColorScheme { - public uint[] colorTable = null; - public ConsoleAttributes consoleAttributes; + public ColorScheme(uint[] colorTable, ConsoleAttributes consoleAttributes) + { + ColorTable = colorTable; + ConsoleAttributes = consoleAttributes; + } + + public uint[] ColorTable { get; } + public ConsoleAttributes ConsoleAttributes { get; } + + public ushort? ScreenColorAttributes => + CalculateBackgroundForegroundAttributes( + this.ConsoleAttributes.Background, + this.ConsoleAttributes.Foreground + ); + + public ushort? PopupColorAttributes => + CalculateBackgroundForegroundAttributes( + this.ConsoleAttributes.PopupBackground, + this.ConsoleAttributes.PopupForeground + ); + + public Color this[int index] => UIntToColor(ColorTable[index]); + + private static Color UIntToColor(uint color) + { + byte r = (byte)(color >> 0); + byte g = (byte)(color >> 8); + byte b = (byte)(color >> 16); + return Color.FromArgb(r, g, b); + } + private ushort? CalculateBackgroundForegroundAttributes(uint? background, uint? foreground) + { + if(!(background.HasValue && foreground.HasValue)) + { + return null; + } + int fgidx = this.CalculateIndex(foreground.Value); + int bgidx = this.CalculateIndex(background.Value); + var attributes = (ushort)(fgidx | (bgidx << 4)); + return attributes; + } public int CalculateIndex(uint value) => - colorTable.Select((color, idx) => Tuple.Create(color, idx)) + ColorTable.Select((color, idx) => Tuple.Create(color, idx)) .OrderBy(Difference(value)) .First().Item2; private static Func, double> Difference(uint c1) => - // heuristic 1: nearest neighbor in RGB space - // tup => Distance(RGB(c1), RGB(tup.Item1)); - // heuristic 2: nearest neighbor in RGB space - // tup => Distance(HSV(c1), HSV(tup.Item1)); - // heuristic 3: weighted RGB L2 distance - tup => WeightedRGBSimilarity(c1, tup.Item1); + // heuristic 1: nearest neighbor in RGB space + // tup => Distance(RGB(c1), RGB(tup.Item1)); + // heuristic 2: nearest neighbor in RGB space + // tup => Distance(HSV(c1), HSV(tup.Item1)); + // heuristic 3: weighted RGB L2 distance + tup => WeightedRGBSimilarity(c1, tup.Item1); private static double WeightedRGBSimilarity(uint c1, uint c2) { @@ -36,9 +79,6 @@ private static double WeightedRGBSimilarity(uint c1, uint c2) return Math.Sqrt(dist[0] * (2 + rbar / 256.0) + dist[1] * 4 + dist[2] * (2 + (255 - rbar) / 256.0)); } - private static double Distance(uint[] c1c, uint[] c2c) - => Math.Sqrt(c1c.Zip(c2c, (a, b) => Math.Pow((int)a - (int)b, 2)).Sum()); - internal static uint[] RGB(uint c) => new[] { c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF }; internal static uint[] HSV(uint c) @@ -77,17 +117,17 @@ internal void Dump() for (int i = 0; i < 16; ++i) { - _dump($"Color[{i}]", colorTable[i]); + _dump($"Color[{i}]", ColorTable[i]); } - if (consoleAttributes.foreground != null) + if (ConsoleAttributes.Foreground != null) { - _dump("FG ", consoleAttributes.foreground.Value); + _dump("FG ", ConsoleAttributes.Foreground.Value); } - if (consoleAttributes.background != null) + if (ConsoleAttributes.Background != null) { - _dump("BG ", consoleAttributes.background.Value); + _dump("BG ", ConsoleAttributes.Background.Value); } } } diff --git a/tools/ColorTool/ColorTool/ColorTable.cs b/tools/ColorTool/ColorTool/ColorTable.cs new file mode 100644 index 00000000000..29470ac1124 --- /dev/null +++ b/tools/ColorTool/ColorTool/ColorTable.cs @@ -0,0 +1,205 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the terms described in the LICENSE file in the root of this project. +// + +using System; +using System.Collections.Generic; + +namespace ColorTool +{ + /// + /// Displays the color table that demonstrates the current colorscheme. + /// + static class ColorTable + { + private const int DarkBlack = 0; + private const int DarkBlue = 1; + private const int DarkGreen = 2; + private const int DarkCyan = 3; + private const int DarkRed = 4; + private const int DarkMagenta = 5; + private const int DarkYellow = 6; + private const int DarkWhite = 7; + private const int BrightBlack = 8; + private const int BrightBlue = 9; + private const int BrightGreen = 10; + private const int BrightCyan = 11; + private const int BrightRed = 12; + private const int BrightMagenta = 13; + private const int BrightYellow = 14; + private const int BrightWhite = 15; + + // This is the order of colors when output by the table. + private static readonly IReadOnlyList Foregrounds = new[] + { + BrightWhite, + DarkBlack, + BrightBlack, + DarkRed, + BrightRed, + DarkGreen, + BrightGreen, + DarkYellow, + BrightYellow, + DarkBlue, + BrightBlue, + DarkMagenta, + BrightMagenta, + DarkCyan, + BrightCyan, + DarkWhite, + BrightWhite + }; + + private static readonly IReadOnlyList Backgrounds = new[] + { + DarkBlack, + DarkRed, + DarkGreen, + DarkYellow, + DarkBlue, + DarkMagenta, + DarkCyan, + DarkWhite + }; + + private const string TestText = " gYw "; + + private static readonly IReadOnlyList AnsiForegroundSequences = new[] + { + "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" + }; + + private static readonly IReadOnlyList AnsiBackgroundSequences = new[] + { + "m", + "40m", + "41m", + "42m", + "43m", + "44m", + "45m", + "46m", + "47m" + }; + + public static void PrintTable() + { + ConsoleColor[] colors = (ConsoleColor[])ConsoleColor.GetValues(typeof(ConsoleColor)); + // Save the current background and foreground colors. + ConsoleColor currentBackground = Console.BackgroundColor; + ConsoleColor currentForeground = Console.ForegroundColor; + + Console.Write("\t"); + for (int bg = 0; bg < AnsiBackgroundSequences.Count; bg++) + { + if (bg > 0) Console.Write(" "); + Console.Write(" "); + Console.Write(bg == 0 ? " " : AnsiBackgroundSequences[bg]); + Console.Write(" "); + } + Console.WriteLine(); + + for (int fg = 0; fg < AnsiForegroundSequences.Count; fg++) + { + Console.ForegroundColor = currentForeground; + Console.BackgroundColor = currentBackground; + + if (fg >= 0) Console.Write(AnsiForegroundSequences[fg] + "\t"); + + if (fg == 0) Console.ForegroundColor = currentForeground; + else Console.ForegroundColor = colors[Foregrounds[fg - 1]]; + + for (int bg = 0; bg < AnsiBackgroundSequences.Count; bg++) + { + if (bg > 0) Console.Write(" "); + if (bg == 0) + Console.BackgroundColor = currentBackground; + else Console.BackgroundColor = colors[Backgrounds[bg - 1]]; + Console.Write(TestText); + Console.BackgroundColor = currentBackground; + } + Console.Write("\n"); + } + Console.Write("\n"); + + // Reset foreground and background colors + Console.ForegroundColor = currentForeground; + Console.BackgroundColor = currentBackground; + } + + public static void PrintTableWithVt() + { + Console.Write("\t"); + for (int bg = 0; bg < AnsiBackgroundSequences.Count; bg++) + { + if (bg > 0) Console.Write(" "); + Console.Write(" "); + Console.Write(bg == 0 ? " " : AnsiBackgroundSequences[bg]); + Console.Write(" "); + } + Console.WriteLine(); + + for (int fg = 0; fg < AnsiForegroundSequences.Count; fg++) + { + Console.Write("\x1b[m"); + + if (fg >= 0) + { + Console.Write(AnsiForegroundSequences[fg] + "\t"); + } + + if (fg == 0) + { + Console.Write("\x1b[39m"); + } + else + { + Console.Write("\x1b[" + AnsiForegroundSequences[fg]); + } + + for (int bg = 0; bg < AnsiBackgroundSequences.Count; bg++) + { + if (bg > 0) + { + Console.Write(" "); + } + if (bg == 0) + { + Console.Write("\x1b[49m"); + } + else + { + Console.Write("\x1b[" + AnsiBackgroundSequences[bg]); + } + + Console.Write(TestText); + Console.Write("\x1b[49m"); + } + Console.Write("\n"); + } + Console.Write("\n"); + + // Reset foreground and background colors + Console.Write("\x1b[m"); + } + } +} diff --git a/tools/ColorTool/ColorTool/ColorTool.csproj b/tools/ColorTool/ColorTool/ColorTool.csproj index 5cfeab4d923..a1df3abb456 100644 --- a/tools/ColorTool/ColorTool/ColorTool.csproj +++ b/tools/ColorTool/ColorTool/ColorTool.csproj @@ -2,7 +2,8 @@ Exe - net46 + net461 false + latest diff --git a/tools/ColorTool/ColorTool/ConsoleAPI.cs b/tools/ColorTool/ColorTool/ConsoleAPI.cs index 3219d34e01f..8df31f64dc3 100644 --- a/tools/ColorTool/ColorTool/ConsoleAPI.cs +++ b/tools/ColorTool/ColorTool/ConsoleAPI.cs @@ -1,5 +1,5 @@ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft. All rights reserved. // Licensed under the terms described in the LICENSE file in the root of this project. // @@ -8,9 +8,11 @@ namespace ColorTool { - class ConsoleAPI + static class ConsoleAPI { - //////////////////////////////////////////////////////////////////////// + private const int StdOutputHandle = -11; + public const int ColorTableSize = 16; + [StructLayout(LayoutKind.Sequential)] public struct COORD { @@ -58,7 +60,7 @@ public static CONSOLE_SCREEN_BUFFER_INFO_EX Create() } } - public static int STD_OUTPUT_HANDLE = -11; + public static IntPtr GetStdOutputHandle() => GetStdHandle(StdOutputHandle); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GetStdHandle(int nStdHandle); @@ -83,13 +85,10 @@ IntPtr lpReserved [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; } } diff --git a/tools/ColorTool/ColorTool/ConsoleAttributes.cs b/tools/ColorTool/ColorTool/ConsoleAttributes.cs index ac2d9bceb38..40234528fc3 100644 --- a/tools/ColorTool/ColorTool/ConsoleAttributes.cs +++ b/tools/ColorTool/ColorTool/ConsoleAttributes.cs @@ -1,17 +1,26 @@ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft. All rights reserved. // Licensed under the terms described in the LICENSE file in the root of this project. // namespace ColorTool { - public struct ConsoleAttributes + /// + /// Keeps track of the color table indices for the background/foreground in a colorscheme. + /// + public readonly struct ConsoleAttributes { - public uint? foreground; - public uint? background; - - public uint? popupForeground; - public uint? popupBackground; + public ConsoleAttributes(uint? background, uint? foreground, uint? popupBackground, uint? popupForeground) + { + Background = background; + Foreground = foreground; + PopupBackground = popupBackground; + PopupForeground = popupForeground; + } + public uint? Foreground { get; } + public uint? Background { get; } + public uint? PopupForeground { get; } + public uint? PopupBackground { get; } } } diff --git a/tools/ColorTool/ColorTool/ConsoleTargets/CurrentConsoleTarget.cs b/tools/ColorTool/ColorTool/ConsoleTargets/CurrentConsoleTarget.cs new file mode 100644 index 00000000000..f116d75cd12 --- /dev/null +++ b/tools/ColorTool/ColorTool/ConsoleTargets/CurrentConsoleTarget.cs @@ -0,0 +1,47 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the terms described in the LICENSE file in the root of this project. +// + +using System; +using static ColorTool.ConsoleAPI; + +namespace ColorTool.ConsoleTargets +{ + /// + /// A console target that writes to the currently open console. + /// + class CurrentConsoleTarget : IConsoleTarget + { + public void ApplyColorScheme(ColorScheme colorScheme, bool quietMode) + { + CONSOLE_SCREEN_BUFFER_INFO_EX csbiex = CONSOLE_SCREEN_BUFFER_INFO_EX.Create(); + IntPtr hOut = GetStdOutputHandle(); + bool success = GetConsoleScreenBufferInfoEx(hOut, ref csbiex); + if (!success) + { + throw new InvalidOperationException("Could not obtain console screen buffer"); + } + + csbiex.srWindow.Bottom++; + for (int i = 0; i < 16; i++) + { + csbiex.ColorTable[i] = colorScheme.ColorTable[i]; + } + if(colorScheme.ScreenColorAttributes is ushort wAttrs) + { + csbiex.wAttributes = wAttrs; + } + if(colorScheme.PopupColorAttributes is ushort wPopupAttrs) + { + csbiex.wPopupAttributes = wPopupAttrs; + } + SetConsoleScreenBufferInfoEx(hOut, ref csbiex); + + if (!quietMode) + { + ColorTable.PrintTable(); + } + } + } +} diff --git a/tools/ColorTool/ColorTool/ConsoleTargets/DefaultConsoleTarget.cs b/tools/ColorTool/ColorTool/ConsoleTargets/DefaultConsoleTarget.cs new file mode 100644 index 00000000000..1948078119d --- /dev/null +++ b/tools/ColorTool/ColorTool/ConsoleTargets/DefaultConsoleTarget.cs @@ -0,0 +1,36 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the terms described in the LICENSE file in the root of this project. +// + +using Microsoft.Win32; +using System; + +namespace ColorTool.ConsoleTargets +{ + /// + /// A console target that writes to the Windows registry to modify system defaults + /// + class DefaultConsoleTarget : IConsoleTarget + { + public void ApplyColorScheme(ColorScheme colorScheme, bool quietMode) + { + RegistryKey consoleKey = Registry.CurrentUser.OpenSubKey("Console", true); + for (int i = 0; i < colorScheme.ColorTable.Length; i++) + { + string valueName = "ColorTable" + (i < 10 ? "0" : "") + i; + consoleKey.SetValue(valueName, colorScheme.ColorTable[i], RegistryValueKind.DWord); + } + if(colorScheme.ScreenColorAttributes is ushort screenColors) + { + consoleKey.SetValue("ScreenColors", screenColors, RegistryValueKind.DWord); + } + if(colorScheme.PopupColorAttributes is ushort popupColors) + { + consoleKey.SetValue("PopupColors", popupColors, RegistryValueKind.DWord); + } + + Console.WriteLine(Resources.WroteToDefaults); + } + } +} diff --git a/tools/ColorTool/ColorTool/ConsoleTargets/IConsoleTarget.cs b/tools/ColorTool/ColorTool/ConsoleTargets/IConsoleTarget.cs new file mode 100644 index 00000000000..a1a2b9d2a85 --- /dev/null +++ b/tools/ColorTool/ColorTool/ConsoleTargets/IConsoleTarget.cs @@ -0,0 +1,15 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the terms described in the LICENSE file in the root of this project. +// + +namespace ColorTool.ConsoleTargets +{ + /// + /// A console that can have a color scheme applied to it. + /// + interface IConsoleTarget + { + void ApplyColorScheme(ColorScheme colorScheme, bool quietMode); + } +} diff --git a/tools/ColorTool/ColorTool/ConsoleTargets/VirtualTerminalConsoleTarget.cs b/tools/ColorTool/ColorTool/ConsoleTargets/VirtualTerminalConsoleTarget.cs new file mode 100644 index 00000000000..bbc24d96b06 --- /dev/null +++ b/tools/ColorTool/ColorTool/ConsoleTargets/VirtualTerminalConsoleTarget.cs @@ -0,0 +1,69 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the terms described in the LICENSE file in the root of this project. +// + +using System; +using System.Collections.Generic; +using System.Drawing; +using static ColorTool.ConsoleAPI; + +namespace ColorTool.ConsoleTargets +{ + /// + /// A console target that writes to the currently open console, using VT sequences. + /// + class VirtualTerminalConsoleTarget : IConsoleTarget + { + // Use a Console index in to get a VT index out. + public static readonly IReadOnlyList VirtualTerminalIndices = new[] + { + 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 + }; + + public void ApplyColorScheme(ColorScheme colorScheme, bool quietMode) + { + 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 = VirtualTerminalIndices[i]; + Color color = colorScheme[i]; + string s = $"\x1b]4;{vtIndex};rgb:{color.R:X}/{color.G:X}/{color.B:X}\x7"; + Console.Write(s); + } + if (!quietMode) + { + ColorTable.PrintTableWithVt(); + } + + if (succeeded) + { + SetConsoleMode(hOut, originalMode); + } + } + } +} diff --git a/tools/ColorTool/ColorTool/Program.cs b/tools/ColorTool/ColorTool/Program.cs index 3496d606dea..b9d12f47855 100644 --- a/tools/ColorTool/ColorTool/Program.cs +++ b/tools/ColorTool/ColorTool/Program.cs @@ -1,497 +1,25 @@ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft. All rights reserved. // Licensed under the terms described in the LICENSE file in the root of this project. // -using Microsoft.Win32; +using ColorTool.ConsoleTargets; +using ColorTool.SchemeWriters; using System; using System.Collections.Generic; -using System.Drawing; -using System.IO; using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using static ColorTool.ConsoleAPI; namespace ColorTool { - class Program + static class Program { - static int DARK_BLACK = 0; - static int DARK_BLUE = 1; - static int DARK_GREEN = 2; - static int DARK_CYAN = 3; - static int DARK_RED = 4; - static int DARK_MAGENTA = 5; - static int DARK_YELLOW = 6; - static int DARK_WHITE = 7; - static int BRIGHT_BLACK = 8; - static int BRIGHT_BLUE = 9; - static int BRIGHT_GREEN = 10; - static int BRIGHT_CYAN = 11; - static int BRIGHT_RED = 12; - static int BRIGHT_MAGENTA = 13; - static int BRIGHT_YELLOW = 14; - static int BRIGHT_WHITE = 15; + private static bool quietMode = false; + private static bool reportErrors = false; + private static bool setDefaults = false; + private static bool setProperties = true; + private static bool setUnixStyle = false; - static int[] saneFgs = { - DARK_BLACK , - DARK_RED , - DARK_GREEN , - DARK_YELLOW , - DARK_BLUE , - DARK_MAGENTA , - DARK_CYAN , - DARK_WHITE , - BRIGHT_BLACK , - BRIGHT_RED , - BRIGHT_GREEN , - BRIGHT_YELLOW , - BRIGHT_MAGENTA , - BRIGHT_BLUE , - BRIGHT_CYAN , - BRIGHT_WHITE - }; - - // This is the order of colors when output by the table. - static int[] outputFgs = { - BRIGHT_WHITE , - DARK_BLACK , - BRIGHT_BLACK , - DARK_RED , - BRIGHT_RED , - DARK_GREEN , - BRIGHT_GREEN , - DARK_YELLOW , - BRIGHT_YELLOW , - DARK_BLUE , - BRIGHT_BLUE , - DARK_MAGENTA , - BRIGHT_MAGENTA , - DARK_CYAN , - BRIGHT_CYAN , - DARK_WHITE , - BRIGHT_WHITE - }; - - static int[] saneBgs = { - DARK_BLACK , - DARK_RED , - DARK_GREEN , - DARK_YELLOW , - DARK_BLUE , - DARK_MAGENTA , - DARK_CYAN , - 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 reportErrors = false; - static bool setDefaults = false; - static bool setProperties = true; - static bool setUnixStyle = false; - - static void Usage() - { - Console.WriteLine(Resources.Usage, - string.Join($"{Environment.NewLine} ", GetParsers().Select(p => p.Name))); - } - - static void OutputUsage() - { - Console.WriteLine(Resources.OutputUsage); - } - - static void Version() - { - var assembly = System.Reflection.Assembly.GetExecutingAssembly(); - var info = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location); - Console.WriteLine($"colortool v{info.FileVersion}"); - } - - static void PrintTable() - { - ConsoleColor[] colors = (ConsoleColor[])ConsoleColor.GetValues(typeof(ConsoleColor)); - // Save the current background and foreground colors. - ConsoleColor currentBackground = Console.BackgroundColor; - ConsoleColor currentForeground = Console.ForegroundColor; - 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.ForegroundColor = currentForeground; - Console.BackgroundColor = currentBackground; - - if (fg >= 0) Console.Write(FGs[fg] + "\t"); - - if (fg == 0) Console.ForegroundColor = currentForeground; - else Console.ForegroundColor = colors[outputFgs[fg - 1]]; - - for (int bg = 0; bg < BGs.Length; bg++) - { - if (bg > 0) Console.Write(" "); - if (bg == 0) - Console.BackgroundColor = currentBackground; - else Console.BackgroundColor = colors[saneBgs[bg - 1]]; - Console.Write(test); - Console.BackgroundColor = currentBackground; - } - Console.Write("\n"); - - } - Console.Write("\n"); - - // Reset foreground and background colors - 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() - { - var schemeDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "schemes"); - - if (Directory.Exists(schemeDirectory)) - { - IntPtr handle = GetStdOutputHandle(); - GetConsoleMode(handle, out var mode); - SetConsoleMode(handle, mode | 0x4); - - int consoleWidth = Console.WindowWidth; - string fgText = " gYw "; - foreach (string schemeName in Directory.GetFiles(schemeDirectory).Select(Path.GetFileName)) - { - ColorScheme colorScheme = GetScheme(schemeName, false); - if (colorScheme != null) - { - string colors = string.Empty; - for (var index = 0; index < 8; index++) - { - uint t = colorScheme.colorTable[index]; - var color = UIntToColor(t); - // Set the background color to the color in the scheme, plus some text to show how it looks - colors += $"\x1b[48;2;{color.R};{color.G};{color.B}m{fgText}"; - } - // Align scheme colors right, or on newline if it doesn't fit - int schemeTextLength = fgText.Length * 8; - int bufferLength = consoleWidth - (schemeName.Length + schemeTextLength); - - string bufferString = bufferLength >= 0 - ? new string(' ', bufferLength) - : "\n" + new string(' ', consoleWidth - schemeTextLength); - - string outputString = schemeName + bufferString + colors; - Console.WriteLine(outputString); - Console.ResetColor(); - } - } - } - } - - static void PrintSchemesDirectory() - { - string schemeDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "schemes"); - Console.WriteLine(schemeDirectory); - } - - private static Color UIntToColor(uint color) - { - byte r = (byte)(color >> 0); - byte g = (byte)(color >> 8); - byte b = (byte)(color >> 16); - return Color.FromArgb(r, g, b); - } - - static bool SetProperties(ColorScheme colorScheme) - { - CONSOLE_SCREEN_BUFFER_INFO_EX csbiex = CONSOLE_SCREEN_BUFFER_INFO_EX.Create(); - IntPtr hOut = GetStdOutputHandle(); - bool success = GetConsoleScreenBufferInfoEx(hOut, ref csbiex); - if (success) - { - csbiex.srWindow.Bottom++; - for (int i = 0; i < 16; i++) - { - csbiex.ColorTable[i] = colorScheme.colorTable[i]; - } - if (colorScheme.consoleAttributes.background != null && colorScheme.consoleAttributes.foreground != null) - { - int fgidx = colorScheme.CalculateIndex(colorScheme.consoleAttributes.foreground.Value); - int bgidx = colorScheme.CalculateIndex(colorScheme.consoleAttributes.background.Value); - csbiex.wAttributes = (ushort)(fgidx | (bgidx << 4)); - } - if (colorScheme.consoleAttributes.popupBackground != null && colorScheme.consoleAttributes.popupForeground != null) - { - int fgidx = colorScheme.CalculateIndex(colorScheme.consoleAttributes.popupForeground.Value); - int bgidx = colorScheme.CalculateIndex(colorScheme.consoleAttributes.popupBackground.Value); - csbiex.wPopupAttributes = (ushort)(fgidx | (bgidx << 4)); - } - SetConsoleScreenBufferInfoEx(hOut, ref csbiex); - } - if (success) - { - if (!quietMode) - { - PrintTable(); - } - } - 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 - RegistryKey consoleKey = Registry.CurrentUser.OpenSubKey("Console", true); - for (int i = 0; i < colorScheme.colorTable.Length; i++) - { - string valueName = "ColorTable" + (i < 10 ? "0" : "") + i; - consoleKey.SetValue(valueName, colorScheme.colorTable[i], RegistryValueKind.DWord); - } - Console.WriteLine(Resources.WroteToDefaults); - return true; - } - - static bool ExportCurrentAsIni(string outputPath) - { - CONSOLE_SCREEN_BUFFER_INFO_EX csbiex = CONSOLE_SCREEN_BUFFER_INFO_EX.Create(); - IntPtr hOut = GetStdOutputHandle(); - bool success = GetConsoleScreenBufferInfoEx(hOut, ref csbiex); - if (success) - { - try - { - // StreamWriter can fail for a variety of file system reasons so catch exceptions and print message. - using (System.IO.StreamWriter file = new System.IO.StreamWriter(outputPath)) - { - file.WriteLine("[table]"); - for (int i = 0; i < 16; i++) - { - string line = IniSchemeParser.COLOR_NAMES[i]; - line += " = "; - uint color = csbiex.ColorTable[i]; - uint r = color & (0x000000ff); - uint g = (color & (0x0000ff00)) >> 8; - uint b = (color & (0x00ff0000)) >> 16; - line += r + "," + g + "," + b; - file.WriteLine(line); - } - - file.WriteLine(); - file.WriteLine("[screen]"); - var forgroundIndex = csbiex.wAttributes & 0xF; - var backgroundIndex = csbiex.wAttributes >> 4; - file.WriteLine($"FOREGROUND = {IniSchemeParser.COLOR_NAMES[forgroundIndex]}"); - file.WriteLine($"BACKGROUND = {IniSchemeParser.COLOR_NAMES[backgroundIndex]}"); - - file.WriteLine(); - file.WriteLine("[popup]"); - forgroundIndex = csbiex.wPopupAttributes & 0xF; - backgroundIndex = csbiex.wPopupAttributes >> 4; - file.WriteLine($"FOREGROUND = {IniSchemeParser.COLOR_NAMES[forgroundIndex]}"); - file.WriteLine($"BACKGROUND = {IniSchemeParser.COLOR_NAMES[backgroundIndex]}"); - } - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - } - else - { - Console.WriteLine("Failed to get console information."); - } - return success; - } - - static void Main(string[] args) + public static void Main(string[] args) { if (args.Length < 1) { @@ -505,7 +33,7 @@ static void Main(string[] args) { case "-c": case "--current": - PrintTable(); + ColorTable.PrintTable(); return; case "-e": case "--errors": @@ -535,7 +63,7 @@ static void Main(string[] args) return; case "-l": case "--location": - PrintSchemesDirectory(); + SchemeManager.PrintSchemesDirectory(); return; case "-x": case "--xterm": @@ -546,7 +74,7 @@ static void Main(string[] args) case "--output": if (i + 1 < args.Length) { - ExportCurrentAsIni(args[i + 1]); + new IniSchemeWriter().ExportCurrentAsIni(args[i + 1]); } else { @@ -555,7 +83,7 @@ static void Main(string[] args) return; case "-s": case "--schemes": - PrintSchemes(); + SchemeManager.PrintSchemes(); return; default: break; @@ -564,7 +92,7 @@ static void Main(string[] args) string schemeName = args[args.Length - 1]; - ColorScheme colorScheme = GetScheme(schemeName, reportErrors); + ColorScheme colorScheme = SchemeManager.GetScheme(schemeName, reportErrors); if (colorScheme == null) { @@ -572,41 +100,51 @@ static void Main(string[] args) return; } - if (setDefaults) + foreach (var target in GetConsoleTargets()) { - SetDefaults(colorScheme); - } - if (setProperties) - { - if (setUnixStyle) - { - SetPropertiesWithVt(colorScheme); - } - else - { - SetProperties(colorScheme); - } + target.ApplyColorScheme(colorScheme, quietMode); } } - private static IEnumerable GetParsers() + private static void Usage() { - return typeof(Program).Assembly.GetTypes() - .Where(t => !t.IsAbstract && typeof(ISchemeParser).IsAssignableFrom(t)) - .Select(t => (ISchemeParser)Activator.CreateInstance(t)); + Console.WriteLine(Resources.Usage, + string.Join($"{Environment.NewLine} ", SchemeManager.GetParsers().Select(p => p.Name))); + } + + private static void OutputUsage() + { + Console.WriteLine(Resources.OutputUsage); } - private static ColorScheme GetScheme(string schemeName, bool reportErrors = false) + private static void Version() { - foreach (var parser in GetParsers()) + var assembly = System.Reflection.Assembly.GetExecutingAssembly(); + var info = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location); + Console.WriteLine($"colortool v{info.FileVersion}"); + } + + /// + /// Returns an enumerable of consoles that we want to apply the colorscheme to. + /// The contents of this enumerable depends on the user's provided command line flags. + /// + private static IEnumerable GetConsoleTargets() + { + if (setDefaults) + { + yield return new DefaultConsoleTarget(); + } + if (setProperties) { - ColorScheme scheme = parser.ParseScheme(schemeName, reportErrors); - if (scheme != null) + if (setUnixStyle) + { + yield return new VirtualTerminalConsoleTarget(); + } + else { - return scheme; + yield return new CurrentConsoleTarget(); } } - return null; } } } diff --git a/tools/ColorTool/ColorTool/Scheme.cs b/tools/ColorTool/ColorTool/Scheme.cs deleted file mode 100644 index 3d7eb6ce726..00000000000 --- a/tools/ColorTool/ColorTool/Scheme.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// Licensed under the terms described in the LICENSE file in the root of this project. -// - -using System.Collections.Generic; -using System.IO; - -namespace ColorTool -{ - static class Scheme - { - public static IEnumerable GetSearchPaths(string schemeName, string extension) - { - // Search order, for argument "name", where 'exe' is the dir of the exe. - // 1. ./name - // 2. ./name.ext - // 3. ./schemes/name - // 4. ./schemes/name.ext - // 5. exe/schemes/name - // 6. exe/schemes/name.ext - // 7. name (as an absolute path) - - string cwd = "./"; - yield return cwd + schemeName; - - string filename = schemeName + extension; - yield return cwd + filename; - - string cwdSchemes = "./schemes/"; - yield return cwdSchemes + schemeName; - yield return cwdSchemes + filename; - - string exeDir = Directory.GetParent(System.Reflection.Assembly.GetEntryAssembly().Location).FullName; - string exeSchemes = exeDir + "/schemes/"; - yield return exeSchemes + schemeName; - yield return exeSchemes + filename; - yield return schemeName; - } - } -} \ No newline at end of file diff --git a/tools/ColorTool/ColorTool/SchemeManager.cs b/tools/ColorTool/ColorTool/SchemeManager.cs new file mode 100644 index 00000000000..1f84a74601c --- /dev/null +++ b/tools/ColorTool/ColorTool/SchemeManager.cs @@ -0,0 +1,106 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the terms described in the LICENSE file in the root of this project. +// + +using ColorTool.SchemeParsers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using static ColorTool.ConsoleAPI; + +namespace ColorTool +{ + static class SchemeManager + { + public static IEnumerable GetSearchPaths(string schemeName, string extension) + { + // Search order, for argument "name", where 'exe' is the dir of the exe. + // 1. ./name + // 2. ./name.ext + // 3. ./schemes/name + // 4. ./schemes/name.ext + // 5. exe/schemes/name + // 6. exe/schemes/name.ext + // 7. name (as an absolute path) + + string cwd = "./"; + yield return cwd + schemeName; + + string filename = schemeName + extension; + yield return cwd + filename; + + string cwdSchemes = "./schemes/"; + yield return cwdSchemes + schemeName; + yield return cwdSchemes + filename; + + string exeDir = Directory.GetParent(System.Reflection.Assembly.GetEntryAssembly().Location).FullName; + string exeSchemes = exeDir + "/schemes/"; + yield return exeSchemes + schemeName; + yield return exeSchemes + filename; + yield return schemeName; + } + + public static void PrintSchemesDirectory() + { + string schemeDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "schemes"); + Console.WriteLine(schemeDirectory); + } + + public static void PrintSchemes() + { + var schemeDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "schemes"); + + if (Directory.Exists(schemeDirectory)) + { + IntPtr handle = GetStdOutputHandle(); + GetConsoleMode(handle, out var mode); + SetConsoleMode(handle, mode | 0x4); + + int consoleWidth = Console.WindowWidth; + string fgText = " gYw "; + foreach (string schemeName in Directory.GetFiles(schemeDirectory).Select(Path.GetFileName)) + { + ColorScheme colorScheme = GetScheme(schemeName, false); + if (colorScheme != null) + { + string colors = string.Empty; + for (var index = 0; index < 8; index++) + { + var color = colorScheme[index]; + // Set the background color to the color in the scheme, plus some text to show how it looks + colors += $"\x1b[48;2;{color.R};{color.G};{color.B}m{fgText}"; + } + // Align scheme colors right, or on newline if it doesn't fit + int schemeTextLength = fgText.Length * 8; + int bufferLength = consoleWidth - (schemeName.Length + schemeTextLength); + + string bufferString = bufferLength >= 0 + ? new string(' ', bufferLength) + : "\n" + new string(' ', consoleWidth - schemeTextLength); + + string outputString = schemeName + bufferString + colors; + Console.WriteLine(outputString); + Console.ResetColor(); + } + } + } + } + + public static ColorScheme GetScheme(string schemeName, bool reportErrors = false) + { + return GetParsers() + .Where(parser => parser.CanParse(schemeName)) + .Select(parser => parser.ParseScheme(schemeName, reportErrors)) + .FirstOrDefault(); + } + + public static IEnumerable GetParsers() + { + return typeof(Program).Assembly.GetTypes() + .Where(t => !t.IsAbstract && typeof(ISchemeParser).IsAssignableFrom(t)) + .Select(t => (ISchemeParser)Activator.CreateInstance(t)); + } + } +} \ No newline at end of file diff --git a/tools/ColorTool/ColorTool/ISchemeParser.cs b/tools/ColorTool/ColorTool/SchemeParsers/ISchemeParser.cs similarity index 66% rename from tools/ColorTool/ColorTool/ISchemeParser.cs rename to tools/ColorTool/ColorTool/SchemeParsers/ISchemeParser.cs index 01531509432..cb4ebce6aff 100644 --- a/tools/ColorTool/ColorTool/ISchemeParser.cs +++ b/tools/ColorTool/ColorTool/SchemeParsers/ISchemeParser.cs @@ -1,14 +1,14 @@ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft. All rights reserved. // Licensed under the terms described in the LICENSE file in the root of this project. // -namespace ColorTool +namespace ColorTool.SchemeParsers { interface ISchemeParser { string Name { get; } - + bool CanParse(string schemeName); ColorScheme ParseScheme(string schemeName, bool reportErrors = false); } } diff --git a/tools/ColorTool/ColorTool/IniSchemeParser.cs b/tools/ColorTool/ColorTool/SchemeParsers/IniSchemeParser.cs similarity index 74% rename from tools/ColorTool/ColorTool/IniSchemeParser.cs rename to tools/ColorTool/ColorTool/SchemeParsers/IniSchemeParser.cs index be332d6002a..e4e1add8242 100644 --- a/tools/ColorTool/ColorTool/IniSchemeParser.cs +++ b/tools/ColorTool/ColorTool/SchemeParsers/IniSchemeParser.cs @@ -1,5 +1,5 @@ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft. All rights reserved. // Licensed under the terms described in the LICENSE file in the root of this project. // @@ -7,19 +7,22 @@ using System.Linq; using System.Text; using System.Runtime.InteropServices; -using static ColorTool.ConsoleAPI; using System.IO; using System.Collections.Generic; +using static ColorTool.ConsoleAPI; -namespace ColorTool +namespace ColorTool.SchemeParsers { class IniSchemeParser : ISchemeParser { [DllImport("kernel32")] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); + private const string FileExtension = ".ini"; + // These are in Windows Color table order - BRG, not RGB. - public static string[] COLOR_NAMES = { + internal static readonly IReadOnlyList ColorNames = new[] + { "DARK_BLACK", "DARK_BLUE", "DARK_GREEN", @@ -38,44 +41,10 @@ class IniSchemeParser : ISchemeParser "BRIGHT_WHITE" }; - public string Name => "INI File Parser"; - - static uint ParseHex(string arg) - { - System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg); - return RGB(col.R, col.G, col.B); - } - - static uint ParseRgb(string arg) - { - int[] components = { 0, 0, 0 }; - string[] args = arg.Split(','); - if (args.Length != components.Length) throw new Exception("Invalid color format \"" + arg + "\""); - if (args.Length != 3) throw new Exception("Invalid color format \"" + arg + "\""); - for (int i = 0; i < args.Length; i++) - { - components[i] = Int32.Parse(args[i]); - } - - return RGB(components[0], components[1], components[2]); - } + public string Name { get; } = "INI File Parser"; - static uint ParseColor(string arg) - { - if (arg[0] == '#') - { - return ParseHex(arg.Substring(1)); - } - else - { - return ParseRgb(arg); - } - } - - static string FindIniScheme(string schemeName) - { - return Scheme.GetSearchPaths(schemeName, ".ini").FirstOrDefault(File.Exists); - } + public bool CanParse(string schemeName) => + string.Equals(Path.GetExtension(schemeName), FileExtension, StringComparison.OrdinalIgnoreCase); public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) { @@ -84,16 +53,16 @@ public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) string filename = FindIniScheme(schemeName); if (filename == null) return null; - string[] tableStrings = new string[COLOR_TABLE_SIZE]; + string[] tableStrings = new string[ColorTableSize]; uint[] colorTable = null; uint? foregroundColor = null; uint? backgroundColor = null; uint? popupForegroundColor = null; uint? popupBackgroundColor = null; - for (int i = 0; i < COLOR_TABLE_SIZE; i++) + for (int i = 0; i < ColorTableSize; i++) { - string name = COLOR_NAMES[i]; + string name = ColorNames[i]; StringBuilder buffer = new StringBuilder(512); GetPrivateProfileString("table", name, null, buffer, 512, filename); @@ -113,16 +82,16 @@ public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) { try { - colorTable = new uint[COLOR_TABLE_SIZE]; - for (int i = 0; i < COLOR_TABLE_SIZE; i++) + colorTable = new uint[ColorTableSize]; + for (int i = 0; i < ColorTableSize; i++) { colorTable[i] = ParseColor(tableStrings[i]); } if (ReadAttributes("popup", out var foreground, out var background)) { - var foregroundIndex = (COLOR_NAMES as IList).IndexOf(foreground); - var backgroundIndex = (COLOR_NAMES as IList).IndexOf(background); + var foregroundIndex = (ColorNames as IList).IndexOf(foreground); + var backgroundIndex = (ColorNames as IList).IndexOf(background); if (foregroundIndex != -1 && backgroundIndex != -1) { popupForegroundColor = colorTable[foregroundIndex]; @@ -132,8 +101,8 @@ public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) if (ReadAttributes("screen", out foreground, out background)) { - var foregroundIndex = (COLOR_NAMES as IList).IndexOf(foreground); - var backgroundIndex = (COLOR_NAMES as IList).IndexOf(background); + var foregroundIndex = (ColorNames as IList).IndexOf(foreground); + var backgroundIndex = (ColorNames as IList).IndexOf(background); if (foregroundIndex != -1 && backgroundIndex != -1) { foregroundColor = colorTable[foregroundIndex]; @@ -154,8 +123,8 @@ public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) if (colorTable != null) { - var consoleAttributes = new ConsoleAttributes { background = backgroundColor, foreground = foregroundColor, popupBackground = popupBackgroundColor, popupForeground = popupForegroundColor }; - return new ColorScheme { colorTable = colorTable, consoleAttributes = consoleAttributes }; + var consoleAttributes = new ConsoleAttributes(backgroundColor, foregroundColor, popupBackgroundColor, popupForegroundColor); + return new ColorScheme(colorTable, consoleAttributes); } else { @@ -170,18 +139,55 @@ bool ReadAttributes(string section, out string foreground, out string background StringBuilder buffer = new StringBuilder(512); GetPrivateProfileString(section, "FOREGROUND", null, buffer, 512, filename); foreground = buffer.ToString(); - if (!COLOR_NAMES.Contains(foreground)) + if (!ColorNames.Contains(foreground)) return false; buffer = new StringBuilder(512); GetPrivateProfileString(section, "BACKGROUND", null, buffer, 512, filename); background = buffer.ToString(); - if (!COLOR_NAMES.Contains(background)) + if (!ColorNames.Contains(background)) return false; return true; } } + + private static uint ParseHex(string arg) + { + System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg); + return RGB(col.R, col.G, col.B); + } + + private static uint ParseRgb(string arg) + { + int[] components = { 0, 0, 0 }; + string[] args = arg.Split(','); + if (args.Length != components.Length) throw new Exception("Invalid color format \"" + arg + "\""); + if (args.Length != 3) throw new Exception("Invalid color format \"" + arg + "\""); + for (int i = 0; i < args.Length; i++) + { + components[i] = Int32.Parse(args[i]); + } + + return RGB(components[0], components[1], components[2]); + } + + private static uint ParseColor(string arg) + { + if (arg[0] == '#') + { + return ParseHex(arg.Substring(1)); + } + else + { + return ParseRgb(arg); + } + } + + private static string FindIniScheme(string schemeName) + { + return SchemeManager.GetSearchPaths(schemeName, FileExtension).FirstOrDefault(File.Exists); + } } } diff --git a/tools/ColorTool/ColorTool/JsonParser.cs b/tools/ColorTool/ColorTool/SchemeParsers/JsonParser.cs similarity index 57% rename from tools/ColorTool/ColorTool/JsonParser.cs rename to tools/ColorTool/ColorTool/SchemeParsers/JsonParser.cs index dde2145df68..37218ba18ac 100644 --- a/tools/ColorTool/ColorTool/JsonParser.cs +++ b/tools/ColorTool/ColorTool/SchemeParsers/JsonParser.cs @@ -1,78 +1,59 @@ -using System; +// +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the terms described in the LICENSE file in the root of this project. +// + +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization.Json; -using System.Text; -using System.Threading.Tasks; using System.Xml; using static ColorTool.ConsoleAPI; -namespace ColorTool +namespace ColorTool.SchemeParsers { class JsonParser : ISchemeParser { - static string[] CONCFG_COLOR_NAMES = { - "black", // DARK_BLACK - "dark_blue", // DARK_BLUE - "dark_green", // DARK_GREEN - "dark_cyan", // DARK_CYAN - "dark_red", // DARK_RED - "dark_magenta", // DARK_MAGENTA - "dark_yellow", // DARK_YELLOW - "gray", // DARK_WHITE - "dark_gray", // BRIGHT_BLACK - "blue", // BRIGHT_BLUE - "green", // BRIGHT_GREEN - "cyan", // BRIGHT_CYAN - "red", // BRIGHT_RED - "magenta", // BRIGHT_MAGENTA - "yellow", // BRIGHT_YELLOW - "white" // BRIGHT_WHITE + private const string FileExtension = ".json"; + private static readonly IReadOnlyList ConcfgColorNames = new[] + { + "black", // Dark Black + "dark_blue", // Dark Blue + "dark_green", // Dark Green + "dark_cyan", // Dark Cyan + "dark_red", // Dark Red + "dark_magenta", // Dark Magenta + "dark_yellow", // Dark Yellow + "gray", // Dark White + "dark_gray", // Bright Black + "blue", // Bright Blue + "green", // Bright Green + "cyan", // Bright Cyan + "red", // Bright Red + "magenta", // Bright Magenta + "yellow", // Bright Yellow + "white" // Bright White }; - public string Name => "concfg Parser"; - - static uint ParseColor(string arg) - { - System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg); - return RGB(col.R, col.G, col.B); - } + public string Name { get; } = "concfg Parser"; - static XmlDocument loadJsonFile(string schemeName) - { - XmlDocument xmlDoc = new XmlDocument(); - foreach (string path in Scheme.GetSearchPaths(schemeName, ".json") - .Where(File.Exists)) - { - try - { - var data = File.ReadAllBytes(path); - var reader = JsonReaderWriterFactory.CreateJsonReader(data, System.Xml.XmlDictionaryReaderQuotas.Max); - xmlDoc.Load(reader); - return xmlDoc; - } - catch (XmlException /*e*/) { /* failed to parse */ } - catch (IOException /*e*/) { /* failed to find */ } - catch (UnauthorizedAccessException /*e*/) { /* unauthorized */ } - } - - return null; - } + public bool CanParse(string schemeName) => + string.Equals(Path.GetExtension(schemeName), FileExtension, StringComparison.OrdinalIgnoreCase); public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) { - XmlDocument xmlDoc = loadJsonFile(schemeName); + XmlDocument xmlDoc = LoadJsonFile(schemeName); if (xmlDoc == null) return null; try { XmlNode root = xmlDoc.DocumentElement; XmlNodeList children = root.ChildNodes; - uint[] colorTable = new uint[COLOR_TABLE_SIZE]; ; - for (int i = 0; i < COLOR_TABLE_SIZE; i++) + uint[] colorTable = new uint[ColorTableSize]; ; + for (int i = 0; i < ColorTableSize; i++) { - string name = CONCFG_COLOR_NAMES[i]; + string name = ConcfgColorNames[i]; var node = children.OfType().Where(n => n.Name == name).Single(); colorTable[i] = ParseColor(node.InnerText); } @@ -87,8 +68,8 @@ public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) var parts = popupNode.InnerText.Split(','); if (parts.Length == 2) { - var foregroundIndex = (CONCFG_COLOR_NAMES as IList).IndexOf(parts[0]); - var backgroundIndex = (CONCFG_COLOR_NAMES as IList).IndexOf(parts[1]); + var foregroundIndex = (ConcfgColorNames as IList).IndexOf(parts[0]); + var backgroundIndex = (ConcfgColorNames as IList).IndexOf(parts[1]); if (foregroundIndex != -1 && backgroundIndex != -1) { popupForeground = colorTable[foregroundIndex]; @@ -106,8 +87,8 @@ public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) var parts = screenNode.InnerText.Split(','); if (parts.Length == 2) { - var foregroundIndex = (CONCFG_COLOR_NAMES as IList).IndexOf(parts[0]); - var backgroundIndex = (CONCFG_COLOR_NAMES as IList).IndexOf(parts[1]); + var foregroundIndex = (ConcfgColorNames as IList).IndexOf(parts[0]); + var backgroundIndex = (ConcfgColorNames as IList).IndexOf(parts[1]); if (foregroundIndex != -1 && backgroundIndex != -1) { screenForeground = colorTable[foregroundIndex]; @@ -116,18 +97,45 @@ public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) } } - var consoleAttributes = new ConsoleAttributes { background = screenBackground, foreground = screenForeground, popupBackground = popupBackground, popupForeground = popupForeground }; - return new ColorScheme { colorTable = colorTable, consoleAttributes = consoleAttributes }; + var consoleAttributes = new ConsoleAttributes(screenBackground, screenForeground, popupBackground, popupForeground); + return new ColorScheme(colorTable, consoleAttributes); } catch (Exception /*e*/) { if (reportErrors) { - Console.WriteLine("failes to load json scheme"); + Console.WriteLine("failed to load json scheme"); } return null; } } + + private static uint ParseColor(string arg) + { + System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg); + return RGB(col.R, col.G, col.B); + } + + private static XmlDocument LoadJsonFile(string schemeName) + { + XmlDocument xmlDoc = new XmlDocument(); + foreach (string path in SchemeManager.GetSearchPaths(schemeName, FileExtension) + .Where(File.Exists)) + { + try + { + var data = File.ReadAllBytes(path); + var reader = JsonReaderWriterFactory.CreateJsonReader(data, System.Xml.XmlDictionaryReaderQuotas.Max); + xmlDoc.Load(reader); + return xmlDoc; + } + catch (XmlException /*e*/) { /* failed to parse */ } + catch (IOException /*e*/) { /* failed to find */ } + catch (UnauthorizedAccessException /*e*/) { /* unauthorized */ } + } + + return null; + } } } diff --git a/tools/ColorTool/ColorTool/XmlSchemeParser.cs b/tools/ColorTool/ColorTool/SchemeParsers/XmlSchemeParser.cs similarity index 54% rename from tools/ColorTool/ColorTool/XmlSchemeParser.cs rename to tools/ColorTool/ColorTool/SchemeParsers/XmlSchemeParser.cs index 615a399bb31..4747fd01570 100644 --- a/tools/ColorTool/ColorTool/XmlSchemeParser.cs +++ b/tools/ColorTool/ColorTool/SchemeParsers/XmlSchemeParser.cs @@ -1,47 +1,94 @@ // -// Copyright (C) Microsoft. All rights reserved. +// Copyright (C) Microsoft. All rights reserved. // Licensed under the terms described in the LICENSE file in the root of this project. // + using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Linq.Expressions; using System.Xml; using static ColorTool.ConsoleAPI; -namespace ColorTool +namespace ColorTool.SchemeParsers { class XmlSchemeParser : ISchemeParser { // In Windows Color Table order - static string[] PLIST_COLOR_NAMES = { - "Ansi 0 Color", // DARK_BLACK - "Ansi 4 Color", // DARK_BLUE - "Ansi 2 Color", // DARK_GREEN - "Ansi 6 Color", // DARK_CYAN - "Ansi 1 Color", // DARK_RED - "Ansi 5 Color", // DARK_MAGENTA - "Ansi 3 Color", // DARK_YELLOW - "Ansi 7 Color", // DARK_WHITE - "Ansi 8 Color", // BRIGHT_BLACK - "Ansi 12 Color", // BRIGHT_BLUE - "Ansi 10 Color", // BRIGHT_GREEN - "Ansi 14 Color", // BRIGHT_CYAN - "Ansi 9 Color", // BRIGHT_RED - "Ansi 13 Color", // BRIGHT_MAGENTA - "Ansi 11 Color", // BRIGHT_YELLOW - "Ansi 15 Color" // BRIGHT_WHITE + private static readonly string[] PListColorNames = + { + "Ansi 0 Color", // Dark Black + "Ansi 4 Color", // Dark Blue + "Ansi 2 Color", // Dark Green + "Ansi 6 Color", // Dark Cyan + "Ansi 1 Color", // Dark Red + "Ansi 5 Color", // Dark Magenta + "Ansi 3 Color", // Dark Yellow + "Ansi 7 Color", // Dark White + "Ansi 8 Color", // Bright Black + "Ansi 12 Color", // Bright Blue + "Ansi 10 Color", // Bright Green + "Ansi 14 Color", // Bright Cyan + "Ansi 9 Color", // Bright Red + "Ansi 13 Color", // Bright Magenta + "Ansi 11 Color", // Bright Yellow + "Ansi 15 Color" // Bright White }; - static string FG_KEY = "Foreground Color"; - static string BG_KEY = "Background Color"; - static string RED_KEY = "Red Component"; - static string GREEN_KEY = "Green Component"; - static string BLUE_KEY = "Blue Component"; - public string Name => "iTerm Parser"; + private const string ForegroundKey = "Foreground Color"; + private const string BackgroundKey = "Background Color"; + private const string RedKey = "Red Component"; + private const string GreenKey = "Green Component"; + private const string BlueKey = "Blue Component"; + private const string FileExtension = ".itermcolors"; + + public string Name { get; } = "iTerm Parser"; + + public bool CanParse(string schemeName) => + string.Equals(Path.GetExtension(schemeName), FileExtension, StringComparison.OrdinalIgnoreCase); + + public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) + { + XmlDocument xmlDoc = LoadXmlScheme(schemeName); // Create an XML document object + if (xmlDoc == null) return null; + XmlNode root = xmlDoc.GetElementsByTagName("dict")[0]; + XmlNodeList children = root.ChildNodes; + + uint[] colorTable = new uint[ColorTableSize]; + uint? fgColor = null, bgColor = null; + int colorsFound = 0; + bool success = false; + foreach (var tableEntry in children.OfType().Where(_ => _.Name == "key")) + { + uint rgb = 0; + int index = -1; + XmlNode components = tableEntry.NextSibling; + success = ParseRgbFromXml(components, ref rgb); + if (!success) { break; } + else if (tableEntry.InnerText == ForegroundKey) { fgColor = rgb; } + else if (tableEntry.InnerText == BackgroundKey) { bgColor = rgb; } + else if (-1 != (index = Array.IndexOf(PListColorNames, tableEntry.InnerText))) + { colorTable[index] = rgb; colorsFound++; } + } + if (colorsFound < ColorTableSize) + { + if (reportErrors) + { + Console.WriteLine(Resources.InvalidNumberOfColors); + } + success = false; + } + if (!success) + { + return null; + } + + var consoleAttributes = new ConsoleAttributes(bgColor, fgColor, null, null); + return new ColorScheme(colorTable, consoleAttributes); + } - static bool parseRgbFromXml(XmlNode components, ref uint rgb) + private static bool ParseRgbFromXml(XmlNode components, ref uint rgb) { int r = -1; int g = -1; @@ -51,15 +98,15 @@ static bool parseRgbFromXml(XmlNode components, ref uint rgb) { if (c.Name == "key") { - if (c.InnerText == RED_KEY) + if (c.InnerText == RedKey) { r = (int)(255 * Convert.ToDouble(c.NextSibling.InnerText, CultureInfo.InvariantCulture)); } - else if (c.InnerText == GREEN_KEY) + else if (c.InnerText == GreenKey) { g = (int)(255 * Convert.ToDouble(c.NextSibling.InnerText, CultureInfo.InvariantCulture)); } - else if (c.InnerText == BLUE_KEY) + else if (c.InnerText == BlueKey) { b = (int)(255 * Convert.ToDouble(c.NextSibling.InnerText, CultureInfo.InvariantCulture)); } @@ -78,11 +125,10 @@ static bool parseRgbFromXml(XmlNode components, ref uint rgb) return true; } - - static XmlDocument loadXmlScheme(string schemeName) + private static XmlDocument LoadXmlScheme(string schemeName) { XmlDocument xmlDoc = new XmlDocument(); // Create an XML document object - foreach (string path in Scheme.GetSearchPaths(schemeName, ".itermcolors") + foreach (string path in SchemeManager.GetSearchPaths(schemeName, FileExtension) .Where(File.Exists)) { try @@ -97,46 +143,5 @@ static XmlDocument loadXmlScheme(string schemeName) return null; } - - - public ColorScheme ParseScheme(string schemeName, bool reportErrors = false) - { - XmlDocument xmlDoc = loadXmlScheme(schemeName); // Create an XML document object - if (xmlDoc == null) return null; - XmlNode root = xmlDoc.GetElementsByTagName("dict")[0]; - XmlNodeList children = root.ChildNodes; - - uint[] colorTable = new uint[COLOR_TABLE_SIZE]; - uint? fgColor = null, bgColor = null; - int colorsFound = 0; - bool success = false; - foreach (var tableEntry in children.OfType().Where(_ => _.Name == "key")) - { - uint rgb = 0; - int index = -1; - XmlNode components = tableEntry.NextSibling; - success = parseRgbFromXml(components, ref rgb); - if (!success) { break; } - else if (tableEntry.InnerText == FG_KEY) { fgColor = rgb; } - else if (tableEntry.InnerText == BG_KEY) { bgColor = rgb; } - else if (-1 != (index = Array.IndexOf(PLIST_COLOR_NAMES, tableEntry.InnerText))) - { colorTable[index] = rgb; colorsFound++; } - } - if (colorsFound < COLOR_TABLE_SIZE) - { - if (reportErrors) - { - Console.WriteLine(Resources.InvalidNumberOfColors); - } - success = false; - } - if (!success) - { - return null; - } - - var consoleAttributes = new ConsoleAttributes { foreground = fgColor, background = bgColor }; - return new ColorScheme { colorTable = colorTable, consoleAttributes = consoleAttributes }; - } } } diff --git a/tools/ColorTool/ColorTool/SchemeWriters/IniSchemeWriter.cs b/tools/ColorTool/ColorTool/SchemeWriters/IniSchemeWriter.cs new file mode 100644 index 00000000000..e72a35fe0c5 --- /dev/null +++ b/tools/ColorTool/ColorTool/SchemeWriters/IniSchemeWriter.cs @@ -0,0 +1,66 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the terms described in the LICENSE file in the root of this project. +// + +using ColorTool.SchemeParsers; +using System; +using static ColorTool.ConsoleAPI; + +namespace ColorTool.SchemeWriters +{ + class IniSchemeWriter + { + public bool ExportCurrentAsIni(string outputPath) + { + CONSOLE_SCREEN_BUFFER_INFO_EX csbiex = CONSOLE_SCREEN_BUFFER_INFO_EX.Create(); + IntPtr hOut = GetStdOutputHandle(); + bool success = GetConsoleScreenBufferInfoEx(hOut, ref csbiex); + if (success) + { + try + { + // StreamWriter can fail for a variety of file system reasons so catch exceptions and print message. + using (System.IO.StreamWriter file = new System.IO.StreamWriter(outputPath)) + { + file.WriteLine("[table]"); + for (int i = 0; i < 16; i++) + { + string line = IniSchemeParser.ColorNames[i]; + line += " = "; + uint color = csbiex.ColorTable[i]; + uint r = color & (0x000000ff); + uint g = (color & (0x0000ff00)) >> 8; + uint b = (color & (0x00ff0000)) >> 16; + line += r + "," + g + "," + b; + file.WriteLine(line); + } + + file.WriteLine(); + file.WriteLine("[screen]"); + var forgroundIndex = csbiex.wAttributes & 0xF; + var backgroundIndex = csbiex.wAttributes >> 4; + file.WriteLine($"FOREGROUND = {IniSchemeParser.ColorNames[forgroundIndex]}"); + file.WriteLine($"BACKGROUND = {IniSchemeParser.ColorNames[backgroundIndex]}"); + + file.WriteLine(); + file.WriteLine("[popup]"); + forgroundIndex = csbiex.wPopupAttributes & 0xF; + backgroundIndex = csbiex.wPopupAttributes >> 4; + file.WriteLine($"FOREGROUND = {IniSchemeParser.ColorNames[forgroundIndex]}"); + file.WriteLine($"BACKGROUND = {IniSchemeParser.ColorNames[backgroundIndex]}"); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } + else + { + Console.WriteLine("Failed to get console information."); + } + return success; + } + } +}