diff --git a/TrueColorConsole/StyleRule.cs b/TrueColorConsole/StyleRule.cs new file mode 100644 index 0000000..6d6e276 --- /dev/null +++ b/TrueColorConsole/StyleRule.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text.RegularExpressions; + +namespace TrueColorConsole.Styled { + public interface iRule { + void ApplyRuleToSegments(List items); + } + public class FormatRule : RawRule { + public FormatRule(Regex regex, IEnumerable before, IEnumerable after, Func match_replace = null) + : base(regex, new Action[] { () => VTConsole.SetFormat(before.ToArray()) }, new Action[] { () => VTConsole.SetFormat(after.ToArray()) }, match_replace) { + } + } + public class RawRule : iRule { + public string only_use_capture_group_name;//if set only operate on this capture group rather than entire match + public RawRule(Regex regex, IEnumerable before, IEnumerable after, Func match_replace = null) { + this.regex = regex; + this.match_replace = match_replace; + this.before_cmds = before; + this.after_cmds = after; + } + public void ApplyRuleToSegments(List items) { + foreach (var item in items.ToArray()) { + var cur_item = item; + var matches = regex.Matches(cur_item.str); + foreach (var entire_match in matches.Cast().OrderByDescending(a => a.Index)) {//need to work back to front, do we? + if (!entire_match.Success) + continue; + var match = ! string.IsNullOrWhiteSpace(only_use_capture_group_name) ? entire_match.Groups[only_use_capture_group_name] : entire_match; + var pos = match.Index; + //So we want to allow other items affecting the segment( ie uderline bg color etc) to conintue through that we don't set + var before_segment = new SegmentItem { str = cur_item.str.Substring(0, pos), before = cur_item.before }; + var after_segment = new SegmentItem { str = cur_item.str.Substring(pos + match.Length), after = cur_item.after }; + //even if there is nothing before or after we want to add them for styling + items.Insert(items.IndexOf(cur_item) + 1, after_segment); + items.Insert(items.IndexOf(cur_item), before_segment); + + cur_item.str = cur_item.str.Substring(pos, match.Length); + + if (match_replace != null) + cur_item.str = match_replace(cur_item.str); + + cur_item.before = before_cmds; + cur_item.after = after_cmds; + cur_item = before_segment;//we work back to front + } + } + } + protected IEnumerable before_cmds; + protected IEnumerable after_cmds; + protected Func match_replace; + protected Regex regex; + } + public class StyleRule : RawRule { + public StyleRule(String regex, Color foreground_color, Func match_replace = null, Color? background_color = null) : this(new Regex(regex), foreground_color, match_replace, background_color) { + + } + protected static IEnumerable GetCommands(Color foreground_color, Color? background_color, bool need_after) { + var cmds = new List(); + if (foreground_color != Color.Empty && foreground_color != Color.Transparent) { + if (!need_after) + cmds.Add(() => VTConsole.SetColorForeground(foreground_color)); + else + cmds.Add(() => VTConsole.PopForegroundColor()); + } + if (background_color != null && background_color != Color.Empty && background_color != Color.Transparent) { + if (!need_after) + cmds.Add(() => VTConsole.SetColorBackground(background_color.Value)); + else + cmds.Add(() => VTConsole.PopBackgroundColor()); + } + + return cmds; + } + public StyleRule(Regex regex, Color foreground_color, Func match_replace = null, Color? background_color = null) : + base(regex, GetCommands(foreground_color, background_color, false), GetCommands(foreground_color, background_color, true), match_replace) { + + } + + + } + +} diff --git a/TrueColorConsole/StyleSheet.cs b/TrueColorConsole/StyleSheet.cs new file mode 100644 index 0000000..a3fb1fc --- /dev/null +++ b/TrueColorConsole/StyleSheet.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Drawing; +using System.Text; +using TrueColorConsole; + +namespace TrueColorConsole.Styled { + public class StyleSheet { + public StyleSheet() { } + public StyleSheet(Color default_color) { + this.default_color = default_color; + } + public Color default_color = Color.Empty; + public List rules = new List(); + public void AddStyle(iRule rule) => rules.Add(rule); + public IEnumerable ApplyRules(String str) { + var items = new List(); + var def = new SegmentItem {str=str }; + if (default_color != Color.Empty && default_color != Color.Transparent) { + def.before = new Action[] { () => VTConsole.SetColorForeground(default_color) }; + def.after = new Action[] { () => VTConsole.PopForegroundColor() }; + } + items.Add(def); + foreach (var rule in rules) + rule.ApplyRuleToSegments(items); + return items; + } + } + public class SegmentItem { + public string str; + public IEnumerable before; + public IEnumerable after; + } + +} diff --git a/TrueColorConsole/VTConsole.Styled.cs b/TrueColorConsole/VTConsole.Styled.cs new file mode 100644 index 0000000..0dee64b --- /dev/null +++ b/TrueColorConsole/VTConsole.Styled.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Drawing; +using System.Text; +using TrueColorConsole.Styled; +namespace TrueColorConsole { + partial class VTConsole { + public static void WriteStyled(String value, StyleSheet styleSheet) { + track_color_history = true; + var items = styleSheet.ApplyRules(value); + foreach (var item in items) { + if (item.before != null) + foreach (var action in item.before) + action(); + Console.Write(item.str); + if (item.after != null) + foreach (var action in item.after) + action(); + } + SetFormat(VTFormat.Default); + track_color_history = false; + foreground_color_history.Clear(); + background_color_history.Clear(); + } + public static void WriteLineStyled(string value, StyleSheet styleSheet) { + WriteStyled(value, styleSheet); + Console.WriteLine(); + } + private static ConcurrentStack foreground_color_history = new ConcurrentStack(); + private static ConcurrentStack background_color_history = new ConcurrentStack(); + private static bool track_color_history; + public static void PopForegroundColor() { + if (foreground_color_history.TryPop(out Color cur)) { + if (foreground_color_history.TryPop(out Color prev)) + SetColorForeground(prev);//will auto requeue it + else//set back to default color + SetFormat(VTFormat.ForegroundDefault); + + } + } + public static void PopBackgroundColor() { + if (background_color_history.TryPop(out Color cur)) { + if (background_color_history.TryPop(out Color prev)) + SetColorBackground(prev);//will auto requeue it + else//set back to default color + SetFormat(VTFormat.BackgroundDefault); + + } + } + + } +} diff --git a/TrueColorConsole/VTConsole.cs b/TrueColorConsole/VTConsole.cs index 75ff896..eb52507 100644 --- a/TrueColorConsole/VTConsole.cs +++ b/TrueColorConsole/VTConsole.cs @@ -14,20 +14,37 @@ public static partial class VTConsole { #region Interop - private const uint StdOutputHandle = unchecked((uint) -11); - private const uint StdInputHandle = unchecked((uint) -10); - private static readonly IntPtr InvalidHandleValue = new IntPtr(-1); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr GetStdHandle(uint nStdHandle); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); + private static class NativeMethods + { + public const uint StdOutputHandle = unchecked((uint) -11); + public const uint StdInputHandle = unchecked((uint) -10); + public static readonly IntPtr InvalidHandleValue = new IntPtr(-1); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr GetStdHandle(uint nStdHandle); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WriteConsole( + IntPtr hConsoleOutput, + [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer, + int lpNumberOfCharsToWrite, + out int lpNumberOfCharsToWritten, + IntPtr lpReserved + ); + } private static bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModeOutput mode) { - if (!GetConsoleMode(hConsoleHandle, out uint lpMode)) + if (!NativeMethods.GetConsoleMode(hConsoleHandle, out uint lpMode)) { mode = 0; return false; @@ -39,7 +56,7 @@ private static bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModeOutput private static bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModeInput mode) { - if (!GetConsoleMode(hConsoleHandle, out uint lpMode)) + if (!NativeMethods.GetConsoleMode(hConsoleHandle, out uint lpMode)) { mode = 0; return false; @@ -49,30 +66,16 @@ private static bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModeInput m return true; } - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool WriteConsole( - IntPtr hConsoleOutput, - [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer, - int lpNumberOfCharsToWrite, - out int lpNumberOfCharsToWritten, - IntPtr lpReserved - ); - private static bool GetStdIn(out IntPtr handle) { - handle = GetStdHandle(StdInputHandle); - return handle != InvalidHandleValue; + handle = NativeMethods.GetStdHandle(NativeMethods.StdInputHandle); + return handle != NativeMethods.InvalidHandleValue; } private static bool GetStdOut(out IntPtr handle) { - handle = GetStdHandle(StdOutputHandle); - return handle != InvalidHandleValue; + handle = NativeMethods.GetStdHandle(NativeMethods.StdOutputHandle); + return handle != NativeMethods.InvalidHandleValue; } #endregion @@ -91,12 +94,24 @@ private static bool GetStdOut(out IntPtr handle) private static ConsoleModeInput _inLast; private static IntPtr _outHandle; private static ConsoleModeOutput _outLast; + private static bool _isEnabled; + + private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); /// /// Gets if virtual terminal features are enabled. /// [PublicAPI] - public static bool IsEnabled { get; private set; } + public static bool IsEnabled + { + get + { + if (IsWindows) + return _isEnabled; + return true; + } + private set => _isEnabled = value; + } /// /// Gets if virtual terminal features are supported (see Remarks). @@ -147,7 +162,7 @@ bool EnableInput() var mode = _inLast | ConsoleModeInput.EnableVirtualTerminalInput; - return SetConsoleMode(StdIn, (uint) mode); + return NativeMethods.SetConsoleMode(StdIn, (uint) mode); } bool EnableOutput() @@ -163,12 +178,12 @@ bool EnableOutput() if (disableNewLineAutoReturn) mode |= ConsoleModeOutput.DisableNewlineAutoReturn; - if (SetConsoleMode(StdOut, (uint) mode)) + if (NativeMethods.SetConsoleMode(StdOut, (uint) mode)) return true; mode = _outLast | ConsoleModeOutput.EnableVirtualTerminalProcessing; - return SetConsoleMode(StdOut, (uint) mode); + return NativeMethods.SetConsoleMode(StdOut, (uint) mode); } IsEnabled = EnableInput() && EnableOutput(); @@ -185,17 +200,19 @@ bool EnableOutput() [PublicAPI] public static bool Disable() { + if (!IsWindows) + return true; if (!IsEnabled) return false; bool DisableInput() { - return GetStdIn(out var handle) && SetConsoleMode(handle, (uint) _inLast); + return GetStdIn(out var handle) && NativeMethods.SetConsoleMode(handle, (uint) _inLast); } bool DisableOutput() { - return GetStdOut(out var handle) && SetConsoleMode(handle, (uint) _outLast); + return GetStdOut(out var handle) && NativeMethods.SetConsoleMode(handle, (uint) _outLast); } IsEnabled = !(DisableInput() && DisableOutput()); @@ -244,6 +261,8 @@ public static string GetColorForegroundString(int r, int g, int b) public static void SetColorBackground(Color color) { Console.Write(GetColorBackgroundString(color.R, color.G, color.B)); + if (track_color_history) + background_color_history.Push(color); } /// @@ -256,6 +275,8 @@ public static void SetColorBackground(Color color) public static void SetColorForeground(Color color) { Console.Write(GetColorForegroundString(color.R, color.G, color.B)); + if (track_color_history) + foreground_color_history.Push(color); } /// @@ -284,7 +305,12 @@ public static void SetFormat(params VTFormat[] formats) [PublicAPI] public static int WriteFast(byte[] buffer) { - WriteConsole(StdOut, buffer, buffer.Length, out var written, IntPtr.Zero); + if (!IsWindows) + { + Console.Write(Console.OutputEncoding.GetString(buffer)); + return buffer.Length; + } + NativeMethods.WriteConsole(StdOut, buffer, buffer.Length, out var written, IntPtr.Zero); return written; } diff --git a/TrueColorConsoleApp/Program.cs b/TrueColorConsoleApp/Program.cs index e9f78bc..7c2f8e6 100644 --- a/TrueColorConsoleApp/Program.cs +++ b/TrueColorConsoleApp/Program.cs @@ -1,8 +1,11 @@ using System; using System.Drawing; +using System.Runtime.InteropServices; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using TrueColorConsole; +using TrueColorConsole.Styled; namespace TrueColorConsoleApp { @@ -10,16 +13,24 @@ internal static class Program { private static void Main(string[] args) { - if (!VTConsole.IsSupported) - throw new NotSupportedException(); + if (!VTConsole.IsSupported) + throw new NotSupportedException(); - VTConsole.Enable(); + VTConsole.Enable(); - Example3(); + Example4(); Console.ReadKey(); } - + private static void Example4() { + var st = new StyleSheet(Color.Orange); + st.AddStyle(new FormatRule(new Regex("hope.+SWEET"), new []{VTFormat.Underline }, new [] {VTFormat.NoUnderline })); + st.AddStyle(new StyleRule(new Regex("are .+"), Color.Transparent, null, Color.Red)); + st.AddStyle(new StyleRule(new Regex(@"Happy.+going"),Color.Lime,(s)=>"Zanay how BOW how howdy")); + st.AddStyle(new StyleRule(new Regex("how"), Color.Blue)); + VTConsole.WriteLineStyled("I hope you are Happy with how its going???? yah **SWEET**",st); + + } private static int Example1() { var width = 80; @@ -75,23 +86,31 @@ private static void Example2() private static void Example3() { var plasma = new Plasma(256, 256); - var width = 80; - var height = 40; - - Console.SetWindowSize(width, height); - Console.SetBufferSize(width, height); - Console.SetWindowSize(width, height); // removes bars Console.Title = "Plasma !"; Console.CursorVisible = false; + var width = Console.WindowWidth; + var height = Console.WindowHeight; var builder = new StringBuilder(width * height * 22); + var resetEvent = new AutoResetEvent(true); + using(new Timer(x =>{resetEvent.Set();}, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(1.0/20*1000))) for (var frame = 0; ; frame++) { + if (width != Console.WindowWidth || height != Console.WindowHeight) + { + width = Console.WindowWidth; + height = Console.WindowHeight; + Console.WriteLine(); + builder = new StringBuilder(width * height * 22); + } + else + { + builder.Clear(); + } plasma.PlasmaFrame(frame); - builder.Clear(); - Thread.Sleep((int)(1.0 / 20 * 1000)); + resetEvent.WaitOne(); for (var i = 0; i < width * height; i++) {