#nullable enable 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); private static ConsoleColor GetDarkenedColor(ConsoleColor color) => color switch { ConsoleColor.Gray => ConsoleColor.DarkGray, ConsoleColor.Blue => ConsoleColor.DarkBlue, ConsoleColor.Green => ConsoleColor.DarkGreen, ConsoleColor.Cyan => ConsoleColor.DarkCyan, ConsoleColor.Red => ConsoleColor.DarkRed, ConsoleColor.Magenta => ConsoleColor.DarkMagenta, ConsoleColor.Yellow => ConsoleColor.DarkYellow, ConsoleColor.White => ConsoleColor.Gray, _ => color, }; private readonly bool darkenSetManually; private readonly bool darkenMessages; public ColoredConsolePrinter() : this(Config.SelfConfig.Debug_.DarkenMessages_) { darkenSetManually = false; } public ColoredConsolePrinter(bool darkenMessages) { darkenSetManually = true; this.darkenMessages = darkenMessages; } /// /// 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); var prefixStr = ""; if ((darkenSetManually && darkenMessages) || (!darkenSetManually && Config.SelfConfig.Debug_.DarkenMessages_)) { prefixStr = StdoutInterceptor.ConsoleColorToForegroundSet(GetDarkenedColor(Color)); } foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) WinConsole.ConOut.WriteLine(Logger.LogFormat, prefixStr + line, logName, time, level.ToString().ToUpperInvariant()); ResetColor(WinConsole.OutHandle); } private static bool _haveReadDefaultColors; private static short _defaultColors; private static void EnsureDefaultsPopulated(IntPtr handle, bool force = false) { if (!_haveReadDefaultColors | force) { _ = GetConsoleScreenBufferInfo(handle, out var info); _defaultColors = (short)(info.Attribute & ~15); _haveReadDefaultColors = true; } } private static void ResetColor(IntPtr handle) { _ = GetConsoleScreenBufferInfo(handle, out var info); var otherAttrs = (short)(info.Attribute & ~15); _ = SetConsoleTextAttribute(handle, (short)(otherAttrs | _defaultColors)); } private static 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); } }