MCGalaxy/CLI/ConsoleDriver.cs
2024-08-31 13:31:16 +10:00

241 lines
8.3 KiB
C#

/*
Copyright 2015 MCGalaxy
Dual-licensed under the Educational Community License, Version 2.0 and
the GNU General Public License, Version 3 (the "Licenses"); you may
not use this file except in compliance with the Licenses. You may
obtain a copy of the Licenses at
https://opensource.org/license/ecl-2-0/
https://www.gnu.org/licenses/gpl-3.0.html
Unless required by applicable law or agreed to in writing,
software distributed under the Licenses are distributed on an "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the Licenses for the specific language governing
permissions and limitations under the Licenses.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using MCGalaxy.UI;
using MCGalaxy.Platform;
namespace MCGalaxy.Cli
{
public abstract class ConsoleDriver
{
public Action<ConsoleCancelEventArgs> OnSpecialInput;
public virtual void Init() {
Console.CancelKeyPress += OnCancelKeyPress;
}
void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e) {
OnSpecialInput(e);
}
public abstract void SetTitle(string title);
public abstract string ReadLine();
public abstract void WriteColored(string msg);
protected static ConsoleColor GetConsoleColor(char c) {
if (c == 'S') return ConsoleColor.White;
Colors.Map(ref c);
switch (c) {
case '0': return ConsoleColor.DarkGray; // black text on black background is unreadable
case '1': return ConsoleColor.DarkBlue;
case '2': return ConsoleColor.DarkGreen;
case '3': return ConsoleColor.DarkCyan;
case '4': return ConsoleColor.DarkRed;
case '5': return ConsoleColor.DarkMagenta;
case '6': return ConsoleColor.DarkYellow;
case '7': return ConsoleColor.Gray;
case '8': return ConsoleColor.DarkGray;
case '9': return ConsoleColor.Blue;
case 'a': return ConsoleColor.Green;
case 'b': return ConsoleColor.Cyan;
case 'c': return ConsoleColor.Red;
case 'd': return ConsoleColor.Magenta;
case 'e': return ConsoleColor.Yellow;
case 'f': return ConsoleColor.White;
default:
if (!Colors.IsDefined(c)) return ConsoleColor.White;
return GetConsoleColor(Colors.Get(c).Fallback);
}
}
public static ConsoleDriver Create() {
try {
IOperatingSystem os = IOperatingSystem.DetectOS();
if (!os.IsWindows) return new UnixConsoleDriver();
} catch { }
return new DefaultConsoleDriver();
}
}
sealed class DefaultConsoleDriver : ConsoleDriver
{
public override void SetTitle(string title) {
Console.Title = title;
}
public override string ReadLine() {
return Console.ReadLine();
}
public override void WriteColored(string message) {
int index = 0;
char col = 'S';
message = UIHelpers.Format(message);
while (index < message.Length)
{
char curCol = col;
string part = UIHelpers.OutputPart(ref col, ref index, message);
if (part.Length == 0) continue;
ConsoleColor color = GetConsoleColor(curCol);
if (color == ConsoleColor.White) {
// show in user's preferred console text color
Console.ResetColor();
} else {
Console.ForegroundColor = color;
}
Console.Write(part);
}
Console.ResetColor();
Console.WriteLine();
}
}
sealed class UnixConsoleDriver : ConsoleDriver
{
Encoding encoding;
bool ttyOutput;
const string ESC = "\x1b";
const string CSI = ESC + "[";
const string OSC = ESC + "]";
const string BEL = "\x7";
const string RESET = CSI + "0m";
public override void Init() {
base.Init();
encoding = new UTF8Encoding(false);
ttyOutput = IsTerminalOutput();
}
public override void SetTitle(string title) {
string msg = OSC + "0;" + title + BEL;
if (ttyOutput) WriteString(msg);
}
public override string ReadLine() {
List<byte> buffer = new List<byte>();
byte[] data = new byte[1];
for (;;)
{
int ret = read(0, data, 1);
if (ret == -1) return null;
if (data[0] == '\n') break;
buffer.Add(data[0]);
}
return encoding.GetString(buffer.ToArray());
}
public override void WriteColored(string message) {
StringBuilder sb = new StringBuilder(message.Length + 10);
int index = 0;
char col = 'S';
message = UIHelpers.Format(message);
while (index < message.Length)
{
char curCol = col;
string part = UIHelpers.OutputPart(ref col, ref index, message);
if (part.Length == 0) continue;
ConsoleColor color = GetConsoleColor(curCol);
if (!ttyOutput) {
// Don't add colours
} else if (color == ConsoleColor.White) {
// show in user's preferred console text color
sb.Append(RESET);
} else {
int code = MapColorCode(color);
sb.Append(CSI).Append(code).Append("m");
}
sb.Append(part);
}
if (ttyOutput) sb.Append(RESET);
sb.Append("\n");
WriteString(sb.ToString());
}
static int MapColorCode(ConsoleColor color) {
switch (color)
{
case ConsoleColor.Black: return 30;
case ConsoleColor.DarkRed: return 31;
case ConsoleColor.DarkGreen: return 32;
case ConsoleColor.DarkYellow: return 33;
case ConsoleColor.DarkBlue: return 34;
case ConsoleColor.DarkMagenta: return 35;
case ConsoleColor.DarkCyan: return 36;
case ConsoleColor.Gray: return 37;
case ConsoleColor.DarkGray: return 90;
case ConsoleColor.Red: return 91;
case ConsoleColor.Green: return 92;
case ConsoleColor.Yellow: return 93;
case ConsoleColor.Blue: return 94;
case ConsoleColor.Magenta: return 95;
case ConsoleColor.Cyan: return 96;
case ConsoleColor.White: return 97;
}
return 0; // reset all attributes
}
void WriteString(string msg) {
byte[] data = encoding.GetBytes(msg);
try { write(1, data, data.Length); } catch { }
}
bool IsTerminalOutput() {
try {
return isatty(1) != 0;
} catch { }
return true;
}
[DllImport("libc", SetLastError = true)]
static extern int isatty(int fd);
[DllImport("libc", SetLastError = true)]
static extern int read(int fd, byte[] data, int len);
[DllImport("libc", SetLastError = true)]
static extern int write(int fd, byte[] data, int len);
}
}