using System; using System.IO; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityInjector.ConsoleUtil; namespace BepInEx.Unix; internal class LinuxConsoleDriver : IConsoleDriver { private static readonly ConfigEntry ForceCustomTtyDriverConfig = ConfigFile.CoreConfig.Bind( "Logging.Console", "ForceBepInExTTYDriver", false, "If enabled, forces to use custom BepInEx TTY driver for handling terminal output on unix."); static LinuxConsoleDriver() { UseMonoTtyDriver = false; if (ForceCustomTtyDriverConfig.Value) return; var consoleDriverType = typeof(Console).Assembly.GetType("System.ConsoleDriver"); if (consoleDriverType != null) UseMonoTtyDriver = typeof(Console).Assembly.GetType("System.ParameterizedStrings") != null; } public static bool UseMonoTtyDriver { get; } public bool StdoutRedirected { get; private set; } public TtyInfo TtyInfo { get; private set; } public TextWriter StandardOut { get; private set; } public TextWriter ConsoleOut { get; private set; } public bool ConsoleActive { get; private set; } public bool ConsoleIsExternal => false; public void PreventClose() { // Not supported by all distros } public void Initialize(bool alreadyActive, bool useManagedEncoder) { // Console is always considered active on Unix ConsoleActive = true; StdoutRedirected = UnixStreamHelper.isatty(1) != 1; var duplicateStream = UnixStreamHelper.CreateDuplicateStream(1); if (UseMonoTtyDriver && !StdoutRedirected) { // Mono implementation handles xterm for us var writer = ConsoleWriter.CreateConsoleStreamWriter(duplicateStream, Console.Out.Encoding, true); StandardOut = TextWriter.Synchronized(writer); var driver = AccessTools.Field(AccessTools.TypeByName("System.ConsoleDriver"), "driver").GetValue(null); AccessTools.Field(AccessTools.TypeByName("System.TermInfoDriver"), "stdout").SetValue(driver, writer); } else { // Handle TTY ourselves var writer = new StreamWriter(duplicateStream, Console.Out.Encoding); writer.AutoFlush = true; StandardOut = TextWriter.Synchronized(writer); TtyInfo = TtyHandler.GetTtyInfo(); } ConsoleOut = StandardOut; } public void CreateConsole(uint codepage) => Logger.Log(LogLevel.Warning, "An external console currently cannot be spawned on a Unix platform."); public void DetachConsole() => throw new PlatformNotSupportedException("Cannot detach console on a Unix platform"); public void SetConsoleColor(ConsoleColor color) { if (StdoutRedirected) return; if (UseMonoTtyDriver) // Use mono's inbuilt terminfo driver to set the foreground color for us SafeConsole.ForegroundColor = color; else ConsoleOut.Write(TtyInfo.GetAnsiCode(color)); } public void SetConsoleTitle(string title) { if (StdoutRedirected) return; if (UseMonoTtyDriver && SafeConsole.TitleExists) SafeConsole.Title = title; else ConsoleOut.Write($"\u001B]2;{title.Replace("\\", "\\\\")}\u0007"); } }