From 0c000e1254f6fce76e7d492cc6a35a5c9ca4a53f Mon Sep 17 00:00:00 2001 From: Alexei Shcherbakov Date: Tue, 20 Jun 2017 11:54:14 +0300 Subject: [PATCH] Windows 10 Anniversary Update Virtual Terminal 256 Color functionality --- src/Colorful.Console/Interop/NativeMethods.cs | 36 +++++++ .../OperationSystemDetector.cs | 98 +++++++++++++++++++ .../VirtualTerminalConsole.cs | 74 ++++++++++++++ .../VirtualTerminalSequences.cs | 63 ++++++++++++ src/ExampleConsoleApplication/Examples.cs | 15 ++- 5 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 src/Colorful.Console/Interop/NativeMethods.cs create mode 100644 src/Colorful.Console/OperationSystemDetector.cs create mode 100644 src/Colorful.Console/VirtualTerminalConsole.cs create mode 100644 src/Colorful.Console/VirtualTerminalSequences.cs diff --git a/src/Colorful.Console/Interop/NativeMethods.cs b/src/Colorful.Console/Interop/NativeMethods.cs new file mode 100644 index 0000000..0f1ce55 --- /dev/null +++ b/src/Colorful.Console/Interop/NativeMethods.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Colorful.Interop +{ + internal static class NativeMethods + { + private const string Kernel32 = "kernel32.dll"; + + #region kernel32.dll + [DllImport(Kernel32, EntryPoint = "GetVersion", SetLastError = true)] + internal static extern int GetVersion(); + [DllImport(Kernel32, EntryPoint = "SetConsoleMode", SetLastError = true)] + internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode); + [DllImport(Kernel32, EntryPoint = "GetConsoleMode", SetLastError = true)] + internal static extern bool GetConsoleMode(IntPtr handle, out int mode); + [DllImport(Kernel32, EntryPoint = "GetStdHandle", SetLastError = true)] + internal static extern IntPtr GetStdHandle(int handle); + #endregion + + private const string WinrtString = "api-ms-win-core-winrt-string-l1-1-0.dll"; + + #region api-ms-win-core-winrt-string-l1-1-0.dll + [DllImport(WinrtString, EntryPoint = "WindowsCreateString")] + internal static extern int WindowsCreateString([MarshalAs(UnmanagedType.LPWStr)]string sourceString, int stringLength, out IntPtr hstring); + [DllImport(WinrtString, EntryPoint = "WindowsDeleteString")] + internal static extern int WindowsDeleteString(IntPtr hstring); + #endregion + + [DllImport("api-ms-win-core-winrt-l1-1-0.dll", EntryPoint = "RoGetActivationFactory")] + internal static extern int RoGetActivationFactory(IntPtr className, ref Guid guid, out IntPtr instance); + } +} diff --git a/src/Colorful.Console/OperationSystemDetector.cs b/src/Colorful.Console/OperationSystemDetector.cs new file mode 100644 index 0000000..67bf768 --- /dev/null +++ b/src/Colorful.Console/OperationSystemDetector.cs @@ -0,0 +1,98 @@ +using Colorful.Interop; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace Colorful +{ + internal static class OperationSystemDetector + { + private static readonly bool _isAnniversaryUpdate; + + static OperationSystemDetector() + { + // OS Detection for all CLR implementations (.NET Framework,Mono,.NET Core) + var windir = Environment.GetEnvironmentVariable("windir"); + + if ((!string.IsNullOrWhiteSpace(windir)) && windir.Contains(@"\") && Directory.Exists(windir)) + { + // windows + int version=NativeMethods.GetVersion(); + int major = version & 0xFF; + int minor = (version >> 8) & 0xFF; + if ((major < 6) || (minor < 2)) + { + _isAnniversaryUpdate = false; + } + else + { + // Windows 6.2 is Windows 8 or later - first which supports Windows RT + _isAnniversaryUpdate = DetectAnniversaryUpdate(); + } + + } + else if (File.Exists("/proc/sys/kernel/ostype")) + { + string osType = File.ReadAllText(@"/proc/sys/kernel/ostype"); + if (osType.StartsWith("Linux", StringComparison.OrdinalIgnoreCase)) + { + // linux + } + } + else if (File.Exists(@"/System/Library/CoreServices/SystemVersion.plist")) + { + // Mac OS + } + + } + + public static bool IsAnniversaryUpdate + { + get { return _isAnniversaryUpdate; } + } + + /// + /// ALL version info functions are DEPRECATED since Windows 10 + /// https://msdn.microsoft.com/en-ca/library/windows/desktop/dn481241(v=vs.85).aspx + /// this code modified version of code: + /// http://answers.unity3d.com/questions/1249727/detect-if-windows-10-anniversary-version-number.html + /// + /// + private static bool DetectAnniversaryUpdate() + { + const string kAppExtensionClassName = "Windows.ApplicationModel.AppExtensions.AppExtensionCatalog"; + var classNameHString = IntPtr.Zero; + bool ok = false; + try + { + if (NativeMethods.WindowsCreateString(kAppExtensionClassName, kAppExtensionClassName.Length, out classNameHString) == 0) + { + IntPtr appExtensionCatalogStatics; + var IID_IAppExtensionCatalogStatics = new Guid(1010198154, 24344, 20235, 156, 229, 202, 182, 29, 25, 111, 17); + + if (NativeMethods.RoGetActivationFactory(classNameHString, ref IID_IAppExtensionCatalogStatics, out appExtensionCatalogStatics) == 0) + { + if (appExtensionCatalogStatics != IntPtr.Zero) + { + Marshal.Release(appExtensionCatalogStatics); + ok = true; + } + } + } + } + finally + { + if (IntPtr.Zero != classNameHString) + { + NativeMethods.WindowsDeleteString(classNameHString); + } + } + return ok; + } + + + + } +} diff --git a/src/Colorful.Console/VirtualTerminalConsole.cs b/src/Colorful.Console/VirtualTerminalConsole.cs new file mode 100644 index 0000000..0404df3 --- /dev/null +++ b/src/Colorful.Console/VirtualTerminalConsole.cs @@ -0,0 +1,74 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using Colorful.Interop; + +namespace Colorful +{ + /// + /// Virtual Terminal Helper class for Windows 10 after Anniversary Update + /// + /// + public static class VirtualTerminalConsole + { + private static readonly bool _isVirtualTerminalProcessingActive; + static VirtualTerminalConsole() + { + if (OperationSystemDetector.IsAnniversaryUpdate) + { + _isVirtualTerminalProcessingActive = EnableVirtualTerminalProcessing(); + } + } + + public static bool IsActive + { + get { return _isVirtualTerminalProcessingActive; } + } + + public static bool EnableVirtualTerminalProcessing() + { + var handle = NativeMethods.GetStdHandle(-11); + int mode; + NativeMethods.GetConsoleMode(handle, out mode); + NativeMethods.SetConsoleMode(handle, mode | 0x4); + NativeMethods.GetConsoleMode(handle, out mode); + return (mode & 0x4) == 0x4; + } + + public static void RestoreForeground() + { + System.Console.Write(VirtualTerminalSequences.RestoreForeground); + } + + public static void RestoreBackground() + { + System.Console.Write(VirtualTerminalSequences.RestoreBackground); + } + + public static void SetBold() + { + System.Console.Write(VirtualTerminalSequences.Bold); + } + + public static void AddUnderline() + { + System.Console.Write(VirtualTerminalSequences.Underline); + } + + public static void RemoveUnderline() + { + System.Console.Write(VirtualTerminalSequences.NoUnderline); + } + + public static void SetForegroundColor(Color color) + { + System.Console.Write(VirtualTerminalSequences.ForegroundRgb(color)); + } + + public static void SetBackgroundColor(Color color) + { + System.Console.Write(VirtualTerminalSequences.BackgroundRgb(color)); + } + } +} diff --git a/src/Colorful.Console/VirtualTerminalSequences.cs b/src/Colorful.Console/VirtualTerminalSequences.cs new file mode 100644 index 0000000..fa8a6ef --- /dev/null +++ b/src/Colorful.Console/VirtualTerminalSequences.cs @@ -0,0 +1,63 @@ +using System.Drawing; +using System.Text; + +namespace Colorful +{ + internal static class VirtualTerminalSequences + { + private const char Escape = '\x1b'; + + internal static readonly string RestoreForeground= SgrCommand(39); + internal static readonly string RestoreBackground = SgrCommand(49); + internal static readonly string Bold = SgrCommand(1); + internal static readonly string Underline = SgrCommand(4); + internal static readonly string NoUnderline = SgrCommand(24); + + + private static readonly string SgrStart = "\x1b["; + + private static StringBuilder SgrCommandBegin(int num) + { + StringBuilder ret = new StringBuilder(SgrStart, 20); + ret.Append(num); + return ret; + } + + private static string SgrCommand(int num) + { + var ret = SgrCommandBegin(num); + ret.Append('m'); + return ret.ToString(); + } + + private static void AppendRgb(StringBuilder builder, Color color) + { + builder.Append(';'); + builder.Append(color.R); + builder.Append(';'); + builder.Append(color.G); + builder.Append(';'); + builder.Append(color.B); + } + + private static string AdvancedColor(int num,Color color) + { + StringBuilder ret = SgrCommandBegin(num); + ret.Append(';'); + ret.Append(2); + AppendRgb(ret, color); + ret.Append('m'); + return ret.ToString(); + } + + public static string ForegroundRgb(Color color) + { + return AdvancedColor(38, color); + } + + public static string BackgroundRgb(Color color) + { + return AdvancedColor(48,color); + } + } +} diff --git a/src/ExampleConsoleApplication/Examples.cs b/src/ExampleConsoleApplication/Examples.cs index 978b525..538ad83 100644 --- a/src/ExampleConsoleApplication/Examples.cs +++ b/src/ExampleConsoleApplication/Examples.cs @@ -4,12 +4,12 @@ using System.Text; using System.Threading.Tasks; using System.Drawing; -using Colorful; using Console = Colorful.Console; using System.Reflection; using System.Resources; using System.Collections; using System.IO; +using Colorful; namespace TestConsole { @@ -20,6 +20,19 @@ static void Main(string[] args) // NOTE: Running all of the following examples at once will result in unexpected // coloring behavior, as more than 16 different colors are used! + for(int rr = 0; rr < 255; rr+=25) + { + for (int gg=0; gg < 255; gg+= 25) + { + for (int bb = 0; bb < 255; bb+= 25) + { + //VirtualTerminalConsole.SetForegroundColor(Color.Pink); + VirtualTerminalConsole.SetForegroundColor(Color.FromArgb(rr, gg, bb)); + Console.Write("*"); + } + } + } + Console.WriteLine("hi", Color.Pink); int r = 225; int g = 255;