using System; using System.Runtime.InteropServices; namespace IPA.Logging.Printers { /// /// Prints a pretty message to the console. /// public class ColoredConsolePrinter : LogPrinter { private Logger.LogLevel filter = Logger.LogLevel.All; /// /// A filter for this specific printer. /// /// the filter to apply to this printer public override Logger.LogLevel Filter { get => filter; set => filter = value; } /// /// The color to print messages as. /// /// the color to print this message as // Initializer calls this function because Unity's .NET 3.5 doesn't have the color properties on Console public ConsoleColor Color { get; set; } = GetConsoleColor(WinConsole.OutHandle); /// /// Prints an entry to the console window. /// /// the of the message /// the the message was recorded at /// the name of the log that sent the message /// the message to print public override void Print(Logger.Level level, DateTime time, string logName, string message) { if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return; EnsureDefaultsPopulated(WinConsole.OutHandle); SetColor(Color, WinConsole.OutHandle); foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) WinConsole.ConOut.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper()); ResetColor(WinConsole.OutHandle); } private static bool _haveReadDefaultColors; private static short _defaultColors; private void EnsureDefaultsPopulated(IntPtr handle, bool force = false) { if (!_haveReadDefaultColors | force) { GetConsoleScreenBufferInfo(handle, out var info); _defaultColors = (short)(info.Attribute & ~15); _haveReadDefaultColors = true; } } private void ResetColor(IntPtr handle) { GetConsoleScreenBufferInfo(handle, out var info); var otherAttrs = (short)(info.Attribute & ~15); SetConsoleTextAttribute(handle, (short)(otherAttrs | _defaultColors)); } private void SetColor(ConsoleColor col, IntPtr handle) { GetConsoleScreenBufferInfo(handle, out var info); var attr = GetAttrForeground(info.Attribute, col); SetConsoleTextAttribute(handle, attr); } private static short GetAttrForeground(int attr, ConsoleColor color) { attr &= ~15; return (short)(attr | (int)color); } private static ConsoleColor GetConsoleColor(IntPtr handle) { GetConsoleScreenBufferInfo(handle, out var info); return (ConsoleColor)(info.Attribute & 15); } // ReSharper disable NotAccessedField.Local #pragma warning disable 649 private struct Coordinate { public short X; public short Y; } private struct SmallRect { public short Left; public short Top; public short Right; public short Bottom; } private struct ConsoleScreenBufferInfo { public Coordinate Size; public Coordinate CursorPosition; public short Attribute; public SmallRect Window; public Coordinate MaxWindowSize; } #pragma warning restore 649 // ReSharper restore NotAccessedField.Local [DllImport("kernel32.dll", EntryPoint = "GetConsoleScreenBufferInfo", SetLastError = true, CharSet = CharSet.Unicode)] private static extern bool GetConsoleScreenBufferInfo(IntPtr handle, out ConsoleScreenBufferInfo info); [DllImport("kernel32.dll", EntryPoint = "SetConsoleTextAttribute", SetLastError = true, CharSet = CharSet.Unicode)] private static extern bool SetConsoleTextAttribute(IntPtr handle, short attribute); } }