From b22c82aaa5b4adf0098b533df5e1ff81c7604aab Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sun, 7 May 2023 13:41:51 +0200 Subject: [PATCH 01/99] Added ClipRegion; cleaned up driver code --- Terminal.Gui/Application.cs | 1 - .../Configuration/ConfigurationManager.cs | 3 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 90 ++- .../CursesDriver/CursesDriver.cs | 347 ++++----- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 230 +++--- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 481 +++++++------ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 670 +++++++++--------- 7 files changed, 955 insertions(+), 867 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index bc42520541..249b0ba768 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -6,7 +6,6 @@ using System.Reflection; using System.IO; using System.Text.Json.Serialization; -using static Terminal.Gui.ConfigurationManager; namespace Terminal.Gui { /// diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs index 455697ffc0..50259813f6 100644 --- a/Terminal.Gui/Configuration/ConfigurationManager.cs +++ b/Terminal.Gui/Configuration/ConfigurationManager.cs @@ -1,4 +1,5 @@ -global using CM = Terminal.Gui.ConfigurationManager; +global using static Terminal.Gui.ConfigurationManager; +global using CM = Terminal.Gui.ConfigurationManager; using System; using System.Collections; diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 597e85c012..192b590a96 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -4,14 +4,11 @@ using NStack; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Text.Json.Serialization; using System.Threading.Tasks; -using Terminal.Gui; -using static Terminal.Gui.ConfigurationManager; namespace Terminal.Gui { /// @@ -347,13 +344,13 @@ public class ColorScheme : IEquatable { /// /// Creates a new instance. /// - public ColorScheme() { } + public ColorScheme () { } /// /// Creates a new instance, initialized with the values from . /// /// The scheme to initlize the new instance with. - public ColorScheme (ColorScheme scheme) : base() + public ColorScheme (ColorScheme scheme) : base () { if (scheme != null) { _normal = scheme.Normal; @@ -617,8 +614,8 @@ static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string s /// /// Provides the defined s. /// - [SerializableConfigurationProperty (Scope = typeof(ThemeScope), OmitClassName = true)] - [JsonConverter(typeof(DictionaryJsonConverter))] + [SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)] + [JsonConverter (typeof (DictionaryJsonConverter))] public static Dictionary ColorSchemes { get; private set; } } @@ -679,7 +676,7 @@ public enum CursorVisibility { /// Works under Xterm-like terminal otherwise this is equivalent to BoxFix = 0x02020164, } - + /// /// ConsoleDriver is an abstract class that defines the requirements for a console driver. /// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. @@ -774,14 +771,18 @@ public static Rune MakePrintable (Rune c) } /// - /// Ensures that the column and line are in a valid range from the size of the driver. + /// Tests whether the specified coordinate are valid for drawing. Returns + /// if the coordinate is outside of the screen bounds or outside either or + /// . /// /// The column. /// The row. - /// The clip. /// trueif it's a valid range,falseotherwise. - public bool IsValidContent (int col, int row, Rect clip) => - col >= 0 && row >= 0 && col < Cols && row < Rows && clip.Contains (col, row); + public bool IsValidLocation (int col, int row) => + col >= 0 && row >= 0 && + col < Cols && row < Rows && + Clip.Contains (col, row) && + IsInClipRegion (row, col); /// /// Adds the to the display at the cursor position. @@ -968,17 +969,72 @@ public enum DiagnosticFlags : uint { /// public abstract void Suspend (); - Rect clip; + /// + /// Gets or sets the clip rectangle that and are + /// subject to. Setting this property is equivalent to calling + /// and . + /// + /// The rectangle describing the bounds of . + public Rect Clip { + get { + if (ClipRegion.Count == 0) { + return new Rect (0, 0, Cols, Rows); + } + + int minX = ClipRegion.Min (rect => rect.X); + int minY = ClipRegion.Min (rect => rect.Y); + int maxX = ClipRegion.Max (rect => rect.X + rect.Width); + int maxY = ClipRegion.Max (rect => rect.Y + rect.Height); + + return new Rect (minX, minY, maxX - minX, maxY - minY); + } + set { + ClearClipRegion (); + AddToClipRegion (value); + } + } + + List _clipRegion = new List (); /// - /// Controls the current clipping region that AddRune/AddStr is subject to. + /// The clipping region that and are + /// subject to. /// /// The clip. - public Rect Clip { - get => clip; - set => this.clip = value; + public List ClipRegion { + get => _clipRegion; + set => _clipRegion = value; } + /// + /// Expands to include . + /// + /// + /// The updated . + public List AddToClipRegion (Rect rect) + { + ClipRegion.Add (rect); + return ClipRegion; + } + + /// + /// Clears the . This has the same effect as expanding the clip + /// region to include the entire screen. + /// + public void ClearClipRegion () + { + ClipRegion.Clear (); + } + + /// + /// Tests if the specified coordinates are within the . + /// + /// + /// + /// if and is + /// within the clip region. + private bool IsInClipRegion (int col, int row) => ClipRegion.Any (rect => rect.Contains (row, col)); + /// /// Start of mouse moves. /// diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 7a7b09402f..b311ff60da 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -3,7 +3,6 @@ // using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -21,46 +20,46 @@ internal class CursesDriver : ConsoleDriver { public override int Left => 0; public override int Top => 0; public override bool EnableConsoleScrolling { get; set; } - public override IClipboard Clipboard { get => clipboard; } + public override IClipboard Clipboard { get => _clipboard; } - CursorVisibility? initialCursorVisibility = null; - CursorVisibility? currentCursorVisibility = null; - IClipboard clipboard; - int [,,] contents; + CursorVisibility? _initialCursorVisibility = null; + CursorVisibility? _currentCursorVisibility = null; + IClipboard _clipboard; + int [,,] _contents; - public override int [,,] Contents => contents; + public override int [,,] Contents => _contents; // Current row, and current col, tracked by Move/AddRune only - int ccol, crow; - bool needMove; + int _ccol, _crow; + bool _needMove; public override void Move (int col, int row) { - ccol = col; - crow = row; + _ccol = col; + _crow = row; if (Clip.Contains (col, row)) { Curses.move (row, col); - needMove = false; + _needMove = false; } else { Curses.move (Clip.Y, Clip.X); - needMove = true; + _needMove = true; } } - static bool sync = false; + static bool _sync = false; public override void AddRune (Rune rune) { rune = MakePrintable (rune); var runeWidth = Rune.ColumnWidth (rune); - var validClip = IsValidContent (ccol, crow, Clip); + var validLocation = IsValidLocation (_ccol, _crow); - if (validClip) { - if (needMove) { - Curses.move (crow, ccol); - needMove = false; + if (validLocation) { + if (_needMove) { + Curses.move (_crow, _ccol); + _needMove = false; } - if (runeWidth == 0 && ccol > 0) { - var r = contents [crow, ccol - 1, 0]; + if (runeWidth == 0 && _ccol > 0) { + var r = _contents [_crow, _ccol - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); string sn; if (!s.IsNormalized ()) { @@ -69,60 +68,60 @@ public override void AddRune (Rune rune) sn = s; } var c = sn [0]; - Curses.mvaddch (crow, ccol - 1, (int)(uint)c); - contents [crow, ccol - 1, 0] = c; - contents [crow, ccol - 1, 1] = CurrentAttribute; - contents [crow, ccol - 1, 2] = 1; + Curses.mvaddch (_crow, _ccol - 1, (int)(uint)c); + _contents [_crow, _ccol - 1, 0] = c; + _contents [_crow, _ccol - 1, 1] = CurrentAttribute; + _contents [_crow, _ccol - 1, 2] = 1; } else { - if (runeWidth < 2 && ccol > 0 - && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + if (runeWidth < 2 && _ccol > 0 + && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { var curAtttib = CurrentAttribute; - Curses.attrset (contents [crow, ccol - 1, 1]); - Curses.mvaddch (crow, ccol - 1, (int)(uint)' '); - contents [crow, ccol - 1, 0] = (int)(uint)' '; - Curses.move (crow, ccol); + Curses.attrset (_contents [_crow, _ccol - 1, 1]); + Curses.mvaddch (_crow, _ccol - 1, (int)(uint)' '); + _contents [_crow, _ccol - 1, 0] = (int)(uint)' '; + Curses.move (_crow, _ccol); Curses.attrset (curAtttib); - } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 + && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { var curAtttib = CurrentAttribute; - Curses.attrset (contents [crow, ccol + 1, 1]); - Curses.mvaddch (crow, ccol + 1, (int)(uint)' '); - contents [crow, ccol + 1, 0] = (int)(uint)' '; - Curses.move (crow, ccol); + Curses.attrset (_contents [_crow, _ccol + 1, 1]); + Curses.mvaddch (_crow, _ccol + 1, (int)(uint)' '); + _contents [_crow, _ccol + 1, 0] = (int)(uint)' '; + Curses.move (_crow, _ccol); Curses.attrset (curAtttib); } - if (runeWidth > 1 && ccol == Clip.Right - 1) { + if (runeWidth > 1 && _ccol == Clip.Right - 1) { Curses.addch ((int)(uint)' '); - contents [crow, ccol, 0] = (int)(uint)' '; + _contents [_crow, _ccol, 0] = (int)(uint)' '; } else { Curses.addch ((int)(uint)rune); - contents [crow, ccol, 0] = (int)(uint)rune; + _contents [_crow, _ccol, 0] = (int)(uint)rune; } - contents [crow, ccol, 1] = CurrentAttribute; - contents [crow, ccol, 2] = 1; + _contents [_crow, _ccol, 1] = CurrentAttribute; + _contents [_crow, _ccol, 2] = 1; } } else { - needMove = true; + _needMove = true; } if (runeWidth < 0 || runeWidth > 0) { - ccol++; + _ccol++; } if (runeWidth > 1) { - if (validClip && ccol < Clip.Right) { - contents [crow, ccol, 1] = CurrentAttribute; - contents [crow, ccol, 2] = 0; + if (validLocation && _ccol < Clip.Right) { + _contents [_crow, _ccol, 1] = CurrentAttribute; + _contents [_crow, _ccol, 2] = 0; } - ccol++; + _ccol++; } - if (sync) { + if (_sync) { UpdateScreen (); } } @@ -130,8 +129,9 @@ public override void AddRune (Rune rune) public override void AddStr (ustring str) { // TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly - foreach (var rune in str) + foreach (var rune in str) { AddRune (rune); + } } public override void Refresh () @@ -161,7 +161,7 @@ public override void End () Curses.endwin (); } - public override void UpdateScreen () => window.redrawwin (); + public override void UpdateScreen () => _window.redrawwin (); public override void SetAttribute (Attribute c) { @@ -169,9 +169,7 @@ public override void SetAttribute (Attribute c) Curses.attrset (CurrentAttribute); } - public Curses.Window window; - - //static short last_color_pair = 16; + public Curses.Window _window; /// /// Creates a curses color from the provided foreground and background colors @@ -198,35 +196,37 @@ public override Attribute MakeColor (Color fore, Color back) return MakeColor ((short)MapColor (fore), (short)MapColor (back)); } - int [,] colorPairs = new int [16, 16]; + int [,] _colorPairs = new int [16, 16]; public override void SetColors (ConsoleColor foreground, ConsoleColor background) { // BUGBUG: This code is never called ?? See Issue #2300 int f = (short)foreground; int b = (short)background; - var v = colorPairs [f, b]; + var v = _colorPairs [f, b]; if ((v & 0x10000) == 0) { b &= 0x7; bool bold = (f & 0x8) != 0; f &= 0x7; v = MakeColor ((short)f, (short)b) | (bold ? Curses.A_BOLD : 0); - colorPairs [(int)foreground, (int)background] = v | 0x1000; + _colorPairs [(int)foreground, (int)background] = v | 0x1000; } SetAttribute (v & 0xffff); } - Dictionary rawPairs = new Dictionary (); + //Dictionary _rawPairs = new Dictionary (); public override void SetColors (short foreColorId, short backgroundColorId) { + throw new NotImplementedException (); + // BUGBUG: This code is never called ?? See Issue #2300 - int key = ((ushort)foreColorId << 16) | (ushort)backgroundColorId; - if (!rawPairs.TryGetValue (key, out var v)) { - v = MakeColor (foreColorId, backgroundColorId); - rawPairs [key] = v; - } - SetAttribute (v); + //int key = ((ushort)foreColorId << 16) | (ushort)backgroundColorId; + //if (!_rawPairs.TryGetValue (key, out var v)) { + // v = MakeColor (foreColorId, backgroundColorId); + // _rawPairs [key] = v; + //} + //SetAttribute (v); } static Key MapCursesKey (int cursesKey) @@ -305,21 +305,25 @@ static Key MapCursesKey (int cursesKey) } } - KeyModifiers keyModifiers; + KeyModifiers _keyModifiers; KeyModifiers MapKeyModifiers (Key key) { - if (keyModifiers == null) - keyModifiers = new KeyModifiers (); + if (_keyModifiers == null) { + _keyModifiers = new KeyModifiers (); + } - if (!keyModifiers.Shift && (key & Key.ShiftMask) != 0) - keyModifiers.Shift = true; - if (!keyModifiers.Alt && (key & Key.AltMask) != 0) - keyModifiers.Alt = true; - if (!keyModifiers.Ctrl && (key & Key.CtrlMask) != 0) - keyModifiers.Ctrl = true; + if (!_keyModifiers.Shift && (key & Key.ShiftMask) != 0) { + _keyModifiers.Shift = true; + } + if (!_keyModifiers.Alt && (key & Key.AltMask) != 0) { + _keyModifiers.Alt = true; + } + if (!_keyModifiers.Ctrl && (key & Key.CtrlMask) != 0) { + _keyModifiers.Ctrl = true; + } - return keyModifiers; + return _keyModifiers; } void ProcessInput () @@ -327,10 +331,11 @@ void ProcessInput () int wch; var code = Curses.get_wch (out wch); //System.Diagnostics.Debug.WriteLine ($"code: {code}; wch: {wch}"); - if (code == Curses.ERR) + if (code == Curses.ERR) { return; + } - keyModifiers = new KeyModifiers (); + _keyModifiers = new KeyModifiers (); Key k = Key.Null; if (code == Curses.KEY_CODE_YES) { @@ -369,9 +374,9 @@ void ProcessInput () wch -= 60; k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch); } - keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); - keyHandler (new KeyEvent (k, MapKeyModifiers (k))); - keyUpHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyUpHandler (new KeyEvent (k, MapKeyModifiers (k))); return; } @@ -407,31 +412,31 @@ void ProcessInput () } else { // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa. if (((Key)wch2 & Key.CtrlMask) != 0) { - keyModifiers.Ctrl = true; + _keyModifiers.Ctrl = true; } if (wch2 == 0) { k = Key.CtrlMask | Key.AltMask | Key.Space; } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) { - keyModifiers.Shift = true; - keyModifiers.Alt = true; + _keyModifiers.Shift = true; + _keyModifiers.Alt = true; } else if (wch2 < 256) { k = (Key)wch2; - keyModifiers.Alt = true; + _keyModifiers.Alt = true; } else { k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2); } } key = new KeyEvent (k, MapKeyModifiers (k)); - keyDownHandler (key); - keyHandler (key); + _keyDownHandler (key); + _keyHandler (key); } else { k = Key.Esc; - keyHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); } } else if (wch == Curses.KeyTab) { k = MapCursesKey (wch); - keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); - keyHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); } else { // Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa. k = (Key)wch; @@ -442,11 +447,11 @@ void ProcessInput () k = Key.CtrlMask | (Key)(wch + 64); } } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) { - keyModifiers.Shift = true; + _keyModifiers.Shift = true; } - keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); - keyHandler (new KeyEvent (k, MapKeyModifiers (k))); - keyUpHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyUpHandler (new KeyEvent (k, MapKeyModifiers (k))); } // Cause OnKeyUp and OnKeyPressed. Note that the special handling for ESC above // will not impact KeyUp. @@ -480,8 +485,8 @@ void GetEscSeq (ref int code, ref Key k, ref int wch2, ref KeyEvent key, ref Con k = ConsoleKeyMapping.MapConsoleKeyToKey (consoleKeyInfo.Key, out _); k = ConsoleKeyMapping.MapKeyModifiers (consoleKeyInfo, k); key = new KeyEvent (k, MapKeyModifiers (k)); - keyDownHandler (key); - keyHandler (key); + _keyDownHandler (key); + _keyHandler (key); } } else { cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki); @@ -496,7 +501,7 @@ void ProcessMouseEvent (MouseFlags mouseFlag, Point pos) X = pos.X, Y = pos.Y }; - mouseHandler (me); + _mouseHandler (me); } void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) @@ -504,19 +509,19 @@ void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) ProcessMouseEvent (mouseFlag, pos); } - Action keyHandler; - Action keyDownHandler; - Action keyUpHandler; - Action mouseHandler; + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { // Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called Curses.timeout (0); - this.keyHandler = keyHandler; - this.keyDownHandler = keyDownHandler; - this.keyUpHandler = keyUpHandler; - this.mouseHandler = mouseHandler; + this._keyHandler = keyHandler; + this._keyDownHandler = keyDownHandler; + this._keyUpHandler = keyUpHandler; + this._mouseHandler = mouseHandler; var mLoop = mainLoop.Driver as UnixMainLoop; @@ -532,11 +537,12 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle public override void Init (Action terminalResized) { - if (window != null) + if (_window != null) { return; + } try { - window = Curses.initscr (); + _window = Curses.initscr (); Curses.set_escdelay (10); } catch (Exception e) { throw new Exception ($"Curses failed to initialize, the exception is: {e.Message}"); @@ -550,31 +556,31 @@ public override void Init (Action terminalResized) // switch (Curses.curs_set (0)) { case 0: - currentCursorVisibility = initialCursorVisibility = CursorVisibility.Invisible; + _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Invisible; break; case 1: - currentCursorVisibility = initialCursorVisibility = CursorVisibility.Underline; + _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Underline; Curses.curs_set (1); break; case 2: - currentCursorVisibility = initialCursorVisibility = CursorVisibility.Box; + _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Box; Curses.curs_set (2); break; default: - currentCursorVisibility = initialCursorVisibility = null; + _currentCursorVisibility = _initialCursorVisibility = null; break; } if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - clipboard = new MacOSXClipboard (); + _clipboard = new MacOSXClipboard (); } else { if (Is_WSL_Platform ()) { - clipboard = new WSLClipboard (); + _clipboard = new WSLClipboard (); } else { - clipboard = new CursesClipboard (); + _clipboard = new CursesClipboard (); } } @@ -631,21 +637,21 @@ public override void Init (Action terminalResized) public override void ResizeScreen () { - Clip = new Rect (0, 0, Cols, Rows); + ClearClipRegion (); Curses.refresh (); } public override void UpdateOffScreen () { - contents = new int [Rows, Cols, 3]; + _contents = new int [Rows, Cols, 3]; for (int row = 0; row < Rows; row++) { for (int col = 0; col < Cols; col++) { //Curses.move (row, col); //Curses.attrset (Colors.TopLevel.Normal); //Curses.addch ((int)(uint)' '); - contents [row, col, 0] = ' '; - contents [row, col, 1] = Colors.TopLevel.Normal; - contents [row, col, 2] = 0; + _contents [row, col, 0] = ' '; + _contents [row, col, 1] = Colors.TopLevel.Normal; + _contents [row, col, 2] = 0; } } } @@ -791,10 +797,10 @@ public override bool GetCursorVisibility (out CursorVisibility visibility) { visibility = CursorVisibility.Invisible; - if (!currentCursorVisibility.HasValue) + if (!_currentCursorVisibility.HasValue) return false; - visibility = currentCursorVisibility.Value; + visibility = _currentCursorVisibility.Value; return true; } @@ -802,8 +808,9 @@ public override bool GetCursorVisibility (out CursorVisibility visibility) /// public override bool SetCursorVisibility (CursorVisibility visibility) { - if (initialCursorVisibility.HasValue == false) + if (_initialCursorVisibility.HasValue == false) { return false; + } Curses.curs_set (((int)visibility >> 16) & 0x000000FF); @@ -811,7 +818,7 @@ public override bool SetCursorVisibility (CursorVisibility visibility) Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF); } - currentCursorVisibility = visibility; + _currentCursorVisibility = visibility; return true; } @@ -861,9 +868,9 @@ public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, key |= Key.CtrlMask; km.Ctrl = control; } - keyDownHandler (new KeyEvent (key, km)); - keyHandler (new KeyEvent (key, km)); - keyUpHandler (new KeyEvent (key, km)); + _keyDownHandler (new KeyEvent (key, km)); + _keyHandler (new KeyEvent (key, km)); + _keyUpHandler (new KeyEvent (key, km)); } public override bool GetColors (int value, out Color foreground, out Color background) @@ -895,18 +902,19 @@ internal static class Platform { [DllImport ("libc")] static extern int killpg (int pgrp, int pid); - static int suspendSignal; + static int _suspendSignal; static int GetSuspendSignal () { - if (suspendSignal != 0) - return suspendSignal; + if (_suspendSignal != 0) { + return _suspendSignal; + } IntPtr buf = Marshal.AllocHGlobal (8192); if (uname (buf) != 0) { Marshal.FreeHGlobal (buf); - suspendSignal = -1; - return suspendSignal; + _suspendSignal = -1; + return _suspendSignal; } try { switch (Marshal.PtrToStringAnsi (buf)) { @@ -915,21 +923,21 @@ static int GetSuspendSignal () case "FreeBSD": case "NetBSD": case "OpenBSD": - suspendSignal = 18; + _suspendSignal = 18; break; case "Linux": // TODO: should fetch the machine name and // if it is MIPS return 24 - suspendSignal = 20; + _suspendSignal = 20; break; case "Solaris": - suspendSignal = 24; + _suspendSignal = 24; break; default: - suspendSignal = -1; + _suspendSignal = -1; break; } - return suspendSignal; + return _suspendSignal; } finally { Marshal.FreeHGlobal (buf); } @@ -942,8 +950,9 @@ static int GetSuspendSignal () static public bool Suspend () { int signal = GetSuspendSignal (); - if (signal == -1) + if (signal == -1) { return false; + } killpg (0, signal); return true; } @@ -962,7 +971,7 @@ public CursesClipboard () IsSupported = CheckSupport (); } - string xclipPath = string.Empty; + string _xclipPath = string.Empty; public override bool IsSupported { get; } bool CheckSupport () @@ -971,7 +980,7 @@ bool CheckSupport () try { var (exitCode, result) = ClipboardProcessRunner.Bash ("which xclip", waitForOutput: true); if (exitCode == 0 && result.FileExists ()) { - xclipPath = result; + _xclipPath = result; return true; } } catch (Exception) { @@ -987,7 +996,7 @@ protected override string GetClipboardDataImpl () var xclipargs = "-selection clipboard -o"; try { - var (exitCode, result) = ClipboardProcessRunner.Bash ($"{xclipPath} {xclipargs} > {tempFileName}", waitForOutput: false); + var (exitCode, result) = ClipboardProcessRunner.Bash ($"{_xclipPath} {xclipargs} > {tempFileName}", waitForOutput: false); if (exitCode == 0) { if (Application.Driver is CursesDriver) { Curses.raw (); @@ -996,7 +1005,7 @@ protected override string GetClipboardDataImpl () return System.IO.File.ReadAllText (tempFileName); } } catch (Exception e) { - throw new NotSupportedException ($"\"{xclipPath} {xclipargs}\" failed.", e); + throw new NotSupportedException ($"\"{_xclipPath} {xclipargs}\" failed.", e); } finally { System.IO.File.Delete (tempFileName); } @@ -1007,13 +1016,13 @@ protected override void SetClipboardDataImpl (string text) { var xclipargs = "-selection clipboard -i"; try { - var (exitCode, _) = ClipboardProcessRunner.Bash ($"{xclipPath} {xclipargs}", text, waitForOutput: false); + var (exitCode, _) = ClipboardProcessRunner.Bash ($"{_xclipPath} {xclipargs}", text, waitForOutput: false); if (exitCode == 0 && Application.Driver is CursesDriver) { Curses.raw (); Curses.noecho (); } } catch (Exception e) { - throw new NotSupportedException ($"\"{xclipPath} {xclipargs} < {text}\" failed", e); + throw new NotSupportedException ($"\"{_xclipPath} {xclipargs} < {text}\" failed", e); } } } @@ -1025,24 +1034,24 @@ protected override void SetClipboardDataImpl (string text) /// is used to determine if copy/paste is supported. /// class MacOSXClipboard : ClipboardBase { - IntPtr nsString = objc_getClass ("NSString"); - IntPtr nsPasteboard = objc_getClass ("NSPasteboard"); - IntPtr utfTextType; - IntPtr generalPasteboard; - IntPtr initWithUtf8Register = sel_registerName ("initWithUTF8String:"); - IntPtr allocRegister = sel_registerName ("alloc"); - IntPtr setStringRegister = sel_registerName ("setString:forType:"); - IntPtr stringForTypeRegister = sel_registerName ("stringForType:"); - IntPtr utf8Register = sel_registerName ("UTF8String"); - IntPtr nsStringPboardType; - IntPtr generalPasteboardRegister = sel_registerName ("generalPasteboard"); - IntPtr clearContentsRegister = sel_registerName ("clearContents"); + IntPtr _nsString = objc_getClass ("NSString"); + IntPtr _nsPasteboard = objc_getClass ("NSPasteboard"); + IntPtr _utfTextType; + IntPtr _generalPasteboard; + IntPtr _initWithUtf8Register = sel_registerName ("initWithUTF8String:"); + IntPtr _allocRegister = sel_registerName ("alloc"); + IntPtr _setStringRegister = sel_registerName ("setString:forType:"); + IntPtr _stringForTypeRegister = sel_registerName ("stringForType:"); + IntPtr _utf8Register = sel_registerName ("UTF8String"); + IntPtr _nsStringPboardType; + IntPtr _generalPasteboardRegister = sel_registerName ("generalPasteboard"); + IntPtr _clearContentsRegister = sel_registerName ("clearContents"); public MacOSXClipboard () { - utfTextType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "public.utf8-plain-text"); - nsStringPboardType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "NSStringPboardType"); - generalPasteboard = objc_msgSend (nsPasteboard, generalPasteboardRegister); + _utfTextType = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, "public.utf8-plain-text"); + _nsStringPboardType = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, "NSStringPboardType"); + _generalPasteboard = objc_msgSend (_nsPasteboard, _generalPasteboardRegister); IsSupported = CheckSupport (); } @@ -1060,8 +1069,8 @@ bool CheckSupport () protected override string GetClipboardDataImpl () { - var ptr = objc_msgSend (generalPasteboard, stringForTypeRegister, nsStringPboardType); - var charArray = objc_msgSend (ptr, utf8Register); + var ptr = objc_msgSend (_generalPasteboard, _stringForTypeRegister, _nsStringPboardType); + var charArray = objc_msgSend (ptr, _utf8Register); return Marshal.PtrToStringAnsi (charArray); } @@ -1069,9 +1078,9 @@ protected override void SetClipboardDataImpl (string text) { IntPtr str = default; try { - str = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, text); - objc_msgSend (generalPasteboard, clearContentsRegister); - objc_msgSend (generalPasteboard, setStringRegister, str, utfTextType); + str = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, text); + objc_msgSend (_generalPasteboard, _clearContentsRegister); + objc_msgSend (_generalPasteboard, _setStringRegister, str, _utfTextType); } finally { if (str != default) { objc_msgSend (str, sel_registerName ("release")); @@ -1105,23 +1114,21 @@ protected override void SetClipboardDataImpl (string text) /// clipboard. /// class WSLClipboard : ClipboardBase { - bool isSupported = false; public WSLClipboard () { - isSupported = CheckSupport (); } public override bool IsSupported { get { - return isSupported = CheckSupport (); + return CheckSupport (); } } - private static string powershellPath = string.Empty; + private static string _powershellPath = string.Empty; bool CheckSupport () { - if (string.IsNullOrEmpty (powershellPath)) { + if (string.IsNullOrEmpty (_powershellPath)) { // Specify pwsh.exe (not pwsh) to ensure we get the Windows version (invoked via WSL) var (exitCode, result) = ClipboardProcessRunner.Bash ("which pwsh.exe", waitForOutput: true); if (exitCode > 0) { @@ -1129,10 +1136,10 @@ bool CheckSupport () } if (exitCode == 0) { - powershellPath = result; + _powershellPath = result; } } - return !string.IsNullOrEmpty (powershellPath); + return !string.IsNullOrEmpty (_powershellPath); } protected override string GetClipboardDataImpl () @@ -1141,7 +1148,7 @@ protected override string GetClipboardDataImpl () return string.Empty; } - var (exitCode, output) = ClipboardProcessRunner.Process (powershellPath, "-noprofile -command \"Get-Clipboard\""); + var (exitCode, output) = ClipboardProcessRunner.Process (_powershellPath, "-noprofile -command \"Get-Clipboard\""); if (exitCode == 0) { if (Application.Driver is CursesDriver) { Curses.raw (); @@ -1162,7 +1169,7 @@ protected override void SetClipboardDataImpl (string text) return; } - var (exitCode, output) = ClipboardProcessRunner.Process (powershellPath, $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\""); + var (exitCode, output) = ClipboardProcessRunner.Process (_powershellPath, $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\""); if (exitCode == 0) { if (Application.Driver is CursesDriver) { Curses.raw (); diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index b71bc3f1de..813df155e0 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; -using System.Threading; using NStack; // Alias Console to MockConsole so we don't accidentally use Console @@ -39,24 +38,24 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN public static FakeDriver.Behaviors FakeBehaviors = new Behaviors (); - int cols, rows, left, top; - public override int Cols => cols; - public override int Rows => rows; + int _cols, _rows, _left, _top; + public override int Cols => _cols; + public override int Rows => _rows; // Only handling left here because not all terminals has a horizontal scroll bar. public override int Left => 0; public override int Top => 0; public override bool EnableConsoleScrolling { get; set; } - private IClipboard clipboard = null; - public override IClipboard Clipboard => clipboard; + private IClipboard _clipboard = null; + public override IClipboard Clipboard => _clipboard; // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - int [,,] contents; - bool [] dirtyLine; + int [,,] _contents; + bool [] _dirtyLine; /// /// Assists with testing, the format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag /// - public override int [,,] Contents => contents; + public override int [,,] Contents => _contents; //void UpdateOffscreen () //{ @@ -76,43 +75,43 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN // dirtyLine [row] = true; //} - static bool sync = false; + static bool _sync = false; public FakeDriver () { if (FakeBehaviors.UseFakeClipboard) { - clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse); + _clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse); } else { if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { - clipboard = new WindowsClipboard (); + _clipboard = new WindowsClipboard (); } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - clipboard = new MacOSXClipboard (); + _clipboard = new MacOSXClipboard (); } else { if (CursesDriver.Is_WSL_Platform ()) { - clipboard = new WSLClipboard (); + _clipboard = new WSLClipboard (); } else { - clipboard = new CursesClipboard (); + _clipboard = new CursesClipboard (); } } } } - bool needMove; + bool _needMove; // Current row, and current col, tracked by Move/AddCh only - int ccol, crow; + int _ccol, _crow; public override void Move (int col, int row) { - ccol = col; - crow = row; + _ccol = col; + _crow = row; if (Clip.Contains (col, row)) { FakeConsole.CursorTop = row; FakeConsole.CursorLeft = col; - needMove = false; + _needMove = false; } else { FakeConsole.CursorTop = Clip.Y; FakeConsole.CursorLeft = Clip.X; - needMove = true; + _needMove = true; } } @@ -120,16 +119,16 @@ public override void AddRune (Rune rune) { rune = MakePrintable (rune); var runeWidth = Rune.ColumnWidth (rune); - var validClip = IsValidContent (ccol, crow, Clip); + var validLocation = IsValidLocation (_ccol, _crow); - if (validClip) { - if (needMove) { + if (validLocation) { + if (_needMove) { //MockConsole.CursorLeft = ccol; //MockConsole.CursorTop = crow; - needMove = false; + _needMove = false; } - if (runeWidth == 0 && ccol > 0) { - var r = contents [crow, ccol - 1, 0]; + if (runeWidth == 0 && _ccol > 0) { + var r = _contents [_crow, _ccol - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); string sn; if (!s.IsNormalized ()) { @@ -138,47 +137,47 @@ public override void AddRune (Rune rune) sn = s; } var c = sn [0]; - contents [crow, ccol - 1, 0] = c; - contents [crow, ccol - 1, 1] = CurrentAttribute; - contents [crow, ccol - 1, 2] = 1; + _contents [_crow, _ccol - 1, 0] = c; + _contents [_crow, _ccol - 1, 1] = CurrentAttribute; + _contents [_crow, _ccol - 1, 2] = 1; } else { - if (runeWidth < 2 && ccol > 0 - && Rune.ColumnWidth ((Rune)contents [crow, ccol - 1, 0]) > 1) { + if (runeWidth < 2 && _ccol > 0 + && Rune.ColumnWidth ((Rune)_contents [_crow, _ccol - 1, 0]) > 1) { - contents [crow, ccol - 1, 0] = (int)(uint)' '; + _contents [_crow, _ccol - 1, 0] = (int)(uint)' '; - } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((Rune)contents [crow, ccol, 0]) > 1) { + } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 + && Rune.ColumnWidth ((Rune)_contents [_crow, _ccol, 0]) > 1) { - contents [crow, ccol + 1, 0] = (int)(uint)' '; - contents [crow, ccol + 1, 2] = 1; + _contents [_crow, _ccol + 1, 0] = (int)(uint)' '; + _contents [_crow, _ccol + 1, 2] = 1; } - if (runeWidth > 1 && ccol == Clip.Right - 1) { - contents [crow, ccol, 0] = (int)(uint)' '; + if (runeWidth > 1 && _ccol == Clip.Right - 1) { + _contents [_crow, _ccol, 0] = (int)(uint)' '; } else { - contents [crow, ccol, 0] = (int)(uint)rune; + _contents [_crow, _ccol, 0] = (int)(uint)rune; } - contents [crow, ccol, 1] = CurrentAttribute; - contents [crow, ccol, 2] = 1; + _contents [_crow, _ccol, 1] = CurrentAttribute; + _contents [_crow, _ccol, 2] = 1; - dirtyLine [crow] = true; + _dirtyLine [_crow] = true; } } else { - needMove = true; + _needMove = true; } if (runeWidth < 0 || runeWidth > 0) { - ccol++; + _ccol++; } if (runeWidth > 1) { - if (validClip && ccol < Clip.Right) { - contents [crow, ccol, 1] = CurrentAttribute; - contents [crow, ccol, 2] = 0; + if (validLocation && _ccol < Clip.Right) { + _contents [_crow, _ccol, 1] = CurrentAttribute; + _contents [_crow, _ccol, 2] = 0; } - ccol++; + _ccol++; } //if (ccol == Cols) { @@ -186,15 +185,16 @@ public override void AddRune (Rune rune) // if (crow + 1 < Rows) // crow++; //} - if (sync) { + if (_sync) { UpdateScreen (); } } public override void AddStr (ustring str) { - foreach (var rune in str) + foreach (var rune in str) { AddRune (rune); + } } public override void End () @@ -224,8 +224,8 @@ public override void Init (Action terminalResized) TerminalResized = terminalResized; - cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; - rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; + _cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; + _rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; FakeConsole.Clear (); ResizeScreen (); // Call InitalizeColorSchemes before UpdateOffScreen as it references Colors @@ -239,10 +239,10 @@ public override Attribute MakeAttribute (Color fore, Color back) return MakeColor ((ConsoleColor)fore, (ConsoleColor)back); } - int redrawColor = -1; + int _redrawColor = -1; void SetColor (int color) { - redrawColor = color; + _redrawColor = color; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) .OfType () .Select (s => (int)s); @@ -265,29 +265,31 @@ public override void UpdateScreen () var savedCol = FakeConsole.CursorLeft; var savedCursorVisible = FakeConsole.CursorVisible; for (int row = top; row < rows; row++) { - if (!dirtyLine [row]) + if (!_dirtyLine [row]) { continue; - dirtyLine [row] = false; + } + _dirtyLine [row] = false; for (int col = left; col < cols; col++) { FakeConsole.CursorTop = row; FakeConsole.CursorLeft = col; for (; col < cols; col++) { - if (contents [row, col, 2] == 0) { + if (_contents [row, col, 2] == 0) { FakeConsole.CursorLeft++; continue; } - var color = contents [row, col, 1]; - if (color != redrawColor) + var color = _contents [row, col, 1]; + if (color != _redrawColor) { SetColor (color); + } - Rune rune = contents [row, col, 0]; + Rune rune = _contents [row, col, 0]; if (Rune.DecodeSurrogatePair (rune, out char [] spair)) { FakeConsole.Write (spair); } else { FakeConsole.Write ((char)rune); } - contents [row, col, 2] = 0; + _contents [row, col, 2] = 0; } } } @@ -374,8 +376,9 @@ Key MapKey (ConsoleKeyInfo keyInfo) case ConsoleKey.OemComma: case ConsoleKey.OemPlus: case ConsoleKey.OemMinus: - if (keyInfo.KeyChar == 0) + if (keyInfo.KeyChar == 0) { return Key.Unknown; + } return (Key)((uint)keyInfo.KeyChar); } @@ -439,26 +442,29 @@ Key MapKey (ConsoleKeyInfo keyInfo) private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) { Key keyMod = new Key (); - if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { keyMod = Key.ShiftMask; - if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) + } + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { keyMod |= Key.CtrlMask; - if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) + } + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { keyMod |= Key.AltMask; + } return keyMod != Key.Null ? keyMod | key : key; } - Action keyDownHandler; - Action keyHandler; - Action keyUpHandler; - private CursorVisibility savedCursorVisibility; + Action _keyDownHandler; + Action _keyHandler; + Action _keyUpHandler; + private CursorVisibility _savedCursorVisibility; public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { - this.keyDownHandler = keyDownHandler; - this.keyHandler = keyHandler; - this.keyUpHandler = keyUpHandler; + _keyDownHandler = keyDownHandler; + _keyHandler = keyHandler; + _keyUpHandler = keyUpHandler; // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called (mainLoop.Driver as FakeMainLoop).KeyPressed += (consoleKey) => ProcessInput (consoleKey); @@ -482,15 +488,15 @@ void ProcessInput (ConsoleKeyInfo consoleKey) var map = MapKey (consoleKey); if (map == (Key)0xffffffff) { if ((consoleKey.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - keyDownHandler (new KeyEvent (map, keyModifiers)); - keyUpHandler (new KeyEvent (map, keyModifiers)); + _keyDownHandler (new KeyEvent (map, keyModifiers)); + _keyUpHandler (new KeyEvent (map, keyModifiers)); } return; } - keyDownHandler (new KeyEvent (map, keyModifiers)); - keyHandler (new KeyEvent (map, keyModifiers)); - keyUpHandler (new KeyEvent (map, keyModifiers)); + _keyDownHandler (new KeyEvent (map, keyModifiers)); + _keyHandler (new KeyEvent (map, keyModifiers)); + _keyUpHandler (new KeyEvent (map, keyModifiers)); } /// @@ -506,21 +512,21 @@ public override bool GetCursorVisibility (out CursorVisibility visibility) /// public override bool SetCursorVisibility (CursorVisibility visibility) { - savedCursorVisibility = visibility; + _savedCursorVisibility = visibility; return FakeConsole.CursorVisible = visibility == CursorVisibility.Default; } /// public override bool EnsureCursorVisibility () { - if (!(ccol >= 0 && crow >= 0 && ccol < Cols && crow < Rows)) { + if (!(_ccol >= 0 && _crow >= 0 && _ccol < Cols && _crow < Rows)) { GetCursorVisibility (out CursorVisibility cursorVisibility); - savedCursorVisibility = cursorVisibility; + _savedCursorVisibility = cursorVisibility; SetCursorVisibility (CursorVisibility.Invisible); return false; } - SetCursorVisibility (savedCursorVisibility); + SetCursorVisibility (_savedCursorVisibility); return FakeConsole.CursorVisible; } @@ -532,8 +538,8 @@ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool al public void SetBufferSize (int width, int height) { FakeConsole.SetBufferSize (width, height); - cols = width; - rows = height; + _cols = width; + _rows = height; if (!EnableConsoleScrolling) { SetWindowSize (width, height); } @@ -544,10 +550,10 @@ public void SetWindowSize (int width, int height) { FakeConsole.SetWindowSize (width, height); if (!EnableConsoleScrolling) { - if (width != cols || height != rows) { + if (width != _cols || height != _rows) { SetBufferSize (width, height); - cols = width; - rows = height; + _cols = width; + _rows = height; } } ProcessResize (); @@ -556,13 +562,13 @@ public void SetWindowSize (int width, int height) public void SetWindowPosition (int left, int top) { if (EnableConsoleScrolling) { - this.left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); - this.top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); - } else if (this.left > 0 || this.top > 0) { - this.left = 0; - this.top = 0; + _left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); + _top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); + } else if (_left > 0 || _top > 0) { + _left = 0; + _top = 0; } - FakeConsole.SetWindowPosition (this.left, this.top); + FakeConsole.SetWindowPosition (_left, _top); } void ProcessResize () @@ -593,30 +599,30 @@ public override void ResizeScreen () } else { try { #pragma warning disable CA1416 - FakeConsole.WindowLeft = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); - FakeConsole.WindowTop = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); + FakeConsole.WindowLeft = Math.Max (Math.Min (_left, Cols - FakeConsole.WindowWidth), 0); + FakeConsole.WindowTop = Math.Max (Math.Min (_top, Rows - FakeConsole.WindowHeight), 0); #pragma warning restore CA1416 } catch (Exception) { return; } } - Clip = new Rect (0, 0, Cols, Rows); + ClearClipRegion (); } public override void UpdateOffScreen () { - contents = new int [Rows, Cols, 3]; - dirtyLine = new bool [Rows]; + _contents = new int [Rows, Cols, 3]; + _dirtyLine = new bool [Rows]; // Can raise an exception while is still resizing. try { - for (int row = 0; row < rows; row++) { - for (int c = 0; c < cols; c++) { - contents [row, c, 0] = ' '; - contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; - contents [row, c, 2] = 0; - dirtyLine [row] = true; + for (int row = 0; row < _rows; row++) { + for (int c = 0; c < _cols; c++) { + _contents [row, c, 0] = ' '; + _contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; + _contents [row, c, 2] = 0; + _dirtyLine [row] = true; } } } catch (IndexOutOfRangeException) { } @@ -649,8 +655,8 @@ public override void UpdateCursor () // Prevents the exception of size changing during resizing. try { - if (ccol >= 0 && ccol < FakeConsole.BufferWidth && crow >= 0 && crow < FakeConsole.BufferHeight) { - FakeConsole.SetCursorPosition (ccol, crow); + if (_ccol >= 0 && _ccol < FakeConsole.BufferWidth && _crow >= 0 && _crow < FakeConsole.BufferHeight) { + FakeConsole.SetCursorPosition (_ccol, _crow); } } catch (System.IO.IOException) { } catch (ArgumentOutOfRangeException) { @@ -691,15 +697,15 @@ public override void UncookMouse () public class FakeClipboard : ClipboardBase { public Exception FakeException = null; - string contents = string.Empty; + string _contents = string.Empty; - bool isSupportedAlwaysFalse = false; + bool _isSupportedAlwaysFalse = false; - public override bool IsSupported => !isSupportedAlwaysFalse; + public override bool IsSupported => !_isSupportedAlwaysFalse; public FakeClipboard (bool fakeClipboardThrowsNotSupportedException = false, bool isSupportedAlwaysFalse = false) { - this.isSupportedAlwaysFalse = isSupportedAlwaysFalse; + _isSupportedAlwaysFalse = isSupportedAlwaysFalse; if (fakeClipboardThrowsNotSupportedException) { FakeException = new NotSupportedException ("Fake clipboard exception"); } @@ -710,7 +716,7 @@ protected override string GetClipboardDataImpl () if (FakeException != null) { throw FakeException; } - return contents; + return _contents; } protected override void SetClipboardDataImpl (string text) @@ -718,7 +724,7 @@ protected override void SetClipboardDataImpl (string text) if (FakeException != null) { throw FakeException; } - contents = text; + _contents = text; } } diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 1617cd1889..b0ce4a0f4c 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -15,43 +15,43 @@ namespace Terminal.Gui { internal class NetWinVTConsole { - IntPtr InputHandle, OutputHandle, ErrorHandle; - uint originalInputConsoleMode, originalOutputConsoleMode, originalErrorConsoleMode; + IntPtr _inputHandle, _outputHandle, _errorHandle; + uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode; public NetWinVTConsole () { - InputHandle = GetStdHandle (STD_INPUT_HANDLE); - if (!GetConsoleMode (InputHandle, out uint mode)) { + _inputHandle = GetStdHandle (STD_INPUT_HANDLE); + if (!GetConsoleMode (_inputHandle, out uint mode)) { throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}."); } - originalInputConsoleMode = mode; + _originalInputConsoleMode = mode; if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) { mode |= ENABLE_VIRTUAL_TERMINAL_INPUT; - if (!SetConsoleMode (InputHandle, mode)) { + if (!SetConsoleMode (_inputHandle, mode)) { throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}."); } } - OutputHandle = GetStdHandle (STD_OUTPUT_HANDLE); - if (!GetConsoleMode (OutputHandle, out mode)) { + _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); + if (!GetConsoleMode (_outputHandle, out mode)) { throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}."); } - originalOutputConsoleMode = mode; + _originalOutputConsoleMode = mode; if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; - if (!SetConsoleMode (OutputHandle, mode)) { + if (!SetConsoleMode (_outputHandle, mode)) { throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}."); } } - ErrorHandle = GetStdHandle (STD_ERROR_HANDLE); - if (!GetConsoleMode (ErrorHandle, out mode)) { + _errorHandle = GetStdHandle (STD_ERROR_HANDLE); + if (!GetConsoleMode (_errorHandle, out mode)) { throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}."); } - originalErrorConsoleMode = mode; + _originalErrorConsoleMode = mode; if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { mode |= DISABLE_NEWLINE_AUTO_RETURN; - if (!SetConsoleMode (ErrorHandle, mode)) { + if (!SetConsoleMode (_errorHandle, mode)) { throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}."); } } @@ -59,13 +59,13 @@ public NetWinVTConsole () public void Cleanup () { - if (!SetConsoleMode (InputHandle, originalInputConsoleMode)) { + if (!SetConsoleMode (_inputHandle, _originalInputConsoleMode)) { throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}."); } - if (!SetConsoleMode (OutputHandle, originalOutputConsoleMode)) { + if (!SetConsoleMode (_outputHandle, _originalOutputConsoleMode)) { throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}."); } - if (!SetConsoleMode (ErrorHandle, originalErrorConsoleMode)) { + if (!SetConsoleMode (_errorHandle, _originalErrorConsoleMode)) { throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}."); } } @@ -106,17 +106,17 @@ public void Cleanup () } internal class NetEvents { - ManualResetEventSlim inputReady = new ManualResetEventSlim (false); - ManualResetEventSlim waitForStart = new ManualResetEventSlim (false); - ManualResetEventSlim winChange = new ManualResetEventSlim (false); - Queue inputResultQueue = new Queue (); - ConsoleDriver consoleDriver; - volatile ConsoleKeyInfo [] cki = null; - static volatile bool isEscSeq; - int lastWindowHeight; - bool stopTasks; + ManualResetEventSlim _inputReady = new ManualResetEventSlim (false); + ManualResetEventSlim _waitForStart = new ManualResetEventSlim (false); + ManualResetEventSlim _winChange = new ManualResetEventSlim (false); + Queue _inputResultQueue = new Queue (); + ConsoleDriver _consoleDriver; + volatile ConsoleKeyInfo [] _cki = null; + static volatile bool _isEscSeq; + int _lastWindowHeight; + bool _stopTasks; #if PROCESS_REQUEST - bool neededProcessRequest; + bool _neededProcessRequest; #endif public bool IsTerminalWithOptions { get; set; } public EscSeqReqProc EscSeqReqProc { get; } = new EscSeqReqProc (); @@ -126,34 +126,34 @@ public NetEvents (ConsoleDriver consoleDriver) if (consoleDriver == null) { throw new ArgumentNullException ("Console driver instance must be provided."); } - this.consoleDriver = consoleDriver; + _consoleDriver = consoleDriver; Task.Run (ProcessInputResultQueue); Task.Run (CheckWinChange); } internal void StopTasks () { - stopTasks = true; + _stopTasks = true; } public InputResult? ReadConsoleInput () { while (true) { - if (stopTasks) { + if (_stopTasks) { return null; } - waitForStart.Set (); - winChange.Set (); + _waitForStart.Set (); + _winChange.Set (); - if (inputResultQueue.Count == 0) { - inputReady.Wait (); - inputReady.Reset (); + if (_inputResultQueue.Count == 0) { + _inputReady.Wait (); + _inputReady.Reset (); } #if PROCESS_REQUEST - neededProcessRequest = false; + _neededProcessRequest = false; #endif - if (inputResultQueue.Count > 0) { - return inputResultQueue.Dequeue (); + if (_inputResultQueue.Count > 0) { + return _inputResultQueue.Dequeue (); } } } @@ -161,14 +161,14 @@ internal void StopTasks () void ProcessInputResultQueue () { while (true) { - waitForStart.Wait (); - waitForStart.Reset (); + _waitForStart.Wait (); + _waitForStart.Reset (); - if (inputResultQueue.Count == 0) { + if (_inputResultQueue.Count == 0) { GetConsoleKey (); } - inputReady.Set (); + _inputReady.Set (); } } @@ -180,24 +180,24 @@ void GetConsoleKey () while (true) { ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true); - if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !isEscSeq) - || (consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq)) { - if (cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq) { - cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, - false, false, false), cki); + if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq) + || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) { + if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) { + _cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, + false, false, false), _cki); } - isEscSeq = true; + _isEscSeq = true; newConsoleKeyInfo = consoleKeyInfo; - cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki); + _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki); if (!Console.KeyAvailable) { - DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod); - cki = null; - isEscSeq = false; + DecodeEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; + _isEscSeq = false; break; } - } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && isEscSeq) { - DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod); - cki = null; + } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq) { + DecodeEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; break; } else { GetConsoleInputType (consoleKeyInfo); @@ -209,13 +209,13 @@ void GetConsoleKey () void CheckWinChange () { while (true) { - if (stopTasks) { + if (_stopTasks) { return; } - winChange.Wait (); - winChange.Reset (); + _winChange.Wait (); + _winChange.Reset (); WaitWinChange (); - inputReady.Set (); + _inputReady.Set (); } } @@ -224,18 +224,18 @@ void WaitWinChange () while (true) { // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. Thread.Sleep (10); - if (stopTasks) { + if (_stopTasks) { return; } switch (IsTerminalWithOptions) { case false: int buffHeight, buffWidth; - if (((NetDriver)consoleDriver).IsWinPlatform) { + if (((NetDriver)_consoleDriver).IsWinPlatform) { buffHeight = Math.Max (Console.BufferHeight, 0); buffWidth = Math.Max (Console.BufferWidth, 0); } else { - buffHeight = consoleDriver.Rows; - buffWidth = consoleDriver.Cols; + buffHeight = _consoleDriver.Rows; + buffWidth = _consoleDriver.Cols; } if (IsWinChanged ( Math.Max (Console.WindowHeight, 0), @@ -257,19 +257,19 @@ void WaitWinChange () bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth) { - if (!consoleDriver.EnableConsoleScrolling) { - if (winWidth != consoleDriver.Cols || winHeight != consoleDriver.Rows) { + if (!_consoleDriver.EnableConsoleScrolling) { + if (winWidth != _consoleDriver.Cols || winHeight != _consoleDriver.Rows) { var w = Math.Max (winWidth, 0); var h = Math.Max (winHeight, 0); GetWindowSizeEvent (new Size (w, h)); return true; } } else { - if (winWidth != consoleDriver.Cols || winHeight != lastWindowHeight - || buffWidth != consoleDriver.Cols || buffHeight != consoleDriver.Rows) { + if (winWidth != _consoleDriver.Cols || winHeight != _lastWindowHeight + || buffWidth != _consoleDriver.Cols || buffHeight != _consoleDriver.Rows) { - lastWindowHeight = Math.Max (winHeight, 0); - GetWindowSizeEvent (new Size (winWidth, lastWindowHeight)); + _lastWindowHeight = Math.Max (winHeight, 0); + GetWindowSizeEvent (new Size (winWidth, _lastWindowHeight)); return true; } } @@ -282,7 +282,7 @@ void GetWindowSizeEvent (Size size) Size = size }; - inputResultQueue.Enqueue (new InputResult () { + _inputResultQueue.Enqueue (new InputResult () { EventType = EventType.WindowSize, WindowSizeEvent = windowSizeEvent }); @@ -301,7 +301,7 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) inputResult.MouseEvent = mouseEvent; } - inputResultQueue.Enqueue (inputResult); + _inputResultQueue.Enqueue (inputResult); } void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) @@ -329,7 +329,7 @@ void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, Con ConsoleKeyInfo = newConsoleKeyInfo }; - inputResultQueue.Enqueue (inputResult); + _inputResultQueue.Enqueue (inputResult); } void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) @@ -436,7 +436,7 @@ MouseButtonState MapMouseFlags (MouseFlags mouseFlags) return mbs; } - Point lastCursorPosition; + Point _lastCursorPosition; void GetRequestEvent (string c1Control, string code, string [] values, string terminating) { @@ -447,13 +447,13 @@ void GetRequestEvent (string c1Control, string code, string [] values, string te X = int.Parse (values [1]) - 1, Y = int.Parse (values [0]) - 1 }; - if (lastCursorPosition.Y != point.Y) { - lastCursorPosition = point; + if (_lastCursorPosition.Y != point.Y) { + _lastCursorPosition = point; eventType = EventType.WindowPosition; var winPositionEv = new WindowPositionEvent () { CursorPosition = point }; - inputResultQueue.Enqueue (new InputResult () { + _inputResultQueue.Enqueue (new InputResult () { EventType = eventType, WindowPositionEvent = winPositionEv }); @@ -496,7 +496,7 @@ void GetRequestEvent (string c1Control, string code, string [] values, string te break; } - inputReady.Set (); + _inputReady.Set (); } void SetRequestedEvent (string c1Control, string code, string [] values, string terminating) @@ -505,7 +505,7 @@ void SetRequestedEvent (string c1Control, string code, string [] values, string var requestRespEv = new RequestResponseEvent () { ResultTuple = (c1Control, code, values, terminating) }; - inputResultQueue.Enqueue (new InputResult () { + _inputResultQueue.Enqueue (new InputResult () { EventType = eventType, RequestResponseEvent = requestRespEv }); @@ -518,12 +518,12 @@ void GetMouseEvent (MouseButtonState buttonState, Point pos) ButtonState = buttonState, }; - inputResultQueue.Enqueue (new InputResult () { + _inputResultQueue.Enqueue (new InputResult () { EventType = EventType.Mouse, MouseEvent = mouseEvent }); - inputReady.Set (); + _inputReady.Set (); } public enum EventType { @@ -614,19 +614,19 @@ internal class NetDriver : ConsoleDriver { const int COLOR_BRIGHT_CYAN = 96; const int COLOR_BRIGHT_WHITE = 97; - int cols, rows, left, top; + int _cols, _rows, _left, _top; - public override int Cols => cols; - public override int Rows => rows; - public override int Left => left; - public override int Top => top; + public override int Cols => _cols; + public override int Rows => _rows; + public override int Left => _left; + public override int Top => _top; public override bool EnableConsoleScrolling { get; set; } public NetWinVTConsole NetWinConsole { get; } public bool IsWinPlatform { get; } public override IClipboard Clipboard { get; } - public override int [,,] Contents => contents; + public override int [,,] Contents => _contents; - int largestBufferHeight; + int _largestBufferHeight; public NetDriver () { @@ -649,32 +649,32 @@ public NetDriver () } // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - int [,,] contents; - bool [] dirtyLine; + int [,,] _contents; + bool [] _dirtyLine; - static bool sync = false; + static bool _sync = false; // Current row, and current col, tracked by Move/AddCh only - int ccol, crow; + int _ccol, _crow; public override void Move (int col, int row) { - ccol = col; - crow = row; + _ccol = col; + _crow = row; } public override void AddRune (Rune rune) { - if (contents.Length != Rows * Cols * 3) { + if (_contents.Length != Rows * Cols * 3) { return; } rune = MakePrintable (rune); var runeWidth = Rune.ColumnWidth (rune); - var validClip = IsValidContent (ccol, crow, Clip); + var validLocation = IsValidLocation (_ccol, _crow); - if (validClip) { - if (runeWidth == 0 && ccol > 0) { - var r = contents [crow, ccol - 1, 0]; + if (validLocation) { + if (runeWidth == 0 && _ccol > 0) { + var r = _contents [_crow, _ccol - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); string sn; if (!s.IsNormalized ()) { @@ -683,61 +683,62 @@ public override void AddRune (Rune rune) sn = s; } var c = sn [0]; - contents [crow, ccol - 1, 0] = c; - contents [crow, ccol - 1, 1] = CurrentAttribute; - contents [crow, ccol - 1, 2] = 1; + _contents [_crow, _ccol - 1, 0] = c; + _contents [_crow, _ccol - 1, 1] = CurrentAttribute; + _contents [_crow, _ccol - 1, 2] = 1; } else { - if (runeWidth < 2 && ccol > 0 - && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + if (runeWidth < 2 && _ccol > 0 + && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { - contents [crow, ccol - 1, 0] = (int)(uint)' '; + _contents [_crow, _ccol - 1, 0] = (int)(uint)' '; - } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 + && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { - contents [crow, ccol + 1, 0] = (int)(uint)' '; - contents [crow, ccol + 1, 2] = 1; + _contents [_crow, _ccol + 1, 0] = (int)(uint)' '; + _contents [_crow, _ccol + 1, 2] = 1; } - if (runeWidth > 1 && ccol == Clip.Right - 1) { - contents [crow, ccol, 0] = (int)(uint)' '; + if (runeWidth > 1 && _ccol == Clip.Right - 1) { + _contents [_crow, _ccol, 0] = (int)(uint)' '; } else { - contents [crow, ccol, 0] = (int)(uint)rune; + _contents [_crow, _ccol, 0] = (int)(uint)rune; } - contents [crow, ccol, 1] = CurrentAttribute; - contents [crow, ccol, 2] = 1; + _contents [_crow, _ccol, 1] = CurrentAttribute; + _contents [_crow, _ccol, 2] = 1; } - dirtyLine [crow] = true; + _dirtyLine [_crow] = true; } if (runeWidth < 0 || runeWidth > 0) { - ccol++; + _ccol++; } if (runeWidth > 1) { - if (validClip && ccol < Clip.Right) { - contents [crow, ccol, 1] = CurrentAttribute; - contents [crow, ccol, 2] = 0; + if (validLocation && _ccol < Clip.Right) { + _contents [_crow, _ccol, 1] = CurrentAttribute; + _contents [_crow, _ccol, 2] = 0; } - ccol++; + _ccol++; } - if (sync) { + if (_sync) { UpdateScreen (); } } public override void AddStr (ustring str) { - foreach (var rune in str) + foreach (var rune in str) { AddRune (rune); + } } public override void End () { - mainLoop.netEvents.StopTasks (); + _mainLoop._netEvents.StopTasks (); if (IsWinPlatform) { NetWinConsole.Cleanup (); @@ -783,13 +784,13 @@ public override void Init (Action terminalResized) Console.TreatControlCAsInput = true; if (EnableConsoleScrolling) { - largestBufferHeight = Console.BufferHeight; + _largestBufferHeight = Console.BufferHeight; } else { - largestBufferHeight = Console.WindowHeight; + _largestBufferHeight = Console.WindowHeight; } - cols = Console.WindowWidth; - rows = largestBufferHeight; + _cols = Console.WindowWidth; + _rows = _largestBufferHeight; CurrentAttribute = MakeColor (Color.White, Color.Black); InitalizeColorSchemes (); @@ -805,30 +806,28 @@ public override void Init (Action terminalResized) public override void ResizeScreen () { - if (!EnableConsoleScrolling) { - if (Console.WindowHeight > 0) { - // Not supported on Unix. - if (IsWinPlatform) { - // Can raise an exception while is still resizing. - try { + if (!EnableConsoleScrolling && Console.WindowHeight > 0) { + // Not supported on Unix. + if (IsWinPlatform) { + // Can raise an exception while is still resizing. + try { #pragma warning disable CA1416 - Console.CursorTop = 0; - Console.CursorLeft = 0; - Console.WindowTop = 0; - Console.WindowLeft = 0; - if (Console.WindowHeight > Rows) { - Console.SetWindowSize (Cols, Rows); - } - Console.SetBufferSize (Cols, Rows); -#pragma warning restore CA1416 - } catch (System.IO.IOException) { - setClip (); - } catch (ArgumentOutOfRangeException) { - setClip (); + Console.CursorTop = 0; + Console.CursorLeft = 0; + Console.WindowTop = 0; + Console.WindowLeft = 0; + if (Console.WindowHeight > Rows) { + Console.SetWindowSize (Cols, Rows); } - } else { - Console.Out.Write ($"\x1b[8;{Rows};{Cols}t"); + Console.SetBufferSize (Cols, Rows); +#pragma warning restore CA1416 + } catch (System.IO.IOException) { + ClearClipRegion (); + } catch (ArgumentOutOfRangeException) { + ClearClipRegion (); } + } else { + Console.Out.Write ($"\x1b[8;{Rows};{Cols}t"); } } else { if (IsWinPlatform) { @@ -844,37 +843,32 @@ public override void ResizeScreen () Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 } catch (System.IO.IOException) { - setClip (); + ClearClipRegion (); } catch (ArgumentOutOfRangeException) { - setClip (); + ClearClipRegion (); } } } else { Console.Out.Write ($"\x1b[30;{Rows};{Cols}t"); } } - setClip (); - - void setClip () - { - Clip = new Rect (0, 0, Cols, Rows); - } + ClearClipRegion (); } public override void UpdateOffScreen () { - contents = new int [Rows, Cols, 3]; - dirtyLine = new bool [Rows]; + _contents = new int [Rows, Cols, 3]; + _dirtyLine = new bool [Rows]; - lock (contents) { + lock (_contents) { // Can raise an exception while is still resizing. try { - for (int row = 0; row < rows; row++) { - for (int c = 0; c < cols; c++) { - contents [row, c, 0] = ' '; - contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; - contents [row, c, 2] = 0; - dirtyLine [row] = true; + for (int row = 0; row < _rows; row++) { + for (int c = 0; c < _cols; c++) { + _contents [row, c, 0] = ' '; + _contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; + _contents [row, c, 2] = 0; + _dirtyLine [row] = true; } } } catch (IndexOutOfRangeException) { } @@ -894,9 +888,9 @@ public override void Refresh () public override void UpdateScreen () { - if (winChanging || Console.WindowHeight < 1 || contents.Length != Rows * Cols * 3 + if (winChanging || Console.WindowHeight < 1 || _contents.Length != Rows * Cols * 3 || (!EnableConsoleScrolling && Rows != Console.WindowHeight) - || (EnableConsoleScrolling && Rows != largestBufferHeight)) { + || (EnableConsoleScrolling && Rows != _largestBufferHeight)) { return; } @@ -914,19 +908,19 @@ public override void UpdateScreen () if (Console.WindowHeight < 1) { return; } - if (!dirtyLine [row]) { + if (!_dirtyLine [row]) { continue; } if (!SetCursorPosition (0, row)) { return; } - dirtyLine [row] = false; + _dirtyLine [row] = false; output.Clear (); for (int col = left; col < cols; col++) { lastCol = -1; var outputWidth = 0; for (; col < cols; col++) { - if (contents [row, col, 2] == 0) { + if (_contents [row, col, 2] == 0) { if (output.Length > 0) { SetCursorPosition (lastCol, row); Console.Write (output); @@ -944,20 +938,20 @@ public override void UpdateScreen () if (lastCol == -1) lastCol = col; - var attr = contents [row, col, 1]; + var attr = _contents [row, col, 1]; if (attr != redrawAttr) { redrawAttr = attr; output.Append (WriteAttributes (attr)); } outputWidth++; - var rune = contents [row, col, 0]; + var rune = _contents [row, col, 0]; char [] spair; if (Rune.DecodeSurrogatePair ((uint)rune, out spair)) { output.Append (spair); } else { output.Append ((char)rune); } - contents [row, col, 2] = 0; + _contents [row, col, 2] = 0; } } if (output.Length > 0) { @@ -1068,8 +1062,8 @@ private void SetWindowPosition (int col, int row) } catch (System.ArgumentOutOfRangeException) { } } } - top = Console.WindowTop; - left = Console.WindowLeft; + _top = Console.WindowTop; + _left = Console.WindowLeft; } private bool EnsureBufferSize () @@ -1093,9 +1087,9 @@ public override void UpdateCursor () EnsureCursorVisibility (); //Debug.WriteLine ($"Before - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); - if (ccol >= 0 && ccol < Cols && crow >= 0 && crow < Rows) { - SetCursorPosition (ccol, crow); - SetWindowPosition (0, crow); + if (_ccol >= 0 && _ccol < Cols && _crow >= 0 && _crow < Rows) { + SetCursorPosition (_ccol, _crow); + SetWindowPosition (0, _crow); } //Debug.WriteLine ($"WindowTop: {Console.WindowTop};WindowLeft: {Console.WindowLeft}"); //Debug.WriteLine ($"After - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); @@ -1233,50 +1227,50 @@ Key MapKey (ConsoleKeyInfo keyInfo) return (Key)(0xffffffff); } - KeyModifiers keyModifiers; + KeyModifiers _keyModifiers; Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) { - if (keyModifiers == null) { - keyModifiers = new KeyModifiers (); + if (_keyModifiers == null) { + _keyModifiers = new KeyModifiers (); } Key keyMod = new Key (); if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { keyMod = Key.ShiftMask; - keyModifiers.Shift = true; + _keyModifiers.Shift = true; } if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { keyMod |= Key.CtrlMask; - keyModifiers.Ctrl = true; + _keyModifiers.Ctrl = true; } if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { keyMod |= Key.AltMask; - keyModifiers.Alt = true; + _keyModifiers.Alt = true; } return keyMod != Key.Null ? keyMod | key : key; } - Action keyHandler; - Action keyDownHandler; - Action keyUpHandler; - Action mouseHandler; - NetMainLoop mainLoop; + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; + NetMainLoop _mainLoop; public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { - this.keyHandler = keyHandler; - this.keyDownHandler = keyDownHandler; - this.keyUpHandler = keyUpHandler; - this.mouseHandler = mouseHandler; + _keyHandler = keyHandler; + _keyDownHandler = keyDownHandler; + _keyUpHandler = keyUpHandler; + _mouseHandler = mouseHandler; - var mLoop = this.mainLoop = mainLoop.Driver as NetMainLoop; + var mLoop = _mainLoop = mainLoop.Driver as NetMainLoop; // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. mLoop.ProcessInput = (e) => ProcessInput (e); // Check if terminal supports requests - this.mainLoop.netEvents.EscSeqReqProc.Add ("c"); + _mainLoop._netEvents.EscSeqReqProc.Add ("c"); Console.Out.Write ("\x1b[0c"); } @@ -1288,22 +1282,22 @@ void ProcessInput (NetEvents.InputResult inputEvent) if (consoleKeyInfo.Key == ConsoleKey.Packet) { consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo); } - keyModifiers = new KeyModifiers (); + _keyModifiers = new KeyModifiers (); var map = MapKey (consoleKeyInfo); if (map == (Key)0xffffffff) { return; } if (map == Key.Null) { - keyDownHandler (new KeyEvent (map, keyModifiers)); - keyUpHandler (new KeyEvent (map, keyModifiers)); + _keyDownHandler (new KeyEvent (map, _keyModifiers)); + _keyUpHandler (new KeyEvent (map, _keyModifiers)); } else { - keyDownHandler (new KeyEvent (map, keyModifiers)); - keyHandler (new KeyEvent (map, keyModifiers)); - keyUpHandler (new KeyEvent (map, keyModifiers)); + _keyDownHandler (new KeyEvent (map, _keyModifiers)); + _keyHandler (new KeyEvent (map, _keyModifiers)); + _keyUpHandler (new KeyEvent (map, _keyModifiers)); } break; case NetEvents.EventType.Mouse: - mouseHandler (ToDriverMouse (inputEvent.MouseEvent)); + _mouseHandler (ToDriverMouse (inputEvent.MouseEvent)); break; case NetEvents.EventType.WindowSize: ChangeWin (inputEvent.WindowSizeEvent.Size); @@ -1320,14 +1314,14 @@ void ChangeWin (Size size) { winChanging = true; if (!EnableConsoleScrolling) { - largestBufferHeight = Math.Max (size.Height, 0); + _largestBufferHeight = Math.Max (size.Height, 0); } else { - largestBufferHeight = Math.Max (size.Height, largestBufferHeight); + _largestBufferHeight = Math.Max (size.Height, _largestBufferHeight); } - top = 0; - left = 0; - cols = size.Width; - rows = largestBufferHeight; + _top = 0; + _left = 0; + _cols = size.Width; + _rows = _largestBufferHeight; ResizeScreen (); UpdateOffScreen (); winChanging = false; @@ -1449,7 +1443,7 @@ public override bool SetCursorVisibility (CursorVisibility visibility) /// public override bool EnsureCursorVisibility () { - if (!(ccol >= 0 && crow >= 0 && ccol < Cols && crow < Rows)) { + if (!(_ccol >= 0 && _crow >= 0 && _ccol < Cols && _crow < Rows)) { GetCursorVisibility (out CursorVisibility cursorVisibility); savedCursorVisibility = cursorVisibility; SetCursorVisibility (CursorVisibility.Invisible); @@ -1497,6 +1491,7 @@ public override void SetColors (ConsoleColor foreground, ConsoleColor background public override void SetColors (short foregroundColorId, short backgroundColorId) { + throw new NotImplementedException (); } public override void CookMouse () @@ -1524,17 +1519,17 @@ public override void UncookMouse () /// This implementation is used for NetDriver. /// internal class NetMainLoop : IMainLoopDriver { - ManualResetEventSlim keyReady = new ManualResetEventSlim (false); - ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false); - Queue inputResult = new Queue (); - MainLoop mainLoop; - CancellationTokenSource tokenSource = new CancellationTokenSource (); - internal NetEvents netEvents; + ManualResetEventSlim _keyReady = new ManualResetEventSlim (false); + ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); + Queue _inputResult = new Queue (); + MainLoop _mainLoop; + CancellationTokenSource _tokenSource = new CancellationTokenSource (); + internal NetEvents _netEvents; /// /// Invoked when a Key is pressed. /// - public Action ProcessInput; + internal Action ProcessInput; /// /// Initializes the class with the console driver. @@ -1548,23 +1543,23 @@ public NetMainLoop (ConsoleDriver consoleDriver = null) if (consoleDriver == null) { throw new ArgumentNullException ("Console driver instance must be provided."); } - netEvents = new NetEvents (consoleDriver); + _netEvents = new NetEvents (consoleDriver); } void NetInputHandler () { while (true) { - waitForProbe.Wait (); - waitForProbe.Reset (); - if (inputResult.Count == 0) { - inputResult.Enqueue (netEvents.ReadConsoleInput ()); + _waitForProbe.Wait (); + _waitForProbe.Reset (); + if (_inputResult.Count == 0) { + _inputResult.Enqueue (_netEvents.ReadConsoleInput ()); } try { - while (inputResult.Peek () == null) { - inputResult.Dequeue (); + while (_inputResult.Peek () == null) { + _inputResult.Dequeue (); } - if (inputResult.Count > 0) { - keyReady.Set (); + if (_inputResult.Count > 0) { + _keyReady.Set (); } } catch (InvalidOperationException) { } } @@ -1572,39 +1567,39 @@ void NetInputHandler () void IMainLoopDriver.Setup (MainLoop mainLoop) { - this.mainLoop = mainLoop; + _mainLoop = mainLoop; Task.Run (NetInputHandler); } void IMainLoopDriver.Wakeup () { - keyReady.Set (); + _keyReady.Set (); } bool IMainLoopDriver.EventsPending (bool wait) { - waitForProbe.Set (); + _waitForProbe.Set (); if (CheckTimers (wait, out var waitTimeout)) { return true; } try { - if (!tokenSource.IsCancellationRequested) { - keyReady.Wait (waitTimeout, tokenSource.Token); + if (!_tokenSource.IsCancellationRequested) { + _keyReady.Wait (waitTimeout, _tokenSource.Token); } } catch (OperationCanceledException) { return true; } finally { - keyReady.Reset (); + _keyReady.Reset (); } - if (!tokenSource.IsCancellationRequested) { - return inputResult.Count > 0 || CheckTimers (wait, out _); + if (!_tokenSource.IsCancellationRequested) { + return _inputResult.Count > 0 || CheckTimers (wait, out _); } - tokenSource.Dispose (); - tokenSource = new CancellationTokenSource (); + _tokenSource.Dispose (); + _tokenSource = new CancellationTokenSource (); return true; } @@ -1612,20 +1607,22 @@ bool CheckTimers (bool wait, out int waitTimeout) { long now = DateTime.UtcNow.Ticks; - if (mainLoop.timeouts.Count > 0) { - waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); - if (waitTimeout < 0) + if (_mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) { return true; + } } else { waitTimeout = -1; } - if (!wait) + if (!wait) { waitTimeout = 0; + } int ic; - lock (mainLoop.idleHandlers) { - ic = mainLoop.idleHandlers.Count; + lock (_mainLoop.idleHandlers) { + ic = _mainLoop.idleHandlers.Count; } return ic > 0; @@ -1633,8 +1630,8 @@ bool CheckTimers (bool wait, out int waitTimeout) void IMainLoopDriver.Iteration () { - while (inputResult.Count > 0) { - ProcessInput?.Invoke (inputResult.Dequeue ().Value); + while (_inputResult.Count > 0) { + ProcessInput?.Invoke (_inputResult.Dequeue ().Value); } } } diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 71b837abff..ae011337d7 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -17,86 +17,87 @@ internal class WindowsConsole { public const int STD_INPUT_HANDLE = -10; public const int STD_ERROR_HANDLE = -12; - internal IntPtr InputHandle, OutputHandle; - IntPtr ScreenBuffer; - readonly uint originalConsoleMode; - CursorVisibility? initialCursorVisibility = null; - CursorVisibility? currentCursorVisibility = null; - CursorVisibility? pendingCursorVisibility = null; + IntPtr _inputHandle, _outputHandle; + IntPtr _screenBuffer; + readonly uint _originalConsoleMode; + CursorVisibility? _initialCursorVisibility = null; + CursorVisibility? _currentCursorVisibility = null; + CursorVisibility? _pendingCursorVisibility = null; public WindowsConsole () { - InputHandle = GetStdHandle (STD_INPUT_HANDLE); - OutputHandle = GetStdHandle (STD_OUTPUT_HANDLE); - originalConsoleMode = ConsoleMode; - var newConsoleMode = originalConsoleMode; + _inputHandle = GetStdHandle (STD_INPUT_HANDLE); + _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); + _originalConsoleMode = _consoleMode; + var newConsoleMode = _originalConsoleMode; newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags); newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode; newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput; - ConsoleMode = newConsoleMode; + _consoleMode = newConsoleMode; } - public CharInfo [] OriginalStdOutChars; + CharInfo [] _originalStdOutChars; public bool WriteToConsole (Size size, CharInfo [] charInfoBuffer, Coord coords, SmallRect window) { - if (ScreenBuffer == IntPtr.Zero) { + if (_screenBuffer == IntPtr.Zero) { ReadFromConsoleOutput (size, coords, ref window); } - return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window); + return WriteConsoleOutput (_screenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window); } public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window) { - ScreenBuffer = CreateConsoleScreenBuffer ( + _screenBuffer = CreateConsoleScreenBuffer ( DesiredAccess.GenericRead | DesiredAccess.GenericWrite, ShareMode.FileShareRead | ShareMode.FileShareWrite, IntPtr.Zero, 1, IntPtr.Zero ); - if (ScreenBuffer == INVALID_HANDLE_VALUE) { + if (_screenBuffer == INVALID_HANDLE_VALUE) { var err = Marshal.GetLastWin32Error (); - if (err != 0) + if (err != 0) { throw new System.ComponentModel.Win32Exception (err); + } } - if (!initialCursorVisibility.HasValue && GetCursorVisibility (out CursorVisibility visibility)) { - initialCursorVisibility = visibility; + if (!_initialCursorVisibility.HasValue && GetCursorVisibility (out CursorVisibility visibility)) { + _initialCursorVisibility = visibility; } - if (!SetConsoleActiveScreenBuffer (ScreenBuffer)) { + if (!SetConsoleActiveScreenBuffer (_screenBuffer)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } - OriginalStdOutChars = new CharInfo [size.Height * size.Width]; + _originalStdOutChars = new CharInfo [size.Height * size.Width]; - if (!ReadConsoleOutput (ScreenBuffer, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window)) { + if (!ReadConsoleOutput (_screenBuffer, _originalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } } public bool SetCursorPosition (Coord position) { - return SetConsoleCursorPosition (ScreenBuffer, position); + return SetConsoleCursorPosition (_screenBuffer, position); } public void SetInitialCursorVisibility () { - if (initialCursorVisibility.HasValue == false && GetCursorVisibility (out CursorVisibility visibility)) { - initialCursorVisibility = visibility; + if (_initialCursorVisibility.HasValue == false && GetCursorVisibility (out CursorVisibility visibility)) { + _initialCursorVisibility = visibility; } } public bool GetCursorVisibility (out CursorVisibility visibility) { - if (ScreenBuffer == IntPtr.Zero) { + if (_screenBuffer == IntPtr.Zero) { visibility = CursorVisibility.Invisible; return false; } - if (!GetConsoleCursorInfo (ScreenBuffer, out ConsoleCursorInfo info)) { + if (!GetConsoleCursorInfo (_screenBuffer, out ConsoleCursorInfo info)) { var err = Marshal.GetLastWin32Error (); if (err != 0) { throw new System.ComponentModel.Win32Exception (err); @@ -106,20 +107,21 @@ public bool GetCursorVisibility (out CursorVisibility visibility) return false; } - if (!info.bVisible) + if (!info.bVisible) { visibility = CursorVisibility.Invisible; - else if (info.dwSize > 50) + } else if (info.dwSize > 50) { visibility = CursorVisibility.Box; - else + } else { visibility = CursorVisibility.Underline; + } return true; } public bool EnsureCursorVisibility () { - if (initialCursorVisibility.HasValue && pendingCursorVisibility.HasValue && SetCursorVisibility (pendingCursorVisibility.Value)) { - pendingCursorVisibility = null; + if (_initialCursorVisibility.HasValue && _pendingCursorVisibility.HasValue && SetCursorVisibility (_pendingCursorVisibility.Value)) { + _pendingCursorVisibility = null; return true; } @@ -129,30 +131,31 @@ public bool EnsureCursorVisibility () public void ForceRefreshCursorVisibility () { - if (currentCursorVisibility.HasValue) { - pendingCursorVisibility = currentCursorVisibility; - currentCursorVisibility = null; + if (_currentCursorVisibility.HasValue) { + _pendingCursorVisibility = _currentCursorVisibility; + _currentCursorVisibility = null; } } public bool SetCursorVisibility (CursorVisibility visibility) { - if (initialCursorVisibility.HasValue == false) { - pendingCursorVisibility = visibility; + if (_initialCursorVisibility.HasValue == false) { + _pendingCursorVisibility = visibility; return false; } - if (currentCursorVisibility.HasValue == false || currentCursorVisibility.Value != visibility) { + if (_currentCursorVisibility.HasValue == false || _currentCursorVisibility.Value != visibility) { ConsoleCursorInfo info = new ConsoleCursorInfo { dwSize = (uint)visibility & 0x00FF, bVisible = ((uint)visibility & 0xFF00) != 0 }; - if (!SetConsoleCursorInfo (ScreenBuffer, ref info)) + if (!SetConsoleCursorInfo (_screenBuffer, ref info)) { return false; + } - currentCursorVisibility = visibility; + _currentCursorVisibility = visibility; } return true; @@ -160,35 +163,36 @@ public bool SetCursorVisibility (CursorVisibility visibility) public void Cleanup () { - if (initialCursorVisibility.HasValue) { - SetCursorVisibility (initialCursorVisibility.Value); + if (_initialCursorVisibility.HasValue) { + SetCursorVisibility (_initialCursorVisibility.Value); } SetConsoleOutputWindow (out _); - ConsoleMode = originalConsoleMode; + _consoleMode = _originalConsoleMode; //ContinueListeningForConsoleEvents = false; - if (!SetConsoleActiveScreenBuffer (OutputHandle)) { + if (!SetConsoleActiveScreenBuffer (_outputHandle)) { var err = Marshal.GetLastWin32Error (); Console.WriteLine ("Error: {0}", err); } - if (ScreenBuffer != IntPtr.Zero) - CloseHandle (ScreenBuffer); + if (_screenBuffer != IntPtr.Zero) { + CloseHandle (_screenBuffer); + } - ScreenBuffer = IntPtr.Zero; + _screenBuffer = IntPtr.Zero; } internal Size GetConsoleBufferWindow (out Point position) { - if (ScreenBuffer == IntPtr.Zero) { + if (_screenBuffer == IntPtr.Zero) { position = Point.Empty; return Size.Empty; } var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) { + if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); position = Point.Empty; return Size.Empty; @@ -204,7 +208,7 @@ internal Size GetConsoleOutputWindow (out Point position) { var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (OutputHandle, ref csbi)) { + if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, @@ -218,20 +222,20 @@ internal Size SetConsoleWindow (short cols, short rows) { var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) { + if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } - var maxWinSize = GetLargestConsoleWindowSize (ScreenBuffer); + var maxWinSize = GetLargestConsoleWindowSize (_screenBuffer); var newCols = Math.Min (cols, maxWinSize.X); var newRows = Math.Min (rows, maxWinSize.Y); csbi.dwSize = new Coord (newCols, Math.Max (newRows, (short)1)); csbi.srWindow = new SmallRect (0, 0, newCols, newRows); csbi.dwMaximumWindowSize = new Coord (newCols, newRows); - if (!SetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) { + if (!SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0)); - if (!SetConsoleWindowInfo (OutputHandle, true, ref winRect)) { + if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); return new Size (cols, rows); } @@ -241,21 +245,21 @@ internal Size SetConsoleWindow (short cols, short rows) void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi) { - if (ScreenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) { + if (_screenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } } internal Size SetConsoleOutputWindow (out Point position) { - if (ScreenBuffer == IntPtr.Zero) { + if (_screenBuffer == IntPtr.Zero) { position = Point.Empty; return Size.Empty; } var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) { + if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, @@ -263,10 +267,10 @@ internal Size SetConsoleOutputWindow (out Point position) position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); SetConsoleOutputWindow (csbi); var winRect = new SmallRect (0, 0, (short)(sz.Width - 1), (short)Math.Max (sz.Height - 1, 0)); - if (!SetConsoleScreenBufferInfoEx (OutputHandle, ref csbi)) { + if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } - if (!SetConsoleWindowInfo (OutputHandle, true, ref winRect)) { + if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } @@ -275,13 +279,13 @@ internal Size SetConsoleOutputWindow (out Point position) //bool ContinueListeningForConsoleEvents = true; - public uint ConsoleMode { + uint _consoleMode { get { - GetConsoleMode (InputHandle, out uint v); + GetConsoleMode (_inputHandle, out uint v); return v; } set { - SetConsoleMode (InputHandle, value); + SetConsoleMode (_inputHandle, value); } } @@ -357,14 +361,14 @@ public override string ToString () } public struct WindowBufferSizeRecord { - public Coord size; + public Coord _size; public WindowBufferSizeRecord (short x, short y) { - this.size = new Coord (x, y); + _size = new Coord (x, y); } - public override string ToString () => $"[WindowBufferSize{size}"; + public override string ToString () => $"[WindowBufferSize{_size}"; } [StructLayout (LayoutKind.Sequential)] @@ -445,10 +449,10 @@ public struct Coord { public short X; public short Y; - public Coord (short X, short Y) + public Coord (short x, short y) { - this.X = X; - this.Y = Y; + X = x; + Y = y; } public override string ToString () => $"({X},{Y})"; }; @@ -514,14 +518,14 @@ public override string ToString () [StructLayout (LayoutKind.Sequential)] public struct ConsoleKeyInfoEx { - public ConsoleKeyInfo consoleKeyInfo; + public ConsoleKeyInfo ConsoleKeyInfo; public bool CapsLock; public bool NumLock; public bool Scrolllock; public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock) { - this.consoleKeyInfo = consoleKeyInfo; + ConsoleKeyInfo = consoleKeyInfo; CapsLock = capslock; NumLock = numlock; Scrolllock = scrolllock; @@ -596,19 +600,13 @@ IntPtr screenBufferData [DllImport ("kernel32.dll", SetLastError = true)] static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents); - public uint InputEventCount { - get { - GetNumberOfConsoleInputEvents (InputHandle, out uint v); - return v; - } - } public InputRecord [] ReadConsoleInput () { const int bufferSize = 1; var pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * bufferSize); try { - ReadConsoleInput (InputHandle, pRecord, bufferSize, + ReadConsoleInput (_inputHandle, pRecord, bufferSize, out var numberEventsRead); return numberEventsRead == 0 @@ -703,46 +701,46 @@ static extern Coord GetLargestConsoleWindowSize ( } internal class WindowsDriver : ConsoleDriver { - static bool sync = false; - WindowsConsole.CharInfo [] OutputBuffer; - int cols, rows, left, top; - WindowsConsole.SmallRect damageRegion; - IClipboard clipboard; - int [,,] contents; - - public override int Cols => cols; - public override int Rows => rows; - public override int Left => left; - public override int Top => top; + static bool _sync = false; + WindowsConsole.CharInfo [] _outputBuffer; + int _cols, _rows, _left, _top; + WindowsConsole.SmallRect _damageRegion; + IClipboard _clipboard; + int [,,] _contents; + + public override int Cols => _cols; + public override int Rows => _rows; + public override int Left => _left; + public override int Top => _top; public override bool EnableConsoleScrolling { get; set; } - public override IClipboard Clipboard => clipboard; - public override int [,,] Contents => contents; + public override IClipboard Clipboard => _clipboard; + public override int [,,] Contents => _contents; public WindowsConsole WinConsole { get; private set; } - Action keyHandler; - Action keyDownHandler; - Action keyUpHandler; - Action mouseHandler; + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; public WindowsDriver () { WinConsole = new WindowsConsole (); - clipboard = new WindowsClipboard (); + _clipboard = new WindowsClipboard (); } public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { - this.keyHandler = keyHandler; - this.keyDownHandler = keyDownHandler; - this.keyUpHandler = keyUpHandler; - this.mouseHandler = mouseHandler; + _keyHandler = keyHandler; + _keyDownHandler = keyDownHandler; + _keyUpHandler = keyUpHandler; + _mouseHandler = mouseHandler; var mLoop = mainLoop.Driver as WindowsMainLoop; mLoop.ProcessInput = (e) => ProcessInput (e); - mLoop.WinChanged = (s,e) => { + mLoop.WinChanged = (s, e) => { ChangeWin (e.Size); }; } @@ -751,15 +749,15 @@ private void ChangeWin (Size e) { if (!EnableConsoleScrolling) { var w = e.Width; - if (w == cols - 3 && e.Height < rows) { + if (w == _cols - 3 && e.Height < _rows) { w += 3; } var newSize = WinConsole.SetConsoleWindow ( (short)Math.Max (w, 16), (short)Math.Max (e.Height, 0)); - left = 0; - top = 0; - cols = newSize.Width; - rows = newSize.Height; + _left = 0; + _top = 0; + _cols = newSize.Width; + _rows = newSize.Height; ResizeScreen (); UpdateOffScreen (); TerminalResized.Invoke (); @@ -815,17 +813,17 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.EnhancedKey: case WindowsConsole.ControlKeyState.EnhancedKey: - key = new KeyEvent (Key.CtrlMask | Key.AltMask, keyModifiers); + key = new KeyEvent (Key.CtrlMask | Key.AltMask, _keyModifiers); break; case WindowsConsole.ControlKeyState.LeftAltPressed: - key = new KeyEvent (Key.AltMask, keyModifiers); + key = new KeyEvent (Key.AltMask, _keyModifiers); break; case WindowsConsole.ControlKeyState.RightControlPressed: case WindowsConsole.ControlKeyState.LeftControlPressed: - key = new KeyEvent (Key.CtrlMask, keyModifiers); + key = new KeyEvent (Key.CtrlMask, _keyModifiers); break; case WindowsConsole.ControlKeyState.ShiftPressed: - key = new KeyEvent (Key.ShiftMask, keyModifiers); + key = new KeyEvent (Key.ShiftMask, _keyModifiers); break; case WindowsConsole.ControlKeyState.NumlockOn: break; @@ -836,47 +834,49 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) default: switch (inputEvent.KeyEvent.wVirtualKeyCode) { case 0x10: - key = new KeyEvent (Key.ShiftMask, keyModifiers); + key = new KeyEvent (Key.ShiftMask, _keyModifiers); break; case 0x11: - key = new KeyEvent (Key.CtrlMask, keyModifiers); + key = new KeyEvent (Key.CtrlMask, _keyModifiers); break; case 0x12: - key = new KeyEvent (Key.AltMask, keyModifiers); + key = new KeyEvent (Key.AltMask, _keyModifiers); break; default: - key = new KeyEvent (Key.Unknown, keyModifiers); + key = new KeyEvent (Key.Unknown, _keyModifiers); break; } break; } - if (inputEvent.KeyEvent.bKeyDown) - keyDownHandler (key); - else - keyUpHandler (key); + if (inputEvent.KeyEvent.bKeyDown) { + _keyDownHandler (key); + } else { + _keyUpHandler (key); + } } else { if (inputEvent.KeyEvent.bKeyDown) { // May occurs using SendKeys - if (keyModifiers == null) - keyModifiers = new KeyModifiers (); + if (_keyModifiers == null) { + _keyModifiers = new KeyModifiers (); + } // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event - keyDownHandler (new KeyEvent (map, keyModifiers)); - keyHandler (new KeyEvent (map, keyModifiers)); + _keyDownHandler (new KeyEvent (map, _keyModifiers)); + _keyHandler (new KeyEvent (map, _keyModifiers)); } else { - keyUpHandler (new KeyEvent (map, keyModifiers)); + _keyUpHandler (new KeyEvent (map, _keyModifiers)); } } if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) { - keyModifiers = null; + _keyModifiers = null; } break; case WindowsConsole.EventType.Mouse: var me = ToDriverMouse (inputEvent.MouseEvent); - mouseHandler (me); - if (processButtonClick) { - mouseHandler ( + _mouseHandler (me); + if (_processButtonClick) { + _mouseHandler ( new MouseEvent () { X = me.X, Y = me.Y, @@ -887,13 +887,13 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) case WindowsConsole.EventType.WindowBufferSize: var winSize = WinConsole.GetConsoleBufferWindow (out Point pos); - left = pos.X; - top = pos.Y; - cols = inputEvent.WindowBufferSizeEvent.size.X; + _left = pos.X; + _top = pos.Y; + _cols = inputEvent.WindowBufferSizeEvent._size.X; if (EnableConsoleScrolling) { - rows = Math.Max (inputEvent.WindowBufferSizeEvent.size.Y, rows); + _rows = Math.Max (inputEvent.WindowBufferSizeEvent._size.Y, _rows); } else { - rows = inputEvent.WindowBufferSizeEvent.size.Y; + _rows = inputEvent.WindowBufferSizeEvent._size.Y; } //System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}"); ResizeScreen (); @@ -906,15 +906,15 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) } } - WindowsConsole.ButtonState? lastMouseButtonPressed = null; - bool isButtonPressed = false; - bool isButtonReleased = false; - bool isButtonDoubleClicked = false; - Point? point; - Point pointMove; - //int buttonPressedCount; - bool isOneFingerDoubleClicked = false; - bool processButtonClick; + WindowsConsole.ButtonState? _lastMouseButtonPressed = null; + bool _isButtonPressed = false; + bool _isButtonReleased = false; + bool _isButtonDoubleClicked = false; + Point? _point; + Point _pointMove; + //int _buttonPressedCount; + bool _isOneFingerDoubleClicked = false; + bool _processButtonClick; MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) { @@ -923,7 +923,7 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) //System.Diagnostics.Debug.WriteLine ( // $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}"); - if (isButtonDoubleClicked || isOneFingerDoubleClicked) { + if (_isButtonDoubleClicked || _isOneFingerDoubleClicked) { Application.MainLoop.AddIdle (() => { Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); return false; @@ -935,10 +935,10 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) // be fired with it's bit set to 0. So when the button is up ButtonState will be 0. // To map to the correct driver events we save the last pressed mouse button so we can // map to the correct clicked event. - if ((lastMouseButtonPressed != null || isButtonReleased) && mouseEvent.ButtonState != 0) { - lastMouseButtonPressed = null; + if ((_lastMouseButtonPressed != null || _isButtonReleased) && mouseEvent.ButtonState != 0) { + _lastMouseButtonPressed = null; //isButtonPressed = false; - isButtonReleased = false; + _isButtonReleased = false; } var p = new Point () { @@ -1006,9 +1006,9 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) // isButtonReleased = false; //} - if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && lastMouseButtonPressed == null && !isButtonDoubleClicked) || - (lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.MouseMoved) && - mouseEvent.ButtonState != 0 && !isButtonReleased && !isButtonDoubleClicked)) { + if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && _lastMouseButtonPressed == null && !_isButtonDoubleClicked) || + (_lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.MouseMoved) && + mouseEvent.ButtonState != 0 && !_isButtonReleased && !_isButtonDoubleClicked)) { switch (mouseEvent.ButtonState) { case WindowsConsole.ButtonState.Button1Pressed: mouseFlag = MouseFlags.Button1Pressed; @@ -1023,16 +1023,17 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) break; } - if (point == null) - point = p; + if (_point == null) { + _point = p; + } if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { mouseFlag |= MouseFlags.ReportMousePosition; - isButtonReleased = false; - processButtonClick = false; + _isButtonReleased = false; + _processButtonClick = false; } - lastMouseButtonPressed = mouseEvent.ButtonState; - isButtonPressed = true; + _lastMouseButtonPressed = mouseEvent.ButtonState; + _isButtonPressed = true; if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) { Application.MainLoop.AddIdle (() => { @@ -1041,9 +1042,9 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) }); } - } else if (lastMouseButtonPressed != null && mouseEvent.EventFlags == 0 - && !isButtonReleased && !isButtonDoubleClicked && !isOneFingerDoubleClicked) { - switch (lastMouseButtonPressed) { + } else if (_lastMouseButtonPressed != null && mouseEvent.EventFlags == 0 + && !_isButtonReleased && !_isButtonDoubleClicked && !_isOneFingerDoubleClicked) { + switch (_lastMouseButtonPressed) { case WindowsConsole.ButtonState.Button1Pressed: mouseFlag = MouseFlags.Button1Released; break; @@ -1056,15 +1057,15 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) mouseFlag = MouseFlags.Button3Released; break; } - isButtonPressed = false; - isButtonReleased = true; - if (point != null && (((Point)point).X == mouseEvent.MousePosition.X && ((Point)point).Y == mouseEvent.MousePosition.Y)) { - processButtonClick = true; + _isButtonPressed = false; + _isButtonReleased = true; + if (_point != null && (((Point)_point).X == mouseEvent.MousePosition.X && ((Point)_point).Y == mouseEvent.MousePosition.Y)) { + _processButtonClick = true; } else { - point = null; + _point = null; } } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved - && !isOneFingerDoubleClicked && isButtonReleased && p == point) { + && !_isOneFingerDoubleClicked && _isButtonReleased && p == _point) { mouseFlag = ProcessButtonClick (mouseEvent); @@ -1082,8 +1083,8 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) mouseFlag = MouseFlags.Button3DoubleClicked; break; } - isButtonDoubleClicked = true; - } else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && isButtonDoubleClicked) { + _isButtonDoubleClicked = true; + } else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && _isButtonDoubleClicked) { switch (mouseEvent.ButtonState) { case WindowsConsole.ButtonState.Button1Pressed: mouseFlag = MouseFlags.Button1TripleClicked; @@ -1097,7 +1098,7 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) mouseFlag = MouseFlags.Button3TripleClicked; break; } - isButtonDoubleClicked = false; + _isButtonDoubleClicked = false; } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) { switch ((int)mouseEvent.ButtonState) { case int v when v > 0: @@ -1134,8 +1135,8 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { mouseFlag = MouseFlags.ReportMousePosition; - if (mouseEvent.MousePosition.X != pointMove.X || mouseEvent.MousePosition.Y != pointMove.Y) { - pointMove = new Point (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y); + if (mouseEvent.MousePosition.X != _pointMove.X || mouseEvent.MousePosition.Y != _pointMove.Y) { + _pointMove = new Point (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y); } } else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) { mouseFlag = 0; @@ -1156,7 +1157,7 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent) { MouseFlags mouseFlag = 0; - switch (lastMouseButtonPressed) { + switch (_lastMouseButtonPressed) { case WindowsConsole.ButtonState.Button1Pressed: mouseFlag = MouseFlags.Button1Clicked; break; @@ -1169,32 +1170,32 @@ MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent) mouseFlag = MouseFlags.Button3Clicked; break; } - point = new Point () { + _point = new Point () { X = mouseEvent.MousePosition.X, Y = mouseEvent.MousePosition.Y }; - lastMouseButtonPressed = null; - isButtonReleased = false; - processButtonClick = false; - point = null; + _lastMouseButtonPressed = null; + _isButtonReleased = false; + _processButtonClick = false; + _point = null; return mouseFlag; } async Task ProcessButtonDoubleClickedAsync () { await Task.Delay (300); - isButtonDoubleClicked = false; - isOneFingerDoubleClicked = false; + _isButtonDoubleClicked = false; + _isOneFingerDoubleClicked = false; //buttonPressedCount = 0; } async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag) { - while (isButtonPressed) { + while (_isButtonPressed) { await Task.Delay (100); var me = new MouseEvent () { - X = pointMove.X, - Y = pointMove.Y, + X = _pointMove.X, + Y = _pointMove.Y, Flags = mouseFlag }; @@ -1202,8 +1203,8 @@ async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag) if (view == null) { break; } - if (isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.Invoke (() => mouseHandler (me)); + if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { + Application.MainLoop.Invoke (() => _mouseHandler (me)); } } } @@ -1211,19 +1212,22 @@ async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag) static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag) { if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed) || - mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed)) + mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed)) { mouseFlag |= MouseFlags.ButtonCtrl; + } - if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) + if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) { mouseFlag |= MouseFlags.ButtonShift; + } if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) || - mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) + mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) { mouseFlag |= MouseFlags.ButtonAlt; + } return mouseFlag; } - KeyModifiers keyModifiers; + KeyModifiers _keyModifiers; public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent) { @@ -1236,20 +1240,27 @@ public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEve bool numlock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0; bool scrolllock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0; - if (keyModifiers == null) - keyModifiers = new KeyModifiers (); - if (shift) - keyModifiers.Shift = shift; - if (alt) - keyModifiers.Alt = alt; - if (control) - keyModifiers.Ctrl = control; - if (capslock) - keyModifiers.Capslock = capslock; - if (numlock) - keyModifiers.Numlock = numlock; - if (scrolllock) - keyModifiers.Scrolllock = scrolllock; + if (_keyModifiers == null) { + _keyModifiers = new KeyModifiers (); + } + if (shift) { + _keyModifiers.Shift = shift; + } + if (alt) { + _keyModifiers.Alt = alt; + } + if (control) { + _keyModifiers.Ctrl = control; + } + if (capslock) { + _keyModifiers.Capslock = capslock; + } + if (numlock) { + _keyModifiers.Numlock = numlock; + } + if (scrolllock) { + _keyModifiers.Scrolllock = scrolllock; + } var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); @@ -1288,7 +1299,7 @@ public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsol public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx) { - var keyInfo = keyInfoEx.consoleKeyInfo; + var keyInfo = keyInfoEx.ConsoleKeyInfo; switch (keyInfo.Key) { case ConsoleKey.Escape: return MapKeyModifiers (keyInfo, Key.Esc); @@ -1359,8 +1370,9 @@ public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx) case ConsoleKey.OemComma: case ConsoleKey.OemPlus: case ConsoleKey.OemMinus: - if (keyInfo.KeyChar == 0) + if (keyInfo.KeyChar == 0) { return Key.Unknown; + } return (Key)((uint)keyInfo.KeyChar); } @@ -1423,12 +1435,15 @@ public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx) private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) { Key keyMod = new Key (); - if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { keyMod = Key.ShiftMask; - if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) + } + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { keyMod |= Key.CtrlMask; - if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) + } + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { keyMod |= Key.AltMask; + } return keyMod != Key.Null ? keyMod | key : key; } @@ -1452,9 +1467,9 @@ public override void Init (Action terminalResized) // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); - cols = winSize.Width; - rows = winSize.Height; - WindowsConsole.SmallRect.MakeEmpty (ref damageRegion); + _cols = winSize.Width; + _rows = winSize.Height; + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); CurrentAttribute = MakeColor (Color.White, Color.Black); InitalizeColorSchemes (); @@ -1471,9 +1486,9 @@ public override void Init (Action terminalResized) public override void ResizeScreen () { - OutputBuffer = new WindowsConsole.CharInfo [Rows * Cols]; - Clip = new Rect (0, 0, Cols, Rows); - damageRegion = new WindowsConsole.SmallRect () { + _outputBuffer = new WindowsConsole.CharInfo [Rows * Cols]; + ClearClipRegion (); + _damageRegion = new WindowsConsole.SmallRect () { Top = 0, Left = 0, Bottom = (short)Rows, @@ -1493,29 +1508,29 @@ public override void ResizeScreen () public override void UpdateOffScreen () { - contents = new int [rows, cols, 3]; - for (int row = 0; row < rows; row++) { - for (int col = 0; col < cols; col++) { - int position = row * cols + col; - OutputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal; - OutputBuffer [position].Char.UnicodeChar = ' '; - contents [row, col, 0] = OutputBuffer [position].Char.UnicodeChar; - contents [row, col, 1] = OutputBuffer [position].Attributes; - contents [row, col, 2] = 0; + _contents = new int [_rows, _cols, 3]; + for (int row = 0; row < _rows; row++) { + for (int col = 0; col < _cols; col++) { + int position = row * _cols + col; + _outputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal; + _outputBuffer [position].Char.UnicodeChar = ' '; + _contents [row, col, 0] = _outputBuffer [position].Char.UnicodeChar; + _contents [row, col, 1] = _outputBuffer [position].Attributes; + _contents [row, col, 2] = 0; } } } - int ccol, crow; + int _ccol, _crow; public override void Move (int col, int row) { - ccol = col; - crow = row; + _ccol = col; + _crow = row; } int GetOutputBufferPosition () { - return crow * Cols + ccol; + return _crow * Cols + _ccol; } public override void AddRune (Rune rune) @@ -1523,11 +1538,11 @@ public override void AddRune (Rune rune) rune = MakePrintable (rune); var runeWidth = Rune.ColumnWidth (rune); var position = GetOutputBufferPosition (); - var validClip = IsValidContent (ccol, crow, Clip); + var validLocation = IsValidLocation (_ccol, _crow); - if (validClip) { - if (runeWidth == 0 && ccol > 0) { - var r = contents [crow, ccol - 1, 0]; + if (validLocation) { + if (runeWidth == 0 && _ccol > 0) { + var r = _contents [_crow, _ccol - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); string sn; if (!s.IsNormalized ()) { @@ -1536,68 +1551,69 @@ public override void AddRune (Rune rune) sn = s; } var c = sn [0]; - var prevPosition = crow * Cols + (ccol - 1); - OutputBuffer [prevPosition].Char.UnicodeChar = c; - contents [crow, ccol - 1, 0] = c; - OutputBuffer [prevPosition].Attributes = (ushort)CurrentAttribute; - contents [crow, ccol - 1, 1] = CurrentAttribute; - contents [crow, ccol - 1, 2] = 1; - WindowsConsole.SmallRect.Update (ref damageRegion, (short)(ccol - 1), (short)crow); + var prevPosition = _crow * Cols + (_ccol - 1); + _outputBuffer [prevPosition].Char.UnicodeChar = c; + _contents [_crow, _ccol - 1, 0] = c; + _outputBuffer [prevPosition].Attributes = (ushort)CurrentAttribute; + _contents [_crow, _ccol - 1, 1] = CurrentAttribute; + _contents [_crow, _ccol - 1, 2] = 1; + WindowsConsole.SmallRect.Update (ref _damageRegion, (short)(_ccol - 1), (short)_crow); } else { - if (runeWidth < 2 && ccol > 0 - && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + if (runeWidth < 2 && _ccol > 0 + && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { - var prevPosition = crow * Cols + (ccol - 1); - OutputBuffer [prevPosition].Char.UnicodeChar = ' '; - contents [crow, ccol - 1, 0] = (int)(uint)' '; + var prevPosition = _crow * Cols + (_ccol - 1); + _outputBuffer [prevPosition].Char.UnicodeChar = ' '; + _contents [_crow, _ccol - 1, 0] = (int)(uint)' '; - } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 + && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { var prevPosition = GetOutputBufferPosition () + 1; - OutputBuffer [prevPosition].Char.UnicodeChar = (char)' '; - contents [crow, ccol + 1, 0] = (int)(uint)' '; + _outputBuffer [prevPosition].Char.UnicodeChar = (char)' '; + _contents [_crow, _ccol + 1, 0] = (int)(uint)' '; } - if (runeWidth > 1 && ccol == Clip.Right - 1) { - OutputBuffer [position].Char.UnicodeChar = (char)' '; - contents [crow, ccol, 0] = (int)(uint)' '; + if (runeWidth > 1 && _ccol == Clip.Right - 1) { + _outputBuffer [position].Char.UnicodeChar = (char)' '; + _contents [_crow, _ccol, 0] = (int)(uint)' '; } else { - OutputBuffer [position].Char.UnicodeChar = (char)rune; - contents [crow, ccol, 0] = (int)(uint)rune; + _outputBuffer [position].Char.UnicodeChar = (char)rune; + _contents [_crow, _ccol, 0] = (int)(uint)rune; } - OutputBuffer [position].Attributes = (ushort)CurrentAttribute; - contents [crow, ccol, 1] = CurrentAttribute; - contents [crow, ccol, 2] = 1; - WindowsConsole.SmallRect.Update (ref damageRegion, (short)ccol, (short)crow); + _outputBuffer [position].Attributes = (ushort)CurrentAttribute; + _contents [_crow, _ccol, 1] = CurrentAttribute; + _contents [_crow, _ccol, 2] = 1; + WindowsConsole.SmallRect.Update (ref _damageRegion, (short)_ccol, (short)_crow); } } if (runeWidth < 0 || runeWidth > 0) { - ccol++; + _ccol++; } if (runeWidth > 1) { - if (validClip && ccol < Clip.Right) { + if (validLocation && _ccol < Clip.Right) { position = GetOutputBufferPosition (); - OutputBuffer [position].Attributes = (ushort)CurrentAttribute; - OutputBuffer [position].Char.UnicodeChar = (char)0x00; - contents [crow, ccol, 0] = (int)(uint)0x00; - contents [crow, ccol, 1] = CurrentAttribute; - contents [crow, ccol, 2] = 0; + _outputBuffer [position].Attributes = (ushort)CurrentAttribute; + _outputBuffer [position].Char.UnicodeChar = (char)0x00; + _contents [_crow, _ccol, 0] = (int)(uint)0x00; + _contents [_crow, _ccol, 1] = CurrentAttribute; + _contents [_crow, _ccol, 2] = 0; } - ccol++; + _ccol++; } - if (sync) { + if (_sync) { UpdateScreen (); } } public override void AddStr (ustring str) { - foreach (var rune in str) + foreach (var rune in str) { AddRune (rune); + } } public override void SetAttribute (Attribute c) @@ -1652,13 +1668,15 @@ public override void Refresh () public override void UpdateScreen () { - if (damageRegion.Left == -1) + if (_damageRegion.Left == -1) { return; + } if (!EnableConsoleScrolling) { var windowSize = WinConsole.GetConsoleBufferWindow (out _); - if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) + if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) { return; + } } var bufferCoords = new WindowsConsole.Coord () { @@ -1673,17 +1691,17 @@ public override void UpdateScreen () // Bottom = (short)Clip.Bottom //}; - WinConsole.WriteToConsole (new Size (Cols, Rows), OutputBuffer, bufferCoords, damageRegion); + WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion); // System.Diagnostics.Debugger.Log (0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n"); - WindowsConsole.SmallRect.MakeEmpty (ref damageRegion); + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); } CursorVisibility savedCursorVisibility; public override void UpdateCursor () { - if (ccol < 0 || crow < 0 || ccol > Cols || crow > Rows) { + if (_ccol < 0 || _crow < 0 || _ccol > Cols || _crow > Rows) { GetCursorVisibility (out CursorVisibility cursorVisibility); savedCursorVisibility = cursorVisibility; SetCursorVisibility (CursorVisibility.Invisible); @@ -1692,8 +1710,8 @@ public override void UpdateCursor () SetCursorVisibility (savedCursorVisibility); var position = new WindowsConsole.Coord () { - X = (short)ccol, - Y = (short)crow + X = (short)_ccol, + Y = (short)_crow }; WinConsole.SetCursorPosition (position); } @@ -1814,6 +1832,7 @@ public override void SetColors (ConsoleColor foreground, ConsoleColor background public override void SetColors (short foregroundColorId, short backgroundColorId) { + throw new NotImplementedException (); } public override void Suspend () @@ -1846,18 +1865,18 @@ public override void CookMouse () /// This implementation is used for WindowsDriver. /// internal class WindowsMainLoop : IMainLoopDriver { - ManualResetEventSlim eventReady = new ManualResetEventSlim (false); - ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false); - ManualResetEventSlim winChange = new ManualResetEventSlim (false); - MainLoop mainLoop; - ConsoleDriver consoleDriver; - WindowsConsole winConsole; - bool winChanged; - Size windowSize; - CancellationTokenSource tokenSource = new CancellationTokenSource (); + ManualResetEventSlim _eventReady = new ManualResetEventSlim (false); + ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); + ManualResetEventSlim _winChange = new ManualResetEventSlim (false); + MainLoop _mainLoop; + ConsoleDriver _consoleDriver; + WindowsConsole _winConsole; + bool _winChanged; + Size _windowSize; + CancellationTokenSource _tokenSource = new CancellationTokenSource (); // The records that we keep fetching - Queue resultQueue = new Queue (); + Queue _resultQueue = new Queue (); /// /// Invoked when a Key is pressed or released. @@ -1868,16 +1887,16 @@ internal class WindowsMainLoop : IMainLoopDriver { /// Invoked when the window is changed. /// public EventHandler WinChanged; - + public WindowsMainLoop (ConsoleDriver consoleDriver = null) { - this.consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided."); - winConsole = ((WindowsDriver)consoleDriver).WinConsole; + _consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided."); + _winConsole = ((WindowsDriver)consoleDriver).WinConsole; } void IMainLoopDriver.Setup (MainLoop mainLoop) { - this.mainLoop = mainLoop; + _mainLoop = mainLoop; Task.Run (WindowsInputHandler); Task.Run (CheckWinChange); } @@ -1885,25 +1904,25 @@ void IMainLoopDriver.Setup (MainLoop mainLoop) void WindowsInputHandler () { while (true) { - waitForProbe.Wait (); - waitForProbe.Reset (); + _waitForProbe.Wait (); + _waitForProbe.Reset (); - if (resultQueue?.Count == 0) { - resultQueue.Enqueue (winConsole.ReadConsoleInput ()); + if (_resultQueue?.Count == 0) { + _resultQueue.Enqueue (_winConsole.ReadConsoleInput ()); } - eventReady.Set (); + _eventReady.Set (); } } void CheckWinChange () { while (true) { - winChange.Wait (); - winChange.Reset (); + _winChange.Wait (); + _winChange.Reset (); WaitWinChange (); - winChanged = true; - eventReady.Set (); + _winChanged = true; + _eventReady.Set (); } } @@ -1911,11 +1930,11 @@ void WaitWinChange () { while (true) { Thread.Sleep (100); - if (!consoleDriver.EnableConsoleScrolling) { - windowSize = winConsole.GetConsoleBufferWindow (out _); + if (!_consoleDriver.EnableConsoleScrolling) { + _windowSize = _winConsole.GetConsoleBufferWindow (out _); //System.Diagnostics.Debug.WriteLine ($"{consoleDriver.EnableConsoleScrolling},{windowSize.Width},{windowSize.Height}"); - if (windowSize != Size.Empty && windowSize.Width != consoleDriver.Cols - || windowSize.Height != consoleDriver.Rows) { + if (_windowSize != Size.Empty && _windowSize.Width != _consoleDriver.Cols + || _windowSize.Height != _consoleDriver.Rows) { return; } } @@ -1925,34 +1944,34 @@ void WaitWinChange () void IMainLoopDriver.Wakeup () { //tokenSource.Cancel (); - eventReady.Set (); + _eventReady.Set (); } bool IMainLoopDriver.EventsPending (bool wait) { - waitForProbe.Set (); - winChange.Set (); + _waitForProbe.Set (); + _winChange.Set (); if (CheckTimers (wait, out var waitTimeout)) { return true; } try { - if (!tokenSource.IsCancellationRequested) { - eventReady.Wait (waitTimeout, tokenSource.Token); + if (!_tokenSource.IsCancellationRequested) { + _eventReady.Wait (waitTimeout, _tokenSource.Token); } } catch (OperationCanceledException) { return true; } finally { - eventReady.Reset (); + _eventReady.Reset (); } - if (!tokenSource.IsCancellationRequested) { - return resultQueue.Count > 0 || CheckTimers (wait, out _) || winChanged; + if (!_tokenSource.IsCancellationRequested) { + return _resultQueue.Count > 0 || CheckTimers (wait, out _) || _winChanged; } - tokenSource.Dispose (); - tokenSource = new CancellationTokenSource (); + _tokenSource.Dispose (); + _tokenSource = new CancellationTokenSource (); return true; } @@ -1960,8 +1979,8 @@ bool CheckTimers (bool wait, out int waitTimeout) { long now = DateTime.UtcNow.Ticks; - if (mainLoop.timeouts.Count > 0) { - waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (_mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); if (waitTimeout < 0) return true; } else { @@ -1972,8 +1991,8 @@ bool CheckTimers (bool wait, out int waitTimeout) waitTimeout = 0; int ic; - lock (mainLoop.idleHandlers) { - ic = mainLoop.idleHandlers.Count; + lock (_mainLoop.idleHandlers) { + ic = _mainLoop.idleHandlers.Count; } return ic > 0; @@ -1981,16 +2000,16 @@ bool CheckTimers (bool wait, out int waitTimeout) void IMainLoopDriver.Iteration () { - while (resultQueue.Count > 0) { - var inputRecords = resultQueue.Dequeue (); + while (_resultQueue.Count > 0) { + var inputRecords = _resultQueue.Dequeue (); if (inputRecords != null && inputRecords.Length > 0) { var inputEvent = inputRecords [0]; ProcessInput?.Invoke (inputEvent); } } - if (winChanged) { - winChanged = false; - WinChanged?.Invoke (this, new SizeChangedEventArgs(windowSize)); + if (_winChanged) { + _winChanged = false; + WinChanged?.Invoke (this, new SizeChangedEventArgs (_windowSize)); } } } @@ -1998,7 +2017,7 @@ void IMainLoopDriver.Iteration () class WindowsClipboard : ClipboardBase { public WindowsClipboard () { - IsSupported = IsClipboardFormatAvailable (cfUnicodeText); + IsSupported = IsClipboardFormatAvailable (_cfUnicodeText); } public override bool IsSupported { get; } @@ -2009,30 +2028,33 @@ protected override string GetClipboardDataImpl () // return null; try { - if (!OpenClipboard (IntPtr.Zero)) + if (!OpenClipboard (IntPtr.Zero)) { return null; + } - IntPtr handle = GetClipboardData (cfUnicodeText); - if (handle == IntPtr.Zero) + IntPtr handle = GetClipboardData (_cfUnicodeText); + if (handle == IntPtr.Zero) { return null; + } IntPtr pointer = IntPtr.Zero; try { pointer = GlobalLock (handle); - if (pointer == IntPtr.Zero) + if (pointer == IntPtr.Zero) { return null; + } int size = GlobalSize (handle); byte [] buff = new byte [size]; Marshal.Copy (pointer, buff, 0, size); - return System.Text.Encoding.Unicode.GetString (buff) - .TrimEnd ('\0'); + return System.Text.Encoding.Unicode.GetString (buff).TrimEnd ('\0'); } finally { - if (pointer != IntPtr.Zero) + if (pointer != IntPtr.Zero) { GlobalUnlock (handle); + } } } finally { CloseClipboard (); @@ -2065,7 +2087,7 @@ protected override void SetClipboardDataImpl (string text) GlobalUnlock (target); } - if (SetClipboardData (cfUnicodeText, hGlobal) == default) { + if (SetClipboardData (_cfUnicodeText, hGlobal) == default) { ThrowWin32 (); } @@ -2095,7 +2117,7 @@ void OpenClipboard () } } - const uint cfUnicodeText = 13; + const uint _cfUnicodeText = 13; void ThrowWin32 () { From 31ba96c6a37a8f8afe438a576f8062b29aa952d4 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sun, 7 May 2023 16:09:39 +0200 Subject: [PATCH 02/99] clip region unit tests --- Terminal.Gui/Application.cs | 2 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 6 +- Terminal.Gui/View/ViewDrawing.cs | 4 +- Terminal.Gui/Views/Menu.cs | 2 +- Terminal.Gui/Views/StatusBar.cs | 3 +- UnitTests/ConsoleDrivers/ClipRegionTests.cs | 246 +++++++++++++++++++ 6 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 UnitTests/ConsoleDrivers/ClipRegionTests.cs diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index 249b0ba768..7c48ef3232 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -1086,7 +1086,7 @@ static void OnTerminalResized () { var full = new Rect (0, 0, Driver.Cols, Driver.Rows); TerminalResized?.Invoke (new ResizedEventArgs () { Cols = full.Width, Rows = full.Height }); - Driver.Clip = full; + Driver.ClearClipRegion (); foreach (var t in _toplevels) { t.SetRelativeLayout (full); t.LayoutSubviews (); diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 192b590a96..4b8d6608ea 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -781,7 +781,6 @@ public static Rune MakePrintable (Rune c) public bool IsValidLocation (int col, int row) => col >= 0 && row >= 0 && col < Cols && row < Rows && - Clip.Contains (col, row) && IsInClipRegion (row, col); /// @@ -998,7 +997,8 @@ public Rect Clip { /// /// The clipping region that and are - /// subject to. + /// subject to. The clip region is an irregularly shaped area defined by the intersection of a set + /// of rectangles added with . To clear the clip region call . /// /// The clip. public List ClipRegion { @@ -1033,7 +1033,7 @@ public void ClearClipRegion () /// /// if and is /// within the clip region. - private bool IsInClipRegion (int col, int row) => ClipRegion.Any (rect => rect.Contains (row, col)); + private bool IsInClipRegion (int col, int row) => ClipRegion.Count == 0 || ClipRegion.Any (rect => rect.Contains (row, col)); /// /// Start of mouse moves. diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs index 5dc5236b3c..492bc1f0b1 100644 --- a/Terminal.Gui/View/ViewDrawing.cs +++ b/Terminal.Gui/View/ViewDrawing.cs @@ -312,8 +312,8 @@ public virtual bool OnDrawFrames () // TODO: Figure out what we should do if we have no superview //if (SuperView != null) { // TODO: Clipping is disabled for now to ensure we see errors - Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows);// screenBounds;// SuperView.ClipToBounds (); - //} + Driver.ClearClipRegion ();// screenBounds;// SuperView.ClipToBounds (); + //} // Each of these renders lines to either this View's LineCanvas // Those lines will be finally rendered in OnRenderLineCanvas diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 02bb864752..73916fc369 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -572,7 +572,7 @@ public override void OnDrawContent (Rect contentArea) return; } var savedClip = Driver.Clip; - Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows); + Driver.ClearClipRegion (); Driver.SetAttribute (GetNormalColor ()); diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs index 9cf90e274f..e94776ef51 100644 --- a/Terminal.Gui/Views/StatusBar.cs +++ b/Terminal.Gui/Views/StatusBar.cs @@ -121,8 +121,9 @@ public override void OnDrawContent (Rect contentArea) { Move (0, 0); Driver.SetAttribute (GetNormalColor ()); - for (int i = 0; i < Frame.Width; i++) + for (int i = 0; i < Frame.Width; i++) { Driver.AddRune (' '); + } Move (1, 0); var scheme = GetNormalColor (); diff --git a/UnitTests/ConsoleDrivers/ClipRegionTests.cs b/UnitTests/ConsoleDrivers/ClipRegionTests.cs new file mode 100644 index 0000000000..c572a9561b --- /dev/null +++ b/UnitTests/ConsoleDrivers/ClipRegionTests.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Xunit; +using Xunit.Abstractions; + +// Alias Console to MockConsole so we don't accidentally use Console +using Console = Terminal.Gui.FakeConsole; + +namespace Terminal.Gui.DriverTests { + public class ClipRegionTests { + readonly ITestOutputHelper output; + + public ClipRegionTests (ITestOutputHelper output) + { + this.output = output; + } + + [Theory] + [InlineData (typeof (FakeDriver))] + public void ClearClipRegion_Means_Screen (Type driverType) + { + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); + + Assert.Empty (driver.ClipRegion); + Assert.Equal (new Rect (0, 0, driver.Cols, driver.Rows), driver.Clip); + + // Define a clip rectangle + driver.Clip = new Rect (5, 5, 5, 5); + Assert.NotEmpty (driver.ClipRegion); + Assert.Equal (new Rect (5, 5, 5, 5), driver.Clip); + + driver.ClearClipRegion (); + Assert.Empty (driver.ClipRegion); + Assert.Equal (new Rect (0, 0, driver.Cols, driver.Rows), driver.Clip); + + // Define a clip rectangle + driver.Clip = new Rect (5, 5, 5, 5); + Assert.NotEmpty (driver.ClipRegion); + Assert.Equal (new Rect (5, 5, 5, 5), driver.Clip); + + driver.ClearClipRegion (); + Assert.Empty (driver.ClipRegion); + Assert.Equal (new Rect (0, 0, driver.Cols, driver.Rows), driver.Clip); + + Application.Shutdown (); + } + + [Theory] + [InlineData (typeof (FakeDriver))] + public void IsValidLocation (Type driverType) + { + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); + + // positive + Assert.True (driver.IsValidLocation (0, 0)); + Assert.True (driver.IsValidLocation (1, 1)); + Assert.True (driver.IsValidLocation (driver.Cols - 1, driver.Rows - 1)); + // negative + Assert.False (driver.IsValidLocation (-1, 0)); + Assert.False (driver.IsValidLocation (0, -1)); + Assert.False (driver.IsValidLocation (-1, -1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows)); + + // Define a clip rectangle + driver.Clip = new Rect (5, 5, 5, 5); + // positive + Assert.True (driver.IsValidLocation (5, 5)); + Assert.True (driver.IsValidLocation (9, 9)); + // negative + Assert.False (driver.IsValidLocation (4, 5)); + Assert.False (driver.IsValidLocation (5, 4)); + Assert.False (driver.IsValidLocation (10, 9)); + Assert.False (driver.IsValidLocation (9, 10)); + Assert.False (driver.IsValidLocation (-1, 0)); + Assert.False (driver.IsValidLocation (0, -1)); + Assert.False (driver.IsValidLocation (-1, -1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows)); + + Application.Shutdown (); + } + + [Theory] + [InlineData (typeof (FakeDriver))] + public void Clip_Set_To_Empty_AllInvalid (Type driverType) + { + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); + + // Define a clip rectangle + driver.Clip = Rect.Empty; + Assert.NotEmpty (driver.ClipRegion); + + // negative + Assert.False (driver.IsValidLocation (4, 5)); + Assert.False (driver.IsValidLocation (5, 4)); + Assert.False (driver.IsValidLocation (10, 9)); + Assert.False (driver.IsValidLocation (9, 10)); + Assert.False (driver.IsValidLocation (-1, 0)); + Assert.False (driver.IsValidLocation (0, -1)); + Assert.False (driver.IsValidLocation (-1, -1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows)); + + Application.Shutdown (); + } + + [Theory] + [InlineData (typeof (FakeDriver))] + public void AddClipRegion_Expands (Type driverType) + { + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); + + Assert.Empty (driver.ClipRegion); + + // Setup the region with a single rectangle + driver.AddToClipRegion (new Rect (5, 5, 5, 5)); + Assert.NotEmpty (driver.ClipRegion); + Assert.Equal (new Rect (5, 5, 5, 5), driver.Clip); + + // positive + Assert.True (driver.IsValidLocation (5, 5)); + Assert.True (driver.IsValidLocation (9, 9)); + // negative + Assert.False (driver.IsValidLocation (4, 5)); + Assert.False (driver.IsValidLocation (5, 4)); + Assert.False (driver.IsValidLocation (10, 9)); + Assert.False (driver.IsValidLocation (9, 10)); + Assert.False (driver.IsValidLocation (-1, 0)); + Assert.False (driver.IsValidLocation (0, -1)); + Assert.False (driver.IsValidLocation (-1, -1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows)); + + // Add a second, disjoint, rectangle to the region. This should expand the region + // to include both rectangles. maxX before was 9. The far right side of the new + // rect is 9 + 5 = 14. + driver.AddToClipRegion (new Rect (10, 10, 5, 5)); + Assert.NotEmpty (driver.ClipRegion); + Assert.Equal (new Rect (5, 5, 10, 10), driver.Clip); + + // positive + Assert.True (driver.IsValidLocation (5, 5)); + Assert.True (driver.IsValidLocation (9, 9)); + Assert.True (driver.IsValidLocation (10, 10)); + Assert.True (driver.IsValidLocation (14, 14)); + // negative + Assert.False (driver.IsValidLocation (4, 5)); + Assert.False (driver.IsValidLocation (5, 4)); + Assert.False (driver.IsValidLocation (10, 9)); + Assert.False (driver.IsValidLocation (9, 10)); + Assert.False (driver.IsValidLocation (-1, 0)); + Assert.False (driver.IsValidLocation (0, -1)); + Assert.False (driver.IsValidLocation (-1, -1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows)); + + // add a third, contiguous with first, rectangle + driver.AddToClipRegion (new Rect (0, 0, 6, 6)); + Assert.NotEmpty (driver.ClipRegion); + Assert.Equal (new Rect (0, 0, 15, 15), driver.Clip); + + // positive + Assert.True (driver.IsValidLocation (0, 5)); + Assert.True (driver.IsValidLocation (4, 4)); + Assert.True (driver.IsValidLocation (5, 5)); + Assert.True (driver.IsValidLocation (9, 9)); + Assert.True (driver.IsValidLocation (10, 10)); + Assert.True (driver.IsValidLocation (14, 14)); + Assert.True (driver.IsValidLocation (7, 7)); + // negative + Assert.False (driver.IsValidLocation (4, 10)); + Assert.False (driver.IsValidLocation (10, 9)); + Assert.False (driver.IsValidLocation (9, 10)); + + Application.Shutdown (); + } + + [Theory] + [InlineData (typeof (FakeDriver))] + public void AddRune_Is_Clipped (Type driverType) + { + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); + + driver.Move (0, 0); + driver.AddRune ('x'); + Assert.Equal ('x', driver.Contents [0, 0, 0]); + + driver.Move (5, 5); + driver.AddRune ('x'); + Assert.Equal ('x', driver.Contents [5, 5, 0]); + + // Clear the contents + driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), ' '); + Assert.Equal (' ', driver.Contents [0, 0, 0]); + + // Setup the region with a single rectangle, fill screen with 'x' + driver.Clip = new Rect (5, 5, 5, 5); + driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), 'x'); + Assert.Equal (' ', (char)driver.Contents [0, 0, 0]); + Assert.Equal (' ', (char)driver.Contents [4, 9, 0]); + Assert.Equal ('x', (char)driver.Contents [5, 5, 0]); + Assert.Equal ('x', (char)driver.Contents [9, 9, 0]); + Assert.Equal (' ', (char)driver.Contents [10, 10, 0]); + + // Add a second, disjoint, rectangle, fill screen with 'y' + driver.AddToClipRegion (new Rect (10, 10, 5, 5)); + driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), 'y'); + Assert.Equal (' ', (char)driver.Contents [4, 9, 0]); + Assert.Equal ('y', (char)driver.Contents [5, 9, 0]); + Assert.Equal ('y', (char)driver.Contents [5, 5, 0]);// in 1st rect + Assert.Equal ('y', (char)driver.Contents [9, 9, 0]); + Assert.Equal (' ', (char)driver.Contents [0, 0, 0]); + Assert.Equal ('y', (char)driver.Contents [10, 10, 0]); + Assert.Equal ('y', (char)driver.Contents [14, 14, 0]); + Assert.Equal (' ', (char)driver.Contents [15, 15, 0]); + + // add a third, contiguous with first, rectangle, fill screen with 'z' + driver.AddToClipRegion (new Rect (0, 0, 5, 5)); + driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), 'z'); + Assert.Equal ('z', (char)driver.Contents [5, 5, 0]); // in 1st rect + Assert.Equal ('z', (char)driver.Contents [0, 0, 0]); // in 3rd rect + Assert.Equal ('z', (char)driver.Contents [4, 4, 0]); // in 3rd rect + Assert.Equal (' ', (char)driver.Contents [4, 9, 0]); + Assert.Equal ('z', (char)driver.Contents [5, 5, 0]); + Assert.Equal ('z', (char)driver.Contents [9, 9, 0]); // in 2nd rect + Assert.Equal ('z', (char)driver.Contents [10, 10, 0]); + Assert.Equal ('z', (char)driver.Contents [14, 14, 0]); + Assert.Equal (' ', (char)driver.Contents [15, 15, 0]); + + Application.Shutdown (); + } + } +} From 90f368bd4f927e112755eb9694de57b66d37d7a9 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sun, 7 May 2023 16:16:33 +0200 Subject: [PATCH 03/99] api docs --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 4b8d6608ea..ec6c3fb85a 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -770,19 +770,6 @@ public static Rune MakePrintable (Rune c) return c; } - /// - /// Tests whether the specified coordinate are valid for drawing. Returns - /// if the coordinate is outside of the screen bounds or outside either or - /// . - /// - /// The column. - /// The row. - /// trueif it's a valid range,falseotherwise. - public bool IsValidLocation (int col, int row) => - col >= 0 && row >= 0 && - col < Cols && row < Rows && - IsInClipRegion (row, col); - /// /// Adds the to the display at the cursor position. /// @@ -968,6 +955,19 @@ public enum DiagnosticFlags : uint { /// public abstract void Suspend (); + /// + /// Tests whether the specified coordinate are valid for drawing. + /// + /// The column. + /// The row. + /// if the coordinate is outside of the + /// screen bounds or outside either or + /// . otherwise. + public bool IsValidLocation (int col, int row) => + col >= 0 && row >= 0 && + col < Cols && row < Rows && + IsInClipRegion (row, col); + /// /// Gets or sets the clip rectangle that and are /// subject to. Setting this property is equivalent to calling From 82b0bffbd380c6d16fe35ebb56ee81e6d138092c Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 08:06:02 +0200 Subject: [PATCH 04/99] Moved color stuff from ConsoleDriver to Color.cs --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 608 ------------------ Terminal.Gui/Drawing/Color.cs | 616 +++++++++++++++++++ 2 files changed, 616 insertions(+), 608 deletions(-) create mode 100644 Terminal.Gui/Drawing/Color.cs diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index ec6c3fb85a..1238262081 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -11,614 +11,6 @@ using System.Threading.Tasks; namespace Terminal.Gui { - /// - /// Colors that can be used to set the foreground and background colors in console applications. - /// - /// - /// The value indicates either no-color has been set or the color is invalid. - /// - [JsonConverter (typeof (ColorJsonConverter))] - public enum Color { - /// - /// The black color. - /// - Black, - /// - /// The blue color. - /// - Blue, - /// - /// The green color. - /// - Green, - /// - /// The cyan color. - /// - Cyan, - /// - /// The red color. - /// - Red, - /// - /// The magenta color. - /// - Magenta, - /// - /// The brown color. - /// - Brown, - /// - /// The gray color. - /// - Gray, - /// - /// The dark gray color. - /// - DarkGray, - /// - /// The bright bBlue color. - /// - BrightBlue, - /// - /// The bright green color. - /// - BrightGreen, - /// - /// The bright cyan color. - /// - BrightCyan, - /// - /// The bright red color. - /// - BrightRed, - /// - /// The bright magenta color. - /// - BrightMagenta, - /// - /// The bright yellow color. - /// - BrightYellow, - /// - /// The White color. - /// - White - } - - /// - /// Indicates the RGB for true colors. - /// - public class TrueColor { - /// - /// Red color component. - /// - public int Red { get; } - /// - /// Green color component. - /// - public int Green { get; } - /// - /// Blue color component. - /// - public int Blue { get; } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// - /// - public TrueColor (int red, int green, int blue) - { - Red = red; - Green = green; - Blue = blue; - } - - /// - /// Converts true color to console color. - /// - /// - public Color ToConsoleColor () - { - var trueColorMap = new Dictionary () { - { new TrueColor (0,0,0),Color.Black}, - { new TrueColor (0, 0, 0x80),Color.Blue}, - { new TrueColor (0, 0x80, 0),Color.Green}, - { new TrueColor (0, 0x80, 0x80),Color.Cyan}, - { new TrueColor (0x80, 0, 0),Color.Red}, - { new TrueColor (0x80, 0, 0x80),Color.Magenta}, - { new TrueColor (0xC1, 0x9C, 0x00),Color.Brown}, // TODO confirm this - { new TrueColor (0xC0, 0xC0, 0xC0),Color.Gray}, - { new TrueColor (0x80, 0x80, 0x80),Color.DarkGray}, - { new TrueColor (0, 0, 0xFF),Color.BrightBlue}, - { new TrueColor (0, 0xFF, 0),Color.BrightGreen}, - { new TrueColor (0, 0xFF, 0xFF),Color.BrightCyan}, - { new TrueColor (0xFF, 0, 0),Color.BrightRed}, - { new TrueColor (0xFF, 0, 0xFF),Color.BrightMagenta }, - { new TrueColor (0xFF, 0xFF, 0),Color.BrightYellow}, - { new TrueColor (0xFF, 0xFF, 0xFF),Color.White}, - }; - // Iterate over all colors in the map - var distances = trueColorMap.Select ( - k => Tuple.Create ( - // the candidate we are considering matching against (RGB) - k.Key, - - CalculateDistance (k.Key, this) - )); - - // get the closest - var match = distances.OrderBy (t => t.Item2).First (); - return trueColorMap [match.Item1]; - } - - private float CalculateDistance (TrueColor color1, TrueColor color2) - { - // use RGB distance - return - Math.Abs (color1.Red - color2.Red) + - Math.Abs (color1.Green - color2.Green) + - Math.Abs (color1.Blue - color2.Blue); - } - } - - /// - /// Attributes are used as elements that contain both a foreground and a background or platform specific features. - /// - /// - /// s are needed to map colors to terminal capabilities that might lack colors. - /// They encode both the foreground and the background color and are used in the - /// class to define color schemes that can be used in an application. - /// - [JsonConverter (typeof (AttributeJsonConverter))] - public struct Attribute { - /// - /// The -specific color attribute value. If is - /// the value of this property is invalid (typically because the Attribute was created before a driver was loaded) - /// and the attribute should be re-made (see ) before it is used. - /// - [JsonIgnore (Condition = JsonIgnoreCondition.Always)] - public int Value { get; } - - /// - /// The foreground color. - /// - [JsonConverter (typeof (ColorJsonConverter))] - public Color Foreground { get; } - - /// - /// The background color. - /// - [JsonConverter (typeof (ColorJsonConverter))] - public Color Background { get; } - - /// - /// Initializes a new instance of the struct with only the value passed to - /// and trying to get the colors if defined. - /// - /// Value. - public Attribute (int value) - { - Color foreground = default; - Color background = default; - - Initialized = false; - if (Application.Driver != null) { - Application.Driver.GetColors (value, out foreground, out background); - Initialized = true; - } - Value = value; - Foreground = foreground; - Background = background; - } - - /// - /// Initializes a new instance of the struct. - /// - /// Value. - /// Foreground - /// Background - public Attribute (int value, Color foreground, Color background) - { - Value = value; - Foreground = foreground; - Background = background; - Initialized = true; - } - - /// - /// Initializes a new instance of the struct. - /// - /// Foreground - /// Background - public Attribute (Color foreground = new Color (), Color background = new Color ()) - { - var make = Make (foreground, background); - Initialized = make.Initialized; - Value = make.Value; - Foreground = foreground; - Background = background; - } - - /// - /// Initializes a new instance of the struct - /// with the same colors for the foreground and background. - /// - /// The color. - public Attribute (Color color) : this (color, color) { } - - /// - /// Implicit conversion from an to the underlying, driver-specific, Int32 representation - /// of the color. - /// - /// The driver-specific color value stored in the attribute. - /// The attribute to convert - public static implicit operator int (Attribute c) - { - if (!c.Initialized) throw new InvalidOperationException ("Attribute: Attributes must be initialized by a driver before use."); - return c.Value; - } - - /// - /// Implicitly convert an driver-specific color value into an - /// - /// An attribute with the specified driver-specific color value. - /// value - public static implicit operator Attribute (int v) => new Attribute (v); - - /// - /// Creates an from the specified foreground and background colors. - /// - /// - /// If a has not been loaded (Application.Driver == null) this - /// method will return an attribute with set to . - /// - /// The new attribute. - /// Foreground color to use. - /// Background color to use. - public static Attribute Make (Color foreground, Color background) - { - if (Application.Driver == null) { - // Create the attribute, but show it's not been initialized - return new Attribute (-1, foreground, background) { - Initialized = false - }; - } - return Application.Driver.MakeAttribute (foreground, background); - } - - /// - /// Gets the current from the driver. - /// - /// The current attribute. - public static Attribute Get () - { - if (Application.Driver == null) - throw new InvalidOperationException ("The Application has not been initialized"); - return Application.Driver.GetAttribute (); - } - - /// - /// If the attribute has been initialized by a and - /// thus has that is valid for that driver. If the - /// and colors may have been set '-1' but - /// the attribute has not been mapped to a specific color value. - /// - /// - /// Attributes that have not been initialized must eventually be initialized before being passed to a driver. - /// - [JsonIgnore] - public bool Initialized { get; internal set; } - - /// - /// Returns if the Attribute is valid (both foreground and background have valid color values). - /// - /// - [JsonIgnore] - public bool HasValidColors { get => (int)Foreground > -1 && (int)Background > -1; } - } - - /// - /// Defines the color s for common visible elements in a . - /// Containers such as and use to determine - /// the colors used by sub-views. - /// - /// - /// See also: . - /// - [JsonConverter (typeof (ColorSchemeJsonConverter))] - public class ColorScheme : IEquatable { - Attribute _normal = new Attribute (Color.White, Color.Black); - Attribute _focus = new Attribute (Color.White, Color.Black); - Attribute _hotNormal = new Attribute (Color.White, Color.Black); - Attribute _hotFocus = new Attribute (Color.White, Color.Black); - Attribute _disabled = new Attribute (Color.White, Color.Black); - - /// - /// Used by and to track which ColorScheme - /// is being accessed. - /// - internal string schemeBeingSet = ""; - - /// - /// Creates a new instance. - /// - public ColorScheme () { } - - /// - /// Creates a new instance, initialized with the values from . - /// - /// The scheme to initlize the new instance with. - public ColorScheme (ColorScheme scheme) : base () - { - if (scheme != null) { - _normal = scheme.Normal; - _focus = scheme.Focus; - _hotNormal = scheme.HotNormal; - _disabled = scheme.Disabled; - _hotFocus = scheme.HotFocus; - } - } - - /// - /// The foreground and background color for text when the view is not focused, hot, or disabled. - /// - public Attribute Normal { - get { return _normal; } - set { - if (!value.HasValidColors) { - return; - } - _normal = value; - } - } - - /// - /// The foreground and background color for text when the view has the focus. - /// - public Attribute Focus { - get { return _focus; } - set { - if (!value.HasValidColors) { - return; - } - _focus = value; - } - } - - /// - /// The foreground and background color for text when the view is highlighted (hot). - /// - public Attribute HotNormal { - get { return _hotNormal; } - set { - if (!value.HasValidColors) { - return; - } - _hotNormal = value; - } - } - - /// - /// The foreground and background color for text when the view is highlighted (hot) and has focus. - /// - public Attribute HotFocus { - get { return _hotFocus; } - set { - if (!value.HasValidColors) { - return; - } - _hotFocus = value; - } - } - - /// - /// The default foreground and background color for text, when the view is disabled. - /// - public Attribute Disabled { - get { return _disabled; } - set { - if (!value.HasValidColors) { - return; - } - _disabled = value; - } - } - - /// - /// Compares two objects for equality. - /// - /// - /// true if the two objects are equal - public override bool Equals (object obj) - { - return Equals (obj as ColorScheme); - } - - /// - /// Compares two objects for equality. - /// - /// - /// true if the two objects are equal - public bool Equals (ColorScheme other) - { - return other != null && - EqualityComparer.Default.Equals (_normal, other._normal) && - EqualityComparer.Default.Equals (_focus, other._focus) && - EqualityComparer.Default.Equals (_hotNormal, other._hotNormal) && - EqualityComparer.Default.Equals (_hotFocus, other._hotFocus) && - EqualityComparer.Default.Equals (_disabled, other._disabled); - } - - /// - /// Returns a hashcode for this instance. - /// - /// hashcode for this instance - public override int GetHashCode () - { - int hashCode = -1242460230; - hashCode = hashCode * -1521134295 + _normal.GetHashCode (); - hashCode = hashCode * -1521134295 + _focus.GetHashCode (); - hashCode = hashCode * -1521134295 + _hotNormal.GetHashCode (); - hashCode = hashCode * -1521134295 + _hotFocus.GetHashCode (); - hashCode = hashCode * -1521134295 + _disabled.GetHashCode (); - return hashCode; - } - - /// - /// Compares two objects for equality. - /// - /// - /// - /// true if the two objects are equivalent - public static bool operator == (ColorScheme left, ColorScheme right) - { - return EqualityComparer.Default.Equals (left, right); - } - - /// - /// Compares two objects for inequality. - /// - /// - /// - /// true if the two objects are not equivalent - public static bool operator != (ColorScheme left, ColorScheme right) - { - return !(left == right); - } - - internal void Initialize () - { - // If the new scheme was created before a driver was loaded, we need to re-make - // the attributes - if (!_normal.Initialized) { - _normal = new Attribute (_normal.Foreground, _normal.Background); - } - if (!_focus.Initialized) { - _focus = new Attribute (_focus.Foreground, _focus.Background); - } - if (!_hotNormal.Initialized) { - _hotNormal = new Attribute (_hotNormal.Foreground, _hotNormal.Background); - } - if (!_hotFocus.Initialized) { - _hotFocus = new Attribute (_hotFocus.Foreground, _hotFocus.Background); - } - if (!_disabled.Initialized) { - _disabled = new Attribute (_disabled.Foreground, _disabled.Background); - } - } - } - - /// - /// The default s for the application. - /// - /// - /// This property can be set in a Theme to change the default for the application. - /// - public static class Colors { - private class SchemeNameComparerIgnoreCase : IEqualityComparer { - public bool Equals (string x, string y) - { - if (x != null && y != null) { - return string.Equals (x, y, StringComparison.InvariantCultureIgnoreCase); - } - return false; - } - - public int GetHashCode (string obj) - { - return obj.ToLowerInvariant ().GetHashCode (); - } - } - - static Colors () - { - ColorSchemes = Create (); - } - - /// - /// Creates a new dictionary of new objects. - /// - public static Dictionary Create () - { - // Use reflection to dynamically create the default set of ColorSchemes from the list defined - // by the class. - return typeof (Colors).GetProperties () - .Where (p => p.PropertyType == typeof (ColorScheme)) - .Select (p => new KeyValuePair (p.Name, new ColorScheme ())) - .ToDictionary (t => t.Key, t => t.Value, comparer: new SchemeNameComparerIgnoreCase ()); - } - - /// - /// The application Toplevel color scheme, for the default Toplevel views. - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["TopLevel"]; - /// - /// - public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); } - - /// - /// The base color scheme, for the default Toplevel views. - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Base"]; - /// - /// - public static ColorScheme Base { get => GetColorScheme (); set => SetColorScheme (value); } - - /// - /// The dialog color scheme, for standard popup dialog boxes - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Dialog"]; - /// - /// - public static ColorScheme Dialog { get => GetColorScheme (); set => SetColorScheme (value); } - - /// - /// The menu bar color - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Menu"]; - /// - /// - public static ColorScheme Menu { get => GetColorScheme (); set => SetColorScheme (value); } - - /// - /// The color scheme for showing errors. - /// - /// - /// - /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Error"]; - /// - /// - public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); } - - static ColorScheme GetColorScheme ([CallerMemberName] string schemeBeingSet = null) - { - return ColorSchemes [schemeBeingSet]; - } - - static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string schemeBeingSet = null) - { - ColorSchemes [schemeBeingSet] = colorScheme; - colorScheme.schemeBeingSet = schemeBeingSet; - } - - /// - /// Provides the defined s. - /// - [SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)] - [JsonConverter (typeof (DictionaryJsonConverter))] - public static Dictionary ColorSchemes { get; private set; } - } - /// /// Cursors Visibility that are displayed /// diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs new file mode 100644 index 0000000000..c97e54117f --- /dev/null +++ b/Terminal.Gui/Drawing/Color.cs @@ -0,0 +1,616 @@ +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text.Json.Serialization; +using System; + +namespace Terminal.Gui { + /// + /// Colors that can be used to set the foreground and background colors in console applications. + /// + /// + /// The value indicates either no-color has been set or the color is invalid. + /// + [JsonConverter (typeof (ColorJsonConverter))] + public enum Color { + /// + /// The black color. + /// + Black, + /// + /// The blue color. + /// + Blue, + /// + /// The green color. + /// + Green, + /// + /// The cyan color. + /// + Cyan, + /// + /// The red color. + /// + Red, + /// + /// The magenta color. + /// + Magenta, + /// + /// The brown color. + /// + Brown, + /// + /// The gray color. + /// + Gray, + /// + /// The dark gray color. + /// + DarkGray, + /// + /// The bright bBlue color. + /// + BrightBlue, + /// + /// The bright green color. + /// + BrightGreen, + /// + /// The bright cyan color. + /// + BrightCyan, + /// + /// The bright red color. + /// + BrightRed, + /// + /// The bright magenta color. + /// + BrightMagenta, + /// + /// The bright yellow color. + /// + BrightYellow, + /// + /// The White color. + /// + White + } + + /// + /// Indicates the RGB for true colors. + /// + public class TrueColor { + /// + /// Red color component. + /// + public int Red { get; } + /// + /// Green color component. + /// + public int Green { get; } + /// + /// Blue color component. + /// + public int Blue { get; } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// + /// + public TrueColor (int red, int green, int blue) + { + Red = red; + Green = green; + Blue = blue; + } + + /// + /// Converts true color to console color. + /// + /// + public Color ToConsoleColor () + { + var trueColorMap = new Dictionary () { + { new TrueColor (0,0,0),Color.Black}, + { new TrueColor (0, 0, 0x80),Color.Blue}, + { new TrueColor (0, 0x80, 0),Color.Green}, + { new TrueColor (0, 0x80, 0x80),Color.Cyan}, + { new TrueColor (0x80, 0, 0),Color.Red}, + { new TrueColor (0x80, 0, 0x80),Color.Magenta}, + { new TrueColor (0xC1, 0x9C, 0x00),Color.Brown}, // TODO confirm this + { new TrueColor (0xC0, 0xC0, 0xC0),Color.Gray}, + { new TrueColor (0x80, 0x80, 0x80),Color.DarkGray}, + { new TrueColor (0, 0, 0xFF),Color.BrightBlue}, + { new TrueColor (0, 0xFF, 0),Color.BrightGreen}, + { new TrueColor (0, 0xFF, 0xFF),Color.BrightCyan}, + { new TrueColor (0xFF, 0, 0),Color.BrightRed}, + { new TrueColor (0xFF, 0, 0xFF),Color.BrightMagenta }, + { new TrueColor (0xFF, 0xFF, 0),Color.BrightYellow}, + { new TrueColor (0xFF, 0xFF, 0xFF),Color.White}, + }; + // Iterate over all colors in the map + var distances = trueColorMap.Select ( + k => Tuple.Create ( + // the candidate we are considering matching against (RGB) + k.Key, + + CalculateDistance (k.Key, this) + )); + + // get the closest + var match = distances.OrderBy (t => t.Item2).First (); + return trueColorMap [match.Item1]; + } + + private float CalculateDistance (TrueColor color1, TrueColor color2) + { + // use RGB distance + return + Math.Abs (color1.Red - color2.Red) + + Math.Abs (color1.Green - color2.Green) + + Math.Abs (color1.Blue - color2.Blue); + } + } + + /// + /// Attributes are used as elements that contain both a foreground and a background or platform specific features. + /// + /// + /// s are needed to map colors to terminal capabilities that might lack colors. + /// They encode both the foreground and the background color and are used in the + /// class to define color schemes that can be used in an application. + /// + [JsonConverter (typeof (AttributeJsonConverter))] + public struct Attribute { + /// + /// The -specific color attribute value. If is + /// the value of this property is invalid (typically because the Attribute was created before a driver was loaded) + /// and the attribute should be re-made (see ) before it is used. + /// + [JsonIgnore (Condition = JsonIgnoreCondition.Always)] + public int Value { get; } + + /// + /// The foreground color. + /// + [JsonConverter (typeof (ColorJsonConverter))] + public Color Foreground { get; } + + /// + /// The background color. + /// + [JsonConverter (typeof (ColorJsonConverter))] + public Color Background { get; } + + /// + /// Initializes a new instance of the struct with only the value passed to + /// and trying to get the colors if defined. + /// + /// Value. + public Attribute (int value) + { + Color foreground = default; + Color background = default; + + Initialized = false; + if (Application.Driver != null) { + Application.Driver.GetColors (value, out foreground, out background); + Initialized = true; + } + Value = value; + Foreground = foreground; + Background = background; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Value. + /// Foreground + /// Background + public Attribute (int value, Color foreground, Color background) + { + Value = value; + Foreground = foreground; + Background = background; + Initialized = true; + } + + /// + /// Initializes a new instance of the struct. + /// + /// Foreground + /// Background + public Attribute (Color foreground = new Color (), Color background = new Color ()) + { + var make = Make (foreground, background); + Initialized = make.Initialized; + Value = make.Value; + Foreground = foreground; + Background = background; + } + + /// + /// Initializes a new instance of the struct + /// with the same colors for the foreground and background. + /// + /// The color. + public Attribute (Color color) : this (color, color) { } + + /// + /// Implicit conversion from an to the underlying, driver-specific, Int32 representation + /// of the color. + /// + /// The driver-specific color value stored in the attribute. + /// The attribute to convert + public static implicit operator int (Attribute c) + { + if (!c.Initialized) throw new InvalidOperationException ("Attribute: Attributes must be initialized by a driver before use."); + return c.Value; + } + + /// + /// Implicitly convert an driver-specific color value into an + /// + /// An attribute with the specified driver-specific color value. + /// value + public static implicit operator Attribute (int v) => new Attribute (v); + + /// + /// Creates an from the specified foreground and background colors. + /// + /// + /// If a has not been loaded (Application.Driver == null) this + /// method will return an attribute with set to . + /// + /// The new attribute. + /// Foreground color to use. + /// Background color to use. + public static Attribute Make (Color foreground, Color background) + { + if (Application.Driver == null) { + // Create the attribute, but show it's not been initialized + return new Attribute (-1, foreground, background) { + Initialized = false + }; + } + return Application.Driver.MakeAttribute (foreground, background); + } + + /// + /// Gets the current from the driver. + /// + /// The current attribute. + public static Attribute Get () + { + if (Application.Driver == null) + throw new InvalidOperationException ("The Application has not been initialized"); + return Application.Driver.GetAttribute (); + } + + /// + /// If the attribute has been initialized by a and + /// thus has that is valid for that driver. If the + /// and colors may have been set '-1' but + /// the attribute has not been mapped to a specific color value. + /// + /// + /// Attributes that have not been initialized must eventually be initialized before being passed to a driver. + /// + [JsonIgnore] + public bool Initialized { get; internal set; } + + /// + /// Returns if the Attribute is valid (both foreground and background have valid color values). + /// + /// + [JsonIgnore] + public bool HasValidColors { get => (int)Foreground > -1 && (int)Background > -1; } + } + + /// + /// Defines the color s for common visible elements in a . + /// Containers such as and use to determine + /// the colors used by sub-views. + /// + /// + /// See also: . + /// + [JsonConverter (typeof (ColorSchemeJsonConverter))] + public class ColorScheme : IEquatable { + Attribute _normal = new Attribute (Color.White, Color.Black); + Attribute _focus = new Attribute (Color.White, Color.Black); + Attribute _hotNormal = new Attribute (Color.White, Color.Black); + Attribute _hotFocus = new Attribute (Color.White, Color.Black); + Attribute _disabled = new Attribute (Color.White, Color.Black); + + /// + /// Used by and to track which ColorScheme + /// is being accessed. + /// + internal string schemeBeingSet = ""; + + /// + /// Creates a new instance. + /// + public ColorScheme () { } + + /// + /// Creates a new instance, initialized with the values from . + /// + /// The scheme to initlize the new instance with. + public ColorScheme (ColorScheme scheme) : base () + { + if (scheme != null) { + _normal = scheme.Normal; + _focus = scheme.Focus; + _hotNormal = scheme.HotNormal; + _disabled = scheme.Disabled; + _hotFocus = scheme.HotFocus; + } + } + + /// + /// The foreground and background color for text when the view is not focused, hot, or disabled. + /// + public Attribute Normal { + get { return _normal; } + set { + if (!value.HasValidColors) { + return; + } + _normal = value; + } + } + + /// + /// The foreground and background color for text when the view has the focus. + /// + public Attribute Focus { + get { return _focus; } + set { + if (!value.HasValidColors) { + return; + } + _focus = value; + } + } + + /// + /// The foreground and background color for text when the view is highlighted (hot). + /// + public Attribute HotNormal { + get { return _hotNormal; } + set { + if (!value.HasValidColors) { + return; + } + _hotNormal = value; + } + } + + /// + /// The foreground and background color for text when the view is highlighted (hot) and has focus. + /// + public Attribute HotFocus { + get { return _hotFocus; } + set { + if (!value.HasValidColors) { + return; + } + _hotFocus = value; + } + } + + /// + /// The default foreground and background color for text, when the view is disabled. + /// + public Attribute Disabled { + get { return _disabled; } + set { + if (!value.HasValidColors) { + return; + } + _disabled = value; + } + } + + /// + /// Compares two objects for equality. + /// + /// + /// true if the two objects are equal + public override bool Equals (object obj) + { + return Equals (obj as ColorScheme); + } + + /// + /// Compares two objects for equality. + /// + /// + /// true if the two objects are equal + public bool Equals (ColorScheme other) + { + return other != null && + EqualityComparer.Default.Equals (_normal, other._normal) && + EqualityComparer.Default.Equals (_focus, other._focus) && + EqualityComparer.Default.Equals (_hotNormal, other._hotNormal) && + EqualityComparer.Default.Equals (_hotFocus, other._hotFocus) && + EqualityComparer.Default.Equals (_disabled, other._disabled); + } + + /// + /// Returns a hashcode for this instance. + /// + /// hashcode for this instance + public override int GetHashCode () + { + int hashCode = -1242460230; + hashCode = hashCode * -1521134295 + _normal.GetHashCode (); + hashCode = hashCode * -1521134295 + _focus.GetHashCode (); + hashCode = hashCode * -1521134295 + _hotNormal.GetHashCode (); + hashCode = hashCode * -1521134295 + _hotFocus.GetHashCode (); + hashCode = hashCode * -1521134295 + _disabled.GetHashCode (); + return hashCode; + } + + /// + /// Compares two objects for equality. + /// + /// + /// + /// true if the two objects are equivalent + public static bool operator == (ColorScheme left, ColorScheme right) + { + return EqualityComparer.Default.Equals (left, right); + } + + /// + /// Compares two objects for inequality. + /// + /// + /// + /// true if the two objects are not equivalent + public static bool operator != (ColorScheme left, ColorScheme right) + { + return !(left == right); + } + + internal void Initialize () + { + // If the new scheme was created before a driver was loaded, we need to re-make + // the attributes + if (!_normal.Initialized) { + _normal = new Attribute (_normal.Foreground, _normal.Background); + } + if (!_focus.Initialized) { + _focus = new Attribute (_focus.Foreground, _focus.Background); + } + if (!_hotNormal.Initialized) { + _hotNormal = new Attribute (_hotNormal.Foreground, _hotNormal.Background); + } + if (!_hotFocus.Initialized) { + _hotFocus = new Attribute (_hotFocus.Foreground, _hotFocus.Background); + } + if (!_disabled.Initialized) { + _disabled = new Attribute (_disabled.Foreground, _disabled.Background); + } + } + } + + /// + /// The default s for the application. + /// + /// + /// This property can be set in a Theme to change the default for the application. + /// + public static class Colors { + private class SchemeNameComparerIgnoreCase : IEqualityComparer { + public bool Equals (string x, string y) + { + if (x != null && y != null) { + return string.Equals (x, y, StringComparison.InvariantCultureIgnoreCase); + } + return false; + } + + public int GetHashCode (string obj) + { + return obj.ToLowerInvariant ().GetHashCode (); + } + } + + static Colors () + { + ColorSchemes = Create (); + } + + /// + /// Creates a new dictionary of new objects. + /// + public static Dictionary Create () + { + // Use reflection to dynamically create the default set of ColorSchemes from the list defined + // by the class. + return typeof (Colors).GetProperties () + .Where (p => p.PropertyType == typeof (ColorScheme)) + .Select (p => new KeyValuePair (p.Name, new ColorScheme ())) + .ToDictionary (t => t.Key, t => t.Value, comparer: new SchemeNameComparerIgnoreCase ()); + } + + /// + /// The application Toplevel color scheme, for the default Toplevel views. + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["TopLevel"]; + /// + /// + public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); } + + /// + /// The base color scheme, for the default Toplevel views. + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Base"]; + /// + /// + public static ColorScheme Base { get => GetColorScheme (); set => SetColorScheme (value); } + + /// + /// The dialog color scheme, for standard popup dialog boxes + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Dialog"]; + /// + /// + public static ColorScheme Dialog { get => GetColorScheme (); set => SetColorScheme (value); } + + /// + /// The menu bar color + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Menu"]; + /// + /// + public static ColorScheme Menu { get => GetColorScheme (); set => SetColorScheme (value); } + + /// + /// The color scheme for showing errors. + /// + /// + /// + /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Error"]; + /// + /// + public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); } + + static ColorScheme GetColorScheme ([CallerMemberName] string schemeBeingSet = null) + { + return ColorSchemes [schemeBeingSet]; + } + + static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string schemeBeingSet = null) + { + ColorSchemes [schemeBeingSet] = colorScheme; + colorScheme.schemeBeingSet = schemeBeingSet; + } + + /// + /// Provides the defined s. + /// + [SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)] + [JsonConverter (typeof (DictionaryJsonConverter))] + public static Dictionary ColorSchemes { get; private set; } + } + +} From 953f4e66ec77e41ab74537031c5692359abb7bc7 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 08:13:53 +0200 Subject: [PATCH 05/99] Removes unused ConsoleDriver APIs --- Terminal.Gui/Application.cs | 4 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 50 ++++++------ .../CursesDriver/CursesDriver.cs | 79 ++++++++----------- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 18 +---- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 25 ------ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 16 ---- 6 files changed, 59 insertions(+), 133 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index 7c48ef3232..a1850526ab 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -1148,7 +1148,7 @@ public static void GrabMouse (View view) if (!OnGrabbingMouse (view)) { OnGrabbedMouse (view); _mouseGrabView = view; - Driver.UncookMouse (); + //Driver.UncookMouse (); } } @@ -1162,7 +1162,7 @@ public static void UngrabMouse () if (!OnUnGrabbingMouse (_mouseGrabView)) { OnUnGrabbedMouse (_mouseGrabView); _mouseGrabView = null; - Driver.CookMouse (); + //Driver.CookMouse (); } } diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 1238262081..10a41ac192 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -256,22 +256,22 @@ public virtual void SetAttribute (Attribute c) CurrentAttribute = c; } - /// - /// Set Colors from limit sets of colors. Not implemented by any driver: See Issue #2300. - /// - /// Foreground. - /// Background. - public abstract void SetColors (ConsoleColor foreground, ConsoleColor background); - - // Advanced uses - set colors to any pre-set pairs, you would need to init_color - // that independently with the R, G, B values. - /// - /// Advanced uses - set colors to any pre-set pairs, you would need to init_color - /// that independently with the R, G, B values. Not implemented by any driver: See Issue #2300. - /// - /// Foreground color identifier. - /// Background color identifier. - public abstract void SetColors (short foregroundColorId, short backgroundColorId); + ///// + ///// Set Colors from limit sets of colors. Not implemented by any driver: See Issue #2300. + ///// + ///// Foreground. + ///// Background. + //public abstract void SetColors (ConsoleColor foreground, ConsoleColor background); + + //// Advanced uses - set colors to any pre-set pairs, you would need to init_color + //// that independently with the R, G, B values. + ///// + ///// Advanced uses - set colors to any pre-set pairs, you would need to init_color + ///// that independently with the R, G, B values. Not implemented by any driver: See Issue #2300. + ///// + ///// Foreground color identifier. + ///// Background color identifier. + //public abstract void SetColors (short foregroundColorId, short backgroundColorId); /// /// Gets the foreground and background colors based on the value. @@ -437,16 +437,16 @@ public void ClearClipRegion () /// public abstract void StopReportingMouseMoves (); - /// - /// Disables the cooked event processing from the mouse driver. - /// At startup, it is assumed mouse events are cooked. Not implemented by any driver: See Issue #2300. - /// - public abstract void UncookMouse (); + ///// + ///// Disables the cooked event processing from the mouse driver. + ///// At startup, it is assumed mouse events are cooked. Not implemented by any driver: See Issue #2300. + ///// + //public abstract void UncookMouse (); - /// - /// Enables the cooked event processing from the mouse driver. Not implemented by any driver: See Issue #2300. - /// - public abstract void CookMouse (); + ///// + ///// Enables the cooked event processing from the mouse driver. Not implemented by any driver: See Issue #2300. + ///// + //public abstract void CookMouse (); private Attribute currentAttribute; diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index b311ff60da..31ccbe7c32 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -198,36 +198,36 @@ public override Attribute MakeColor (Color fore, Color back) int [,] _colorPairs = new int [16, 16]; - public override void SetColors (ConsoleColor foreground, ConsoleColor background) - { - // BUGBUG: This code is never called ?? See Issue #2300 - int f = (short)foreground; - int b = (short)background; - var v = _colorPairs [f, b]; - if ((v & 0x10000) == 0) { - b &= 0x7; - bool bold = (f & 0x8) != 0; - f &= 0x7; - - v = MakeColor ((short)f, (short)b) | (bold ? Curses.A_BOLD : 0); - _colorPairs [(int)foreground, (int)background] = v | 0x1000; - } - SetAttribute (v & 0xffff); - } - - //Dictionary _rawPairs = new Dictionary (); - public override void SetColors (short foreColorId, short backgroundColorId) - { - throw new NotImplementedException (); - - // BUGBUG: This code is never called ?? See Issue #2300 - //int key = ((ushort)foreColorId << 16) | (ushort)backgroundColorId; - //if (!_rawPairs.TryGetValue (key, out var v)) { - // v = MakeColor (foreColorId, backgroundColorId); - // _rawPairs [key] = v; - //} - //SetAttribute (v); - } + //public override void SetColors (ConsoleColor foreground, ConsoleColor background) + //{ + // // BUGBUG: This code is never called ?? See Issue #2300 + // int f = (short)foreground; + // int b = (short)background; + // var v = _colorPairs [f, b]; + // if ((v & 0x10000) == 0) { + // b &= 0x7; + // bool bold = (f & 0x8) != 0; + // f &= 0x7; + + // v = MakeColor ((short)f, (short)b) | (bold ? Curses.A_BOLD : 0); + // _colorPairs [(int)foreground, (int)background] = v | 0x1000; + // } + // SetAttribute (v & 0xffff); + //} + + ////Dictionary _rawPairs = new Dictionary (); + //public override void SetColors (short foreColorId, short backgroundColorId) + //{ + // throw new NotImplementedException (); + + // // BUGBUG: This code is never called ?? See Issue #2300 + // //int key = ((ushort)foreColorId << 16) | (ushort)backgroundColorId; + // //if (!_rawPairs.TryGetValue (key, out var v)) { + // // v = MakeColor (foreColorId, backgroundColorId); + // // _rawPairs [key] = v; + // //} + // //SetAttribute (v); + //} static Key MapCursesKey (int cursesKey) { @@ -774,24 +774,7 @@ public override void StopReportingMouseMoves () { Console.Out.Write (EscSeqUtils.DisableMouseEvents); } - - //int lastMouseInterval; - //bool mouseGrabbed; - - public override void UncookMouse () - { - //if (mouseGrabbed) - // return; - //lastMouseInterval = Curses.mouseinterval (0); - //mouseGrabbed = true; - } - - public override void CookMouse () - { - //mouseGrabbed = false; - //Curses.mouseinterval (lastMouseInterval); - } - + /// public override bool GetCursorVisibility (out CursorVisibility visibility) { diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 813df155e0..a32bb60164 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -674,23 +674,7 @@ public override void StopReportingMouseMoves () public override void Suspend () { } - - public override void SetColors (ConsoleColor foreground, ConsoleColor background) - { - } - - public override void SetColors (short foregroundColorId, short backgroundColorId) - { - throw new NotImplementedException (); - } - - public override void CookMouse () - { - } - - public override void UncookMouse () - { - } + #endregion diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index b0ce4a0f4c..ffbd31a2b5 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1483,31 +1483,6 @@ public override bool GetColors (int value, out Color foreground, out Color backg } return hasColor; } - - #region Unused - public override void SetColors (ConsoleColor foreground, ConsoleColor background) - { - } - - public override void SetColors (short foregroundColorId, short backgroundColorId) - { - throw new NotImplementedException (); - } - - public override void CookMouse () - { - } - - public override void UncookMouse () - { - } - #endregion - - // - // These are for the .NET driver, but running natively on Windows, wont run - // on the Mono emulation - // - } /// diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index ae011337d7..bc02960f40 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1826,15 +1826,6 @@ public override bool GetColors (int value, out Color foreground, out Color backg } #region Unused - public override void SetColors (ConsoleColor foreground, ConsoleColor background) - { - } - - public override void SetColors (short foregroundColorId, short backgroundColorId) - { - throw new NotImplementedException (); - } - public override void Suspend () { } @@ -1847,13 +1838,6 @@ public override void StopReportingMouseMoves () { } - public override void UncookMouse () - { - } - - public override void CookMouse () - { - } #endregion } From 73a1b8671dd1400faaac046b56466511870ed69f Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 08:44:07 +0200 Subject: [PATCH 06/99] Code cleanup and Removes unused ConsoleDriver APIs --- Terminal.Gui/Configuration/ThemeScope.cs | 2 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 62 ++++--------------- .../CursesDriver/CursesDriver.cs | 8 +-- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 21 ++----- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 19 ++---- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 20 ++---- Terminal.Gui/Input/Command.cs | 2 +- Terminal.Gui/Views/TileView.cs | 2 +- UnitTests/ConsoleDrivers/AttributeTests.cs | 2 - 9 files changed, 36 insertions(+), 102 deletions(-) diff --git a/Terminal.Gui/Configuration/ThemeScope.cs b/Terminal.Gui/Configuration/ThemeScope.cs index 04adf94a52..3c63a55222 100644 --- a/Terminal.Gui/Configuration/ThemeScope.cs +++ b/Terminal.Gui/Configuration/ThemeScope.cs @@ -57,7 +57,7 @@ public class ThemeScope : Scope { internal override bool Apply () { var ret = base.Apply (); - Application.Driver?.InitalizeColorSchemes (); + Application.Driver?.InitializeColorSchemes (); return ret; } } diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 10a41ac192..886d3b87d8 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -232,7 +232,7 @@ public static Rune MakePrintable (Rune c) /// The current attribute the driver is using. /// public virtual Attribute CurrentAttribute { - get => currentAttribute; + get => _currentAttribute; set { if (!value.Initialized && value.HasValidColors && Application.Driver != null) { CurrentAttribute = Application.Driver.MakeAttribute (value.Foreground, value.Background); @@ -240,7 +240,7 @@ public virtual Attribute CurrentAttribute { } if (!value.Initialized) Debug.WriteLine ("ConsoleDriver.CurrentAttribute: Attributes must be initialized before use."); - currentAttribute = value; + _currentAttribute = value; } } @@ -255,24 +255,7 @@ public virtual void SetAttribute (Attribute c) { CurrentAttribute = c; } - - ///// - ///// Set Colors from limit sets of colors. Not implemented by any driver: See Issue #2300. - ///// - ///// Foreground. - ///// Background. - //public abstract void SetColors (ConsoleColor foreground, ConsoleColor background); - - //// Advanced uses - set colors to any pre-set pairs, you would need to init_color - //// that independently with the R, G, B values. - ///// - ///// Advanced uses - set colors to any pre-set pairs, you would need to init_color - ///// that independently with the R, G, B values. Not implemented by any driver: See Issue #2300. - ///// - ///// Foreground color identifier. - ///// Background color identifier. - //public abstract void SetColors (short foregroundColorId, short backgroundColorId); - + /// /// Gets the foreground and background colors based on the value. /// @@ -343,8 +326,9 @@ public enum DiagnosticFlags : uint { public static DiagnosticFlags Diagnostics { get; set; } /// - /// Suspend the application, typically needs to save the state, suspend the app and upon return, reset the console driver. + /// Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver. /// + /// This is only implemented in . public abstract void Suspend (); /// @@ -436,19 +420,8 @@ public void ClearClipRegion () /// Stop reporting mouses moves. /// public abstract void StopReportingMouseMoves (); - - ///// - ///// Disables the cooked event processing from the mouse driver. - ///// At startup, it is assumed mouse events are cooked. Not implemented by any driver: See Issue #2300. - ///// - //public abstract void UncookMouse (); - - ///// - ///// Enables the cooked event processing from the mouse driver. Not implemented by any driver: See Issue #2300. - ///// - //public abstract void CookMouse (); - - private Attribute currentAttribute; + + Attribute _currentAttribute; /// /// Make the attribute for the foreground and background colors. @@ -456,7 +429,10 @@ public void ClearClipRegion () /// Foreground. /// Background. /// - public abstract Attribute MakeAttribute (Color fore, Color back); + public virtual Attribute MakeAttribute (Color fore, Color back) + { + return MakeColor (fore, back); + } /// /// Gets the current . @@ -476,27 +452,13 @@ public void ClearClipRegion () /// Ensures all s in are correctly /// initialized by the driver. /// - /// - /// This method was previsouly named CreateColors. It was reanmed to InitalizeColorSchemes when - /// was enabled. - /// /// Flag indicating if colors are supported (not used). - public void InitalizeColorSchemes (bool supportsColors = true) + public void InitializeColorSchemes (bool supportsColors = true) { // Ensure all Attributes are initialized by the driver foreach (var s in Colors.ColorSchemes) { s.Value.Initialize (); } - - if (!supportsColors) { - return; - } - - } - - internal void SetAttribute (object attribute) - { - throw new NotImplementedException (); } } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 31ccbe7c32..449054a1da 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -597,9 +597,9 @@ public override void Init (Action terminalResized) Curses.StartColor (); Curses.UseDefaultColors (); - InitalizeColorSchemes (); + InitializeColorSchemes (); } else { - InitalizeColorSchemes (false); + InitializeColorSchemes (false); // BUGBUG: This is a hack to make the colors work on the Mac? // The new Theme support overwrites these colors, so this is not needed? @@ -751,9 +751,7 @@ static Color MapCursesColor (int color) public override Attribute MakeAttribute (Color fore, Color back) { - var f = MapColor (fore); - //return MakeColor ((short)(f & 0xffff), (short)MapColor (back)) | ((f & Curses.A_BOLD) != 0 ? Curses.A_BOLD : 0); - return MakeColor ((short)(f & 0xffff), (short)MapColor (back)); + return MakeColor ((short)(MapColor (fore) & 0xffff), (short)MapColor (back)); } public override void Suspend () diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index a32bb60164..2f525ee968 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -228,17 +228,12 @@ public override void Init (Action terminalResized) _rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; FakeConsole.Clear (); ResizeScreen (); - // Call InitalizeColorSchemes before UpdateOffScreen as it references Colors + // Call InitializeColorSchemes before UpdateOffScreen as it references Colors CurrentAttribute = MakeColor (Color.White, Color.Black); - InitalizeColorSchemes (); + InitializeColorSchemes (); UpdateOffScreen (); } - public override Attribute MakeAttribute (Color fore, Color back) - { - return MakeColor ((ConsoleColor)fore, (ConsoleColor)back); - } - int _redrawColor = -1; void SetColor (int color) { @@ -304,11 +299,6 @@ public override void Refresh () UpdateCursor (); } - public override void SetAttribute (Attribute c) - { - base.SetAttribute (c); - } - public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) { if (consoleKeyInfo.Key != ConsoleKey.Packet) { @@ -647,7 +637,6 @@ public override bool GetColors (int value, out Color foreground, out Color backg return hasColor; } - #region Unused public override void UpdateCursor () { if (!EnsureCursorVisibility ()) @@ -663,19 +652,21 @@ public override void UpdateCursor () } } + #region Not Implemented public override void StartReportingMouseMoves () { + throw new NotImplementedException (); } public override void StopReportingMouseMoves () { + throw new NotImplementedException (); } public override void Suspend () { + throw new NotImplementedException (); } - - #endregion public class FakeClipboard : ClipboardBase { diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index ffbd31a2b5..98bdce4dcf 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -793,10 +793,10 @@ public override void Init (Action terminalResized) _rows = _largestBufferHeight; CurrentAttribute = MakeColor (Color.White, Color.Black); - InitalizeColorSchemes (); + InitializeColorSchemes (); CurrentAttribute = MakeColor (Color.White, Color.Black); - InitalizeColorSchemes (); + InitializeColorSchemes (); ResizeScreen (); UpdateOffScreen (); @@ -874,12 +874,7 @@ public override void UpdateOffScreen () } catch (IndexOutOfRangeException) { } } } - - public override Attribute MakeAttribute (Color fore, Color back) - { - return MakeColor ((ConsoleColor)fore, (ConsoleColor)back); - } - + public override void Refresh () { UpdateScreen (); @@ -1105,14 +1100,12 @@ public override void StopReportingMouseMoves () Console.Out.Write (EscSeqUtils.DisableMouseEvents); } + #region Not Implemented public override void Suspend () { + throw new NotImplementedException (); } - - public override void SetAttribute (Attribute c) - { - base.SetAttribute (c); - } + #endregion public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) { diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index bc02960f40..94335a18f8 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1472,10 +1472,10 @@ public override void Init (Action terminalResized) WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); CurrentAttribute = MakeColor (Color.White, Color.Black); - InitalizeColorSchemes (); + InitializeColorSchemes (); CurrentAttribute = MakeColor (Color.White, Color.Black); - InitalizeColorSchemes (); + InitializeColorSchemes (); ResizeScreen (); UpdateOffScreen (); @@ -1616,11 +1616,6 @@ public override void AddStr (ustring str) } } - public override void SetAttribute (Attribute c) - { - base.SetAttribute (c); - } - public override Attribute MakeColor (Color foreground, Color background) { return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); @@ -1636,11 +1631,6 @@ Attribute MakeColor (ConsoleColor f, ConsoleColor b) ); } - public override Attribute MakeAttribute (Color fore, Color back) - { - return MakeColor ((ConsoleColor)fore, (ConsoleColor)back); - } - public override void Refresh () { UpdateScreen (); @@ -1825,19 +1815,21 @@ public override bool GetColors (int value, out Color foreground, out Color backg return hasColor; } - #region Unused + #region Not Implemented public override void Suspend () { + throw new NotImplementedException (); } public override void StartReportingMouseMoves () { + throw new NotImplementedException (); } public override void StopReportingMouseMoves () { + throw new NotImplementedException (); } - #endregion } diff --git a/Terminal.Gui/Input/Command.cs b/Terminal.Gui/Input/Command.cs index 29de444eea..b6ce90623c 100644 --- a/Terminal.Gui/Input/Command.cs +++ b/Terminal.Gui/Input/Command.cs @@ -340,7 +340,7 @@ public enum Command { QuitToplevel, /// - /// Suspend a application (used on Linux). + /// Suspend a application (Only implemented in ). /// Suspend, diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs index c305fd7994..de9a32fbe1 100644 --- a/Terminal.Gui/Views/TileView.cs +++ b/Terminal.Gui/Views/TileView.cs @@ -989,7 +989,7 @@ public override bool MouseEvent (MouseEvent mouseEvent) // End Drag Application.UngrabMouse (); - Driver.UncookMouse (); + //Driver.UncookMouse (); FinalisePosition ( dragOrignalPos, Orientation == Orientation.Horizontal ? Y : X); diff --git a/UnitTests/ConsoleDrivers/AttributeTests.cs b/UnitTests/ConsoleDrivers/AttributeTests.cs index f2130f220d..a02b0846f1 100644 --- a/UnitTests/ConsoleDrivers/AttributeTests.cs +++ b/UnitTests/ConsoleDrivers/AttributeTests.cs @@ -185,8 +185,6 @@ public void Get_Gets () var attr = new Attribute (value, fg, bg); - driver.SetAttribute (attr); - var ret_attr = Attribute.Get (); Assert.Equal (value, ret_attr.Value); From b688d8dab33f296065378e63b97891c047f68539 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 08:51:33 +0200 Subject: [PATCH 07/99] Code cleanup and Removes unused ConsoleDriver APIs --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 886d3b87d8..720057f646 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -356,10 +356,12 @@ public Rect Clip { return new Rect (0, 0, Cols, Rows); } - int minX = ClipRegion.Min (rect => rect.X); - int minY = ClipRegion.Min (rect => rect.Y); - int maxX = ClipRegion.Max (rect => rect.X + rect.Width); - int maxY = ClipRegion.Max (rect => rect.Y + rect.Height); + // BUGBUG: ? This means that if you have a clip region of 0,0,10,10 and 20,20,10,10 + // BUGBUG: ? the Clip will be 0,0,30,30. Is this the desired behavior? + var minX = ClipRegion.Min (rect => rect.X); + var minY = ClipRegion.Min (rect => rect.Y); + var maxX = ClipRegion.Max (rect => rect.X + rect.Width); + var maxY = ClipRegion.Max (rect => rect.Y + rect.Height); return new Rect (minX, minY, maxX - minX, maxY - minY); } @@ -369,18 +371,13 @@ public Rect Clip { } } - List _clipRegion = new List (); - /// /// The clipping region that and are /// subject to. The clip region is an irregularly shaped area defined by the intersection of a set /// of rectangles added with . To clear the clip region call . /// /// The clip. - public List ClipRegion { - get => _clipRegion; - set => _clipRegion = value; - } + public List ClipRegion { get; set; } = new List (); /// /// Expands to include . From 693c4ad43c71ee80a4b957b815e7c72e75be1edc Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 12:51:38 +0200 Subject: [PATCH 08/99] Work around https://github.com/gui-cs/Terminal.Gui/issues/2610 --- .../CursesDriver/CursesDriver.cs | 48 +++++------ .../ConsoleDrivers/FakeDriver/FakeConsole.cs | 1 + .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 45 ++-------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 27 ++---- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 84 ++++++++++--------- Terminal.Gui/Drawing/Color.cs | 3 +- Terminal.Gui/Resources/config.json | 1 - UnitTests/ConsoleDrivers/AttributeTests.cs | 2 + UnitTests/Text/UnicodeTests.cs | 10 +-- 9 files changed, 97 insertions(+), 124 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 449054a1da..915e327adc 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -3,6 +3,7 @@ // using System; using System.Collections.Generic; +using System.ComponentModel.Design.Serialization; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -37,16 +38,20 @@ public override void Move (int col, int row) _ccol = col; _crow = row; - if (Clip.Contains (col, row)) { + if (IsValidLocation (col, row)) { Curses.move (row, col); _needMove = false; } else { + // Not a valid location (outside screen or clip region) + // We need to move the cursor to within clip region, and then + // later move it to the desired location in AddStr + // BUGBUG: Clip.X/Y may actually be outside of the defined clip region rects as + // currently implemented. Curses.move (Clip.Y, Clip.X); _needMove = true; } } - static bool _sync = false; public override void AddRune (Rune rune) { rune = MakePrintable (rune); @@ -55,49 +60,48 @@ public override void AddRune (Rune rune) if (validLocation) { if (_needMove) { + // Move was called, wanting to put cursor in an invalid location. + // We need to move the cursor to the clip region, and then + // move it to the desired location. Curses.move (_crow, _ccol); _needMove = false; } if (runeWidth == 0 && _ccol > 0) { var r = _contents [_crow, _ccol - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); - string sn; - if (!s.IsNormalized ()) { - sn = s.Normalize (); - } else { - sn = s; - } + var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - Curses.mvaddch (_crow, _ccol - 1, (int)(uint)c); _contents [_crow, _ccol - 1, 0] = c; _contents [_crow, _ccol - 1, 1] = CurrentAttribute; _contents [_crow, _ccol - 1, 2] = 1; + Curses.mvaddch (_crow, _ccol - 1, c); + } else { if (runeWidth < 2 && _ccol > 0 && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { - var curAtttib = CurrentAttribute; + var curAttr = CurrentAttribute; Curses.attrset (_contents [_crow, _ccol - 1, 1]); - Curses.mvaddch (_crow, _ccol - 1, (int)(uint)' '); - _contents [_crow, _ccol - 1, 0] = (int)(uint)' '; + Curses.mvaddch (_crow, _ccol - 1, System.Text.Rune.ReplacementChar.Value); + _contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; Curses.move (_crow, _ccol); - Curses.attrset (curAtttib); + Curses.attrset (curAttr); } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { - var curAtttib = CurrentAttribute; + var curAttr = CurrentAttribute; Curses.attrset (_contents [_crow, _ccol + 1, 1]); - Curses.mvaddch (_crow, _ccol + 1, (int)(uint)' '); - _contents [_crow, _ccol + 1, 0] = (int)(uint)' '; + Curses.mvaddch (_crow, _ccol + 1, System.Text.Rune.ReplacementChar.Value); + _contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; Curses.move (_crow, _ccol); - Curses.attrset (curAtttib); + Curses.attrset (curAttr); } if (runeWidth > 1 && _ccol == Clip.Right - 1) { - Curses.addch ((int)(uint)' '); - _contents [_crow, _ccol, 0] = (int)(uint)' '; + Curses.addch (System.Text.Rune.ReplacementChar.Value); + _contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; } else { Curses.addch ((int)(uint)rune); _contents [_crow, _ccol, 0] = (int)(uint)rune; @@ -109,7 +113,7 @@ public override void AddRune (Rune rune) _needMove = true; } - if (runeWidth < 0 || runeWidth > 0) { + if (runeWidth is < 0 or > 0) { _ccol++; } @@ -120,10 +124,6 @@ public override void AddRune (Rune rune) } _ccol++; } - - if (_sync) { - UpdateScreen (); - } } public override void AddStr (ustring str) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs index 75a58f9c32..b4fc5fc0b0 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs @@ -235,6 +235,7 @@ public static class FakeConsole { /// /// public static int BufferWidth { get; set; } = WIDTH; + // // Summary: // Gets or sets the height of the console window area. diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 2f525ee968..7062f312c2 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -96,7 +96,6 @@ public FakeDriver () } } - bool _needMove; // Current row, and current col, tracked by Move/AddCh only int _ccol, _crow; public override void Move (int col, int row) @@ -107,11 +106,6 @@ public override void Move (int col, int row) if (Clip.Contains (col, row)) { FakeConsole.CursorTop = row; FakeConsole.CursorLeft = col; - _needMove = false; - } else { - FakeConsole.CursorTop = Clip.Y; - FakeConsole.CursorLeft = Clip.X; - _needMove = true; } } @@ -122,50 +116,36 @@ public override void AddRune (Rune rune) var validLocation = IsValidLocation (_ccol, _crow); if (validLocation) { - if (_needMove) { - //MockConsole.CursorLeft = ccol; - //MockConsole.CursorTop = crow; - _needMove = false; - } if (runeWidth == 0 && _ccol > 0) { var r = _contents [_crow, _ccol - 1, 0]; - var s = new string (new char [] { (char)r, (char)rune }); - string sn; - if (!s.IsNormalized ()) { - sn = s.Normalize (); - } else { - sn = s; - } + var s = new string (new [] { (char)r, (char)rune }); + var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; _contents [_crow, _ccol - 1, 0] = c; _contents [_crow, _ccol - 1, 1] = CurrentAttribute; _contents [_crow, _ccol - 1, 2] = 1; - } else { if (runeWidth < 2 && _ccol > 0 - && Rune.ColumnWidth ((Rune)_contents [_crow, _ccol - 1, 0]) > 1) { + && Rune.ColumnWidth (_contents [_crow, _ccol - 1, 0]) > 1) { - _contents [_crow, _ccol - 1, 0] = (int)(uint)' '; + _contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((Rune)_contents [_crow, _ccol, 0]) > 1) { + && Rune.ColumnWidth (_contents [_crow, _ccol, 0]) > 1) { - _contents [_crow, _ccol + 1, 0] = (int)(uint)' '; + _contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; _contents [_crow, _ccol + 1, 2] = 1; } if (runeWidth > 1 && _ccol == Clip.Right - 1) { - _contents [_crow, _ccol, 0] = (int)(uint)' '; + _contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; } else { _contents [_crow, _ccol, 0] = (int)(uint)rune; } _contents [_crow, _ccol, 1] = CurrentAttribute; _contents [_crow, _ccol, 2] = 1; - _dirtyLine [_crow] = true; } - } else { - _needMove = true; } if (runeWidth < 0 || runeWidth > 0) { @@ -179,15 +159,6 @@ public override void AddRune (Rune rune) } _ccol++; } - - //if (ccol == Cols) { - // ccol = 0; - // if (crow + 1 < Rows) - // crow++; - //} - if (_sync) { - UpdateScreen (); - } } public override void AddStr (ustring str) @@ -248,7 +219,7 @@ void SetColor (int color) FakeConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff); } } - + public override void UpdateScreen () { int top = Top; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 98bdce4dcf..c1e8c23c60 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -652,11 +652,8 @@ public NetDriver () int [,,] _contents; bool [] _dirtyLine; - static bool _sync = false; - // Current row, and current col, tracked by Move/AddCh only int _ccol, _crow; - public override void Move (int col, int row) { _ccol = col; @@ -666,42 +663,40 @@ public override void Move (int col, int row) public override void AddRune (Rune rune) { if (_contents.Length != Rows * Cols * 3) { + // BUGBUG: Shouldn't this throw an exception? Doing so to see what happens + throw new InvalidOperationException ("Driver contents are wrong size"); return; } + rune = MakePrintable (rune); var runeWidth = Rune.ColumnWidth (rune); var validLocation = IsValidLocation (_ccol, _crow); if (validLocation) { if (runeWidth == 0 && _ccol > 0) { + // Single column glyph beyond first col var r = _contents [_crow, _ccol - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); - string sn; - if (!s.IsNormalized ()) { - sn = s.Normalize (); - } else { - sn = s; - } + var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; _contents [_crow, _ccol - 1, 0] = c; _contents [_crow, _ccol - 1, 1] = CurrentAttribute; _contents [_crow, _ccol - 1, 2] = 1; - } else { if (runeWidth < 2 && _ccol > 0 && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { - _contents [_crow, _ccol - 1, 0] = (int)(uint)' '; + _contents [_crow, _ccol - 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { - _contents [_crow, _ccol + 1, 0] = (int)(uint)' '; + _contents [_crow, _ccol + 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; _contents [_crow, _ccol + 1, 2] = 1; } if (runeWidth > 1 && _ccol == Clip.Right - 1) { - _contents [_crow, _ccol, 0] = (int)(uint)' '; + _contents [_crow, _ccol, 0] = (char)System.Text.Rune.ReplacementChar.Value; } else { _contents [_crow, _ccol, 0] = (int)(uint)rune; } @@ -723,10 +718,6 @@ public override void AddRune (Rune rune) } _ccol++; } - - if (_sync) { - UpdateScreen (); - } } public override void AddStr (ustring str) @@ -874,7 +865,7 @@ public override void UpdateOffScreen () } catch (IndexOutOfRangeException) { } } } - + public override void Refresh () { UpdateScreen (); diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 94335a18f8..1e4f536ea1 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1521,6 +1521,7 @@ public override void UpdateOffScreen () } } + // Current row, and current col, tracked by Move/AddCh only int _ccol, _crow; public override void Move (int col, int row) { @@ -1533,57 +1534,68 @@ int GetOutputBufferPosition () return _crow * Cols + _ccol; } - public override void AddRune (Rune rune) + public override void AddRune (Rune nStackrune) { - rune = MakePrintable (rune); - var runeWidth = Rune.ColumnWidth (rune); + // BUGBUG: https://github.com/gui-cs/Terminal.Gui/issues/2610 + System.Text.Rune rune = (System.Text.Rune)MakePrintable (nStackrune).Value; + var runeWidth = Rune.ColumnWidth (nStackrune); var position = GetOutputBufferPosition (); var validLocation = IsValidLocation (_ccol, _crow); if (validLocation) { if (runeWidth == 0 && _ccol > 0) { + // Non spacing glyph beyond first col var r = _contents [_crow, _ccol - 1, 0]; - var s = new string (new char [] { (char)r, (char)rune }); - string sn; - if (!s.IsNormalized ()) { - sn = s.Normalize (); - } else { - sn = s; - } + var s = new string (new char [] { (char)r, (char)rune.Value }); + var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - var prevPosition = _crow * Cols + (_ccol - 1); - _outputBuffer [prevPosition].Char.UnicodeChar = c; _contents [_crow, _ccol - 1, 0] = c; - _outputBuffer [prevPosition].Attributes = (ushort)CurrentAttribute; _contents [_crow, _ccol - 1, 1] = CurrentAttribute; _contents [_crow, _ccol - 1, 2] = 1; + + var prevPosition = _crow * Cols + (_ccol - 1); + _outputBuffer [prevPosition].Char.UnicodeChar = c; + _outputBuffer [prevPosition].Attributes = (ushort)CurrentAttribute; WindowsConsole.SmallRect.Update (ref _damageRegion, (short)(_ccol - 1), (short)_crow); } else { - if (runeWidth < 2 && _ccol > 0 - && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { - - var prevPosition = _crow * Cols + (_ccol - 1); - _outputBuffer [prevPosition].Char.UnicodeChar = ' '; - _contents [_crow, _ccol - 1, 0] = (int)(uint)' '; - - } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { - - var prevPosition = GetOutputBufferPosition () + 1; - _outputBuffer [prevPosition].Char.UnicodeChar = (char)' '; - _contents [_crow, _ccol + 1, 0] = (int)(uint)' '; - + if (runeWidth < 2) { + // Single column glyph + if (_ccol > 0 && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { + // glyph to left is wide; nuke it + // BUGBUG: Does this make sense? + _contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; + var prevPosition = _crow * Cols + (_ccol - 1); + _outputBuffer [prevPosition].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; + } else if (_ccol <= Clip.Right - 1 && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { + // we're just inside the clip rect and the glyph there is wide + // nuke the existing glyph to right + _contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; + var prevPosition = GetOutputBufferPosition () + 1; + _outputBuffer [prevPosition].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; + } } + if (runeWidth > 1 && _ccol == Clip.Right - 1) { - _outputBuffer [position].Char.UnicodeChar = (char)' '; - _contents [_crow, _ccol, 0] = (int)(uint)' '; + // Wide glyph and we're just inside the clip rect + // ignore it + // BUGBUG: Shouldn't we write the unknown glyph glyph? + _contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; + _outputBuffer [position].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; } else { - _outputBuffer [position].Char.UnicodeChar = (char)rune; - _contents [_crow, _ccol, 0] = (int)(uint)rune; + // Add the glyph (wide or not) + if (rune.IsBmp) { + _contents [_crow, _ccol, 0] = rune.Value; + _outputBuffer [position].Char.UnicodeChar = (char) rune.Value; + } else { + _contents [_crow, _ccol, 0] = (char)System.Text.Rune.ReplacementChar.Value; + _outputBuffer [position].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; + + } } - _outputBuffer [position].Attributes = (ushort)CurrentAttribute; _contents [_crow, _ccol, 1] = CurrentAttribute; _contents [_crow, _ccol, 2] = 1; + + _outputBuffer [position].Attributes = (ushort)CurrentAttribute; WindowsConsole.SmallRect.Update (ref _damageRegion, (short)_ccol, (short)_crow); } } @@ -1593,20 +1605,16 @@ public override void AddRune (Rune rune) } if (runeWidth > 1) { - if (validLocation && _ccol < Clip.Right) { + if (rune.IsBmp && _ccol < Clip.Right) { position = GetOutputBufferPosition (); _outputBuffer [position].Attributes = (ushort)CurrentAttribute; _outputBuffer [position].Char.UnicodeChar = (char)0x00; - _contents [_crow, _ccol, 0] = (int)(uint)0x00; + _contents [_crow, _ccol, 0] = (char)0x00; _contents [_crow, _ccol, 1] = CurrentAttribute; _contents [_crow, _ccol, 2] = 0; } _ccol++; } - - if (_sync) { - UpdateScreen (); - } } public override void AddStr (ustring str) diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs index c97e54117f..6c6d78fa55 100644 --- a/Terminal.Gui/Drawing/Color.cs +++ b/Terminal.Gui/Drawing/Color.cs @@ -288,8 +288,9 @@ public static Attribute Make (Color foreground, Color background) /// The current attribute. public static Attribute Get () { - if (Application.Driver == null) + if (Application.Driver == null) { throw new InvalidOperationException ("The Application has not been initialized"); + } return Application.Driver.GetAttribute (); } diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json index af1932937f..f633bdbb43 100644 --- a/Terminal.Gui/Resources/config.json +++ b/Terminal.Gui/Resources/config.json @@ -30,7 +30,6 @@ "Ctrl" ] }, - "Application.UseSystemConsole": false, "Application.IsMouseDisabled": false, "Theme": "Default", "Themes": [ diff --git a/UnitTests/ConsoleDrivers/AttributeTests.cs b/UnitTests/ConsoleDrivers/AttributeTests.cs index a02b0846f1..c94cabd233 100644 --- a/UnitTests/ConsoleDrivers/AttributeTests.cs +++ b/UnitTests/ConsoleDrivers/AttributeTests.cs @@ -185,6 +185,8 @@ public void Get_Gets () var attr = new Attribute (value, fg, bg); + driver.SetAttribute(attr); + var ret_attr = Attribute.Get (); Assert.Equal (value, ret_attr.Value); diff --git a/UnitTests/Text/UnicodeTests.cs b/UnitTests/Text/UnicodeTests.cs index 09aac7fd6d..a5c0c46b20 100644 --- a/UnitTests/Text/UnicodeTests.cs +++ b/UnitTests/Text/UnicodeTests.cs @@ -43,7 +43,7 @@ public void MakePrintable_Does_Not_Convert_Ansi_Chars_To_Unicode (uint code) } [Fact, AutoInitShutdown] - public void AddRune_On_Clip_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Space () + public void AddRune_On_Clip_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Replacement () { var tv = new TextView () { Width = Dim.Fill (), @@ -71,10 +71,10 @@ public void AddRune_On_Clip_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_Wit ┌────────────────────────────┐ │これは広いルーンラインです。│ │これは広いルーンラインです。│ -│これは ┌────────────┐ です。│ -│これは │ワイドルーン│ です。│ -│これは │ {CM.Glyphs.LeftBracket} 選ぶ {CM.Glyphs.RightBracket} │ です。│ -│これは └────────────┘ です。│ +│これは�┌────────────┐�です。│ +│これは�│ワイドルーン│�です。│ +│これは�│ {CM.Glyphs.LeftBracket} 選ぶ {CM.Glyphs.RightBracket} │�です。│ +│これは�└────────────┘�です。│ │これは広いルーンラインです。│ │これは広いルーンラインです。│ └────────────────────────────┘ From 4658198c4f865756f0abda2a47f9e81ef13cf239 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 13:20:42 +0200 Subject: [PATCH 09/99] adjusted unit tests --- UnitTests/Configuration/ConfigurationMangerTests.cs | 7 ------- UnitTests/Configuration/SettingsScopeTests.cs | 3 --- 2 files changed, 10 deletions(-) diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index 7390d6092c..8024941d88 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -224,7 +224,6 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q; ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; - ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Apply (); @@ -233,7 +232,6 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () Assert.Equal (Key.Q, Application.QuitKey); Assert.Equal (Key.F, Application.AlternateForwardKey); Assert.Equal (Key.B, Application.AlternateBackwardKey); - Assert.True (Application.UseSystemConsole); Assert.True (Application.IsMouseDisabled); Assert.True (Application.EnableConsoleScrolling); @@ -246,7 +244,6 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () Assert.Equal (Key.Q | Key.CtrlMask, Application.QuitKey); Assert.Equal (Key.PageDown | Key.CtrlMask, Application.AlternateForwardKey); Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey); - Assert.False (Application.UseSystemConsole); Assert.False (Application.IsMouseDisabled); Assert.False (Application.EnableConsoleScrolling); @@ -254,7 +251,6 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q; ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; - ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Apply (); @@ -271,7 +267,6 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () Assert.Equal (Key.Q | Key.CtrlMask, Application.QuitKey); Assert.Equal (Key.PageDown | Key.CtrlMask, Application.AlternateForwardKey); Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey); - Assert.False (Application.UseSystemConsole); Assert.False (Application.IsMouseDisabled); Assert.False (Application.EnableConsoleScrolling); @@ -764,7 +759,6 @@ public void Load_FiresUpdated () ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q; ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; - ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; @@ -777,7 +771,6 @@ void ConfigurationManager_Updated (object sender, ConfigurationManagerEventArgs Assert.Equal (Key.Q | Key.CtrlMask, ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue); Assert.Equal (Key.PageDown | Key.CtrlMask, ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue); Assert.Equal (Key.PageUp | Key.CtrlMask, ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue); - Assert.False ((bool)ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue); } diff --git a/UnitTests/Configuration/SettingsScopeTests.cs b/UnitTests/Configuration/SettingsScopeTests.cs index 06777daf18..0ca8505ae6 100644 --- a/UnitTests/Configuration/SettingsScopeTests.cs +++ b/UnitTests/Configuration/SettingsScopeTests.cs @@ -43,7 +43,6 @@ public void Apply_ShouldApplyProperties () Assert.Equal (Key.Q | Key.CtrlMask, (Key)ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue); Assert.Equal (Key.PageDown | Key.CtrlMask, (Key)ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue); Assert.Equal (Key.PageUp | Key.CtrlMask, (Key)ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue); - Assert.False ((bool)ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue); @@ -51,7 +50,6 @@ public void Apply_ShouldApplyProperties () ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q; ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; - ConfigurationManager.Settings ["Application.UseSystemConsole"].PropertyValue = true; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; @@ -61,7 +59,6 @@ public void Apply_ShouldApplyProperties () Assert.Equal (Key.Q, Application.QuitKey); Assert.Equal (Key.F, Application.AlternateForwardKey); Assert.Equal (Key.B, Application.AlternateBackwardKey); - Assert.True (Application.UseSystemConsole); Assert.True (Application.IsMouseDisabled); Assert.True (Application.EnableConsoleScrolling); } From 5c79ba68ede36c460f25c2e74fcd1f5af9d04778 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 21:10:08 +0200 Subject: [PATCH 10/99] initial commit --- Terminal.Gui/Application.cs | 2 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 70 ++------- .../CursesDriver/CursesDriver.cs | 2 +- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 5 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 10 +- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 3 +- Terminal.Gui/View/ViewDrawing.cs | 4 +- Terminal.Gui/Views/Menu.cs | 3 +- UnitTests/ConsoleDrivers/ClipRegionTests.cs | 133 +----------------- 9 files changed, 23 insertions(+), 209 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index a1850526ab..5a5667aee5 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -1086,7 +1086,7 @@ static void OnTerminalResized () { var full = new Rect (0, 0, Driver.Cols, Driver.Rows); TerminalResized?.Invoke (new ResizedEventArgs () { Cols = full.Width, Rows = full.Height }); - Driver.ClearClipRegion (); + Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows); foreach (var t in _toplevels) { t.SetRelativeLayout (full); t.LayoutSubviews (); diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 720057f646..35b6d65f1f 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -331,82 +331,30 @@ public enum DiagnosticFlags : uint { /// This is only implemented in . public abstract void Suspend (); + Rect _clip; + /// /// Tests whether the specified coordinate are valid for drawing. /// /// The column. /// The row. /// if the coordinate is outside of the - /// screen bounds or outside either or - /// . otherwise. + /// screen bounds or outside of . otherwise. public bool IsValidLocation (int col, int row) => col >= 0 && row >= 0 && col < Cols && row < Rows && - IsInClipRegion (row, col); + Clip.Contains (col, row); /// /// Gets or sets the clip rectangle that and are - /// subject to. Setting this property is equivalent to calling - /// and . + /// subject to. /// - /// The rectangle describing the bounds of . + /// The rectangle describing the bounds of . public Rect Clip { - get { - if (ClipRegion.Count == 0) { - return new Rect (0, 0, Cols, Rows); - } - - // BUGBUG: ? This means that if you have a clip region of 0,0,10,10 and 20,20,10,10 - // BUGBUG: ? the Clip will be 0,0,30,30. Is this the desired behavior? - var minX = ClipRegion.Min (rect => rect.X); - var minY = ClipRegion.Min (rect => rect.Y); - var maxX = ClipRegion.Max (rect => rect.X + rect.Width); - var maxY = ClipRegion.Max (rect => rect.Y + rect.Height); - - return new Rect (minX, minY, maxX - minX, maxY - minY); - } - set { - ClearClipRegion (); - AddToClipRegion (value); - } + get => _clip; + set => _clip = value; } - - /// - /// The clipping region that and are - /// subject to. The clip region is an irregularly shaped area defined by the intersection of a set - /// of rectangles added with . To clear the clip region call . - /// - /// The clip. - public List ClipRegion { get; set; } = new List (); - - /// - /// Expands to include . - /// - /// - /// The updated . - public List AddToClipRegion (Rect rect) - { - ClipRegion.Add (rect); - return ClipRegion; - } - - /// - /// Clears the . This has the same effect as expanding the clip - /// region to include the entire screen. - /// - public void ClearClipRegion () - { - ClipRegion.Clear (); - } - - /// - /// Tests if the specified coordinates are within the . - /// - /// - /// - /// if and is - /// within the clip region. - private bool IsInClipRegion (int col, int row) => ClipRegion.Count == 0 || ClipRegion.Any (rect => rect.Contains (row, col)); + /// /// Start of mouse moves. diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 915e327adc..78b2dba06e 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -637,7 +637,7 @@ public override void Init (Action terminalResized) public override void ResizeScreen () { - ClearClipRegion (); + Clip = new Rect (0, 0, Cols, Rows); Curses.refresh (); } diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 7062f312c2..82cbb3ee7f 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -75,8 +75,6 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN // dirtyLine [row] = true; //} - static bool _sync = false; - public FakeDriver () { if (FakeBehaviors.UseFakeClipboard) { @@ -568,7 +566,7 @@ public override void ResizeScreen () } } - ClearClipRegion (); + Clip = new Rect (0, 0, Cols, Rows); } public override void UpdateOffScreen () @@ -587,6 +585,7 @@ public override void UpdateOffScreen () } } } catch (IndexOutOfRangeException) { } + Clip = new Rect (0, 0, Cols, Rows); } public override bool GetColors (int value, out Color foreground, out Color background) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index c1e8c23c60..e8a029abff 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -813,9 +813,9 @@ public override void ResizeScreen () Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 } catch (System.IO.IOException) { - ClearClipRegion (); + Clip = new Rect (0, 0, Cols, Rows); } catch (ArgumentOutOfRangeException) { - ClearClipRegion (); + Clip = new Rect (0, 0, Cols, Rows); } } else { Console.Out.Write ($"\x1b[8;{Rows};{Cols}t"); @@ -834,16 +834,16 @@ public override void ResizeScreen () Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 } catch (System.IO.IOException) { - ClearClipRegion (); + Clip = new Rect (0, 0, Cols, Rows); } catch (ArgumentOutOfRangeException) { - ClearClipRegion (); + Clip = new Rect (0, 0, Cols, Rows); } } } else { Console.Out.Write ($"\x1b[30;{Rows};{Cols}t"); } } - ClearClipRegion (); + Clip = new Rect (0, 0, Cols, Rows); } public override void UpdateOffScreen () diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 1e4f536ea1..09fe68a4ef 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -701,7 +701,6 @@ static extern Coord GetLargestConsoleWindowSize ( } internal class WindowsDriver : ConsoleDriver { - static bool _sync = false; WindowsConsole.CharInfo [] _outputBuffer; int _cols, _rows, _left, _top; WindowsConsole.SmallRect _damageRegion; @@ -1487,7 +1486,7 @@ public override void Init (Action terminalResized) public override void ResizeScreen () { _outputBuffer = new WindowsConsole.CharInfo [Rows * Cols]; - ClearClipRegion (); + Clip = new Rect (0, 0, Cols, Rows); _damageRegion = new WindowsConsole.SmallRect () { Top = 0, Left = 0, diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs index 492bc1f0b1..5dc5236b3c 100644 --- a/Terminal.Gui/View/ViewDrawing.cs +++ b/Terminal.Gui/View/ViewDrawing.cs @@ -312,8 +312,8 @@ public virtual bool OnDrawFrames () // TODO: Figure out what we should do if we have no superview //if (SuperView != null) { // TODO: Clipping is disabled for now to ensure we see errors - Driver.ClearClipRegion ();// screenBounds;// SuperView.ClipToBounds (); - //} + Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows);// screenBounds;// SuperView.ClipToBounds (); + //} // Each of these renders lines to either this View's LineCanvas // Those lines will be finally rendered in OnRenderLineCanvas diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 73916fc369..925f9a2c61 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -572,8 +572,7 @@ public override void OnDrawContent (Rect contentArea) return; } var savedClip = Driver.Clip; - Driver.ClearClipRegion (); - + Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows); Driver.SetAttribute (GetNormalColor ()); OnDrawFrames (); diff --git a/UnitTests/ConsoleDrivers/ClipRegionTests.cs b/UnitTests/ConsoleDrivers/ClipRegionTests.cs index c572a9561b..619b166e0a 100644 --- a/UnitTests/ConsoleDrivers/ClipRegionTests.cs +++ b/UnitTests/ConsoleDrivers/ClipRegionTests.cs @@ -16,38 +16,7 @@ public ClipRegionTests (ITestOutputHelper output) { this.output = output; } - - [Theory] - [InlineData (typeof (FakeDriver))] - public void ClearClipRegion_Means_Screen (Type driverType) - { - var driver = (FakeDriver)Activator.CreateInstance (driverType); - Application.Init (driver); - - Assert.Empty (driver.ClipRegion); - Assert.Equal (new Rect (0, 0, driver.Cols, driver.Rows), driver.Clip); - - // Define a clip rectangle - driver.Clip = new Rect (5, 5, 5, 5); - Assert.NotEmpty (driver.ClipRegion); - Assert.Equal (new Rect (5, 5, 5, 5), driver.Clip); - - driver.ClearClipRegion (); - Assert.Empty (driver.ClipRegion); - Assert.Equal (new Rect (0, 0, driver.Cols, driver.Rows), driver.Clip); - - // Define a clip rectangle - driver.Clip = new Rect (5, 5, 5, 5); - Assert.NotEmpty (driver.ClipRegion); - Assert.Equal (new Rect (5, 5, 5, 5), driver.Clip); - - driver.ClearClipRegion (); - Assert.Empty (driver.ClipRegion); - Assert.Equal (new Rect (0, 0, driver.Cols, driver.Rows), driver.Clip); - - Application.Shutdown (); - } - + [Theory] [InlineData (typeof (FakeDriver))] public void IsValidLocation (Type driverType) @@ -96,7 +65,6 @@ public void Clip_Set_To_Empty_AllInvalid (Type driverType) // Define a clip rectangle driver.Clip = Rect.Empty; - Assert.NotEmpty (driver.ClipRegion); // negative Assert.False (driver.IsValidLocation (4, 5)); @@ -113,80 +81,6 @@ public void Clip_Set_To_Empty_AllInvalid (Type driverType) Application.Shutdown (); } - [Theory] - [InlineData (typeof (FakeDriver))] - public void AddClipRegion_Expands (Type driverType) - { - var driver = (FakeDriver)Activator.CreateInstance (driverType); - Application.Init (driver); - - Assert.Empty (driver.ClipRegion); - - // Setup the region with a single rectangle - driver.AddToClipRegion (new Rect (5, 5, 5, 5)); - Assert.NotEmpty (driver.ClipRegion); - Assert.Equal (new Rect (5, 5, 5, 5), driver.Clip); - - // positive - Assert.True (driver.IsValidLocation (5, 5)); - Assert.True (driver.IsValidLocation (9, 9)); - // negative - Assert.False (driver.IsValidLocation (4, 5)); - Assert.False (driver.IsValidLocation (5, 4)); - Assert.False (driver.IsValidLocation (10, 9)); - Assert.False (driver.IsValidLocation (9, 10)); - Assert.False (driver.IsValidLocation (-1, 0)); - Assert.False (driver.IsValidLocation (0, -1)); - Assert.False (driver.IsValidLocation (-1, -1)); - Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows)); - - // Add a second, disjoint, rectangle to the region. This should expand the region - // to include both rectangles. maxX before was 9. The far right side of the new - // rect is 9 + 5 = 14. - driver.AddToClipRegion (new Rect (10, 10, 5, 5)); - Assert.NotEmpty (driver.ClipRegion); - Assert.Equal (new Rect (5, 5, 10, 10), driver.Clip); - - // positive - Assert.True (driver.IsValidLocation (5, 5)); - Assert.True (driver.IsValidLocation (9, 9)); - Assert.True (driver.IsValidLocation (10, 10)); - Assert.True (driver.IsValidLocation (14, 14)); - // negative - Assert.False (driver.IsValidLocation (4, 5)); - Assert.False (driver.IsValidLocation (5, 4)); - Assert.False (driver.IsValidLocation (10, 9)); - Assert.False (driver.IsValidLocation (9, 10)); - Assert.False (driver.IsValidLocation (-1, 0)); - Assert.False (driver.IsValidLocation (0, -1)); - Assert.False (driver.IsValidLocation (-1, -1)); - Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows - 1)); - Assert.False (driver.IsValidLocation (driver.Cols, driver.Rows)); - - // add a third, contiguous with first, rectangle - driver.AddToClipRegion (new Rect (0, 0, 6, 6)); - Assert.NotEmpty (driver.ClipRegion); - Assert.Equal (new Rect (0, 0, 15, 15), driver.Clip); - - // positive - Assert.True (driver.IsValidLocation (0, 5)); - Assert.True (driver.IsValidLocation (4, 4)); - Assert.True (driver.IsValidLocation (5, 5)); - Assert.True (driver.IsValidLocation (9, 9)); - Assert.True (driver.IsValidLocation (10, 10)); - Assert.True (driver.IsValidLocation (14, 14)); - Assert.True (driver.IsValidLocation (7, 7)); - // negative - Assert.False (driver.IsValidLocation (4, 10)); - Assert.False (driver.IsValidLocation (10, 9)); - Assert.False (driver.IsValidLocation (9, 10)); - - Application.Shutdown (); - } - [Theory] [InlineData (typeof (FakeDriver))] public void AddRune_Is_Clipped (Type driverType) @@ -215,31 +109,6 @@ public void AddRune_Is_Clipped (Type driverType) Assert.Equal ('x', (char)driver.Contents [9, 9, 0]); Assert.Equal (' ', (char)driver.Contents [10, 10, 0]); - // Add a second, disjoint, rectangle, fill screen with 'y' - driver.AddToClipRegion (new Rect (10, 10, 5, 5)); - driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), 'y'); - Assert.Equal (' ', (char)driver.Contents [4, 9, 0]); - Assert.Equal ('y', (char)driver.Contents [5, 9, 0]); - Assert.Equal ('y', (char)driver.Contents [5, 5, 0]);// in 1st rect - Assert.Equal ('y', (char)driver.Contents [9, 9, 0]); - Assert.Equal (' ', (char)driver.Contents [0, 0, 0]); - Assert.Equal ('y', (char)driver.Contents [10, 10, 0]); - Assert.Equal ('y', (char)driver.Contents [14, 14, 0]); - Assert.Equal (' ', (char)driver.Contents [15, 15, 0]); - - // add a third, contiguous with first, rectangle, fill screen with 'z' - driver.AddToClipRegion (new Rect (0, 0, 5, 5)); - driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), 'z'); - Assert.Equal ('z', (char)driver.Contents [5, 5, 0]); // in 1st rect - Assert.Equal ('z', (char)driver.Contents [0, 0, 0]); // in 3rd rect - Assert.Equal ('z', (char)driver.Contents [4, 4, 0]); // in 3rd rect - Assert.Equal (' ', (char)driver.Contents [4, 9, 0]); - Assert.Equal ('z', (char)driver.Contents [5, 5, 0]); - Assert.Equal ('z', (char)driver.Contents [9, 9, 0]); // in 2nd rect - Assert.Equal ('z', (char)driver.Contents [10, 10, 0]); - Assert.Equal ('z', (char)driver.Contents [14, 14, 0]); - Assert.Equal (' ', (char)driver.Contents [15, 15, 0]); - Application.Shutdown (); } } From 066a9b942cb65705630fb8354a11d6c72c556c5a Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 21:36:39 +0200 Subject: [PATCH 11/99] Made Rows, Cols, Top, Left virtual --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 10 ++--- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 44 +++++++++---------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 26 +++++------ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 37 +++++++--------- .../ConsoleDrivers/ConsoleScrolllingTests.cs | 2 +- 5 files changed, 54 insertions(+), 65 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 35b6d65f1f..8b8bbb2338 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -78,26 +78,26 @@ public abstract class ConsoleDriver { /// The handler fired when the terminal is resized. /// protected Action TerminalResized; - + /// /// The current number of columns in the terminal. /// - public abstract int Cols { get; } + public virtual int Cols { get; internal set; } /// /// The current number of rows in the terminal. /// - public abstract int Rows { get; } + public virtual int Rows { get; internal set; } /// /// The current left in the terminal. /// - public abstract int Left { get; } + public virtual int Left { get; internal set; } /// /// The current top in the terminal. /// - public abstract int Top { get; } + public virtual int Top { get; internal set; } /// /// Get the operation system clipboard. diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 82cbb3ee7f..d23ce210c9 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -38,12 +38,12 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN public static FakeDriver.Behaviors FakeBehaviors = new Behaviors (); - int _cols, _rows, _left, _top; - public override int Cols => _cols; - public override int Rows => _rows; + //int Cols, Rows;//, Left, Top; + //public override int Cols => Cols; + //public override int Rows => Rows; // Only handling left here because not all terminals has a horizontal scroll bar. - public override int Left => 0; - public override int Top => 0; + //public override int Left => 0; + //public override int Top => 0; public override bool EnableConsoleScrolling { get; set; } private IClipboard _clipboard = null; public override IClipboard Clipboard => _clipboard; @@ -193,8 +193,8 @@ public override void Init (Action terminalResized) TerminalResized = terminalResized; - _cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; - _rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; + Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; + Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; FakeConsole.Clear (); ResizeScreen (); // Call InitializeColorSchemes before UpdateOffScreen as it references Colors @@ -497,8 +497,8 @@ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool al public void SetBufferSize (int width, int height) { FakeConsole.SetBufferSize (width, height); - _cols = width; - _rows = height; + Cols = width; + Rows = height; if (!EnableConsoleScrolling) { SetWindowSize (width, height); } @@ -509,10 +509,10 @@ public void SetWindowSize (int width, int height) { FakeConsole.SetWindowSize (width, height); if (!EnableConsoleScrolling) { - if (width != _cols || height != _rows) { + if (width != Cols || height != Rows) { SetBufferSize (width, height); - _cols = width; - _rows = height; + Cols = width; + Rows = height; } } ProcessResize (); @@ -521,13 +521,13 @@ public void SetWindowSize (int width, int height) public void SetWindowPosition (int left, int top) { if (EnableConsoleScrolling) { - _left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); - _top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); - } else if (_left > 0 || _top > 0) { - _left = 0; - _top = 0; + Left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); + Top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); + } else if (Left > 0 || Top > 0) { + Left = 0; + Top = 0; } - FakeConsole.SetWindowPosition (_left, _top); + FakeConsole.SetWindowPosition (Left, Top); } void ProcessResize () @@ -558,8 +558,8 @@ public override void ResizeScreen () } else { try { #pragma warning disable CA1416 - FakeConsole.WindowLeft = Math.Max (Math.Min (_left, Cols - FakeConsole.WindowWidth), 0); - FakeConsole.WindowTop = Math.Max (Math.Min (_top, Rows - FakeConsole.WindowHeight), 0); + FakeConsole.WindowLeft = Math.Max (Math.Min (Left, Cols - FakeConsole.WindowWidth), 0); + FakeConsole.WindowTop = Math.Max (Math.Min (Top, Rows - FakeConsole.WindowHeight), 0); #pragma warning restore CA1416 } catch (Exception) { return; @@ -576,8 +576,8 @@ public override void UpdateOffScreen () // Can raise an exception while is still resizing. try { - for (int row = 0; row < _rows; row++) { - for (int c = 0; c < _cols; c++) { + for (int row = 0; row < Rows; row++) { + for (int c = 0; c < Cols; c++) { _contents [row, c, 0] = ' '; _contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; _contents [row, c, 2] = 0; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index e8a029abff..e13339898d 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -614,12 +614,6 @@ internal class NetDriver : ConsoleDriver { const int COLOR_BRIGHT_CYAN = 96; const int COLOR_BRIGHT_WHITE = 97; - int _cols, _rows, _left, _top; - - public override int Cols => _cols; - public override int Rows => _rows; - public override int Left => _left; - public override int Top => _top; public override bool EnableConsoleScrolling { get; set; } public NetWinVTConsole NetWinConsole { get; } public bool IsWinPlatform { get; } @@ -780,8 +774,8 @@ public override void Init (Action terminalResized) _largestBufferHeight = Console.WindowHeight; } - _cols = Console.WindowWidth; - _rows = _largestBufferHeight; + Cols = Console.WindowWidth; + Rows = _largestBufferHeight; CurrentAttribute = MakeColor (Color.White, Color.Black); InitializeColorSchemes (); @@ -854,8 +848,8 @@ public override void UpdateOffScreen () lock (_contents) { // Can raise an exception while is still resizing. try { - for (int row = 0; row < _rows; row++) { - for (int c = 0; c < _cols; c++) { + for (int row = 0; row < Rows; row++) { + for (int c = 0; c < Cols; c++) { _contents [row, c, 0] = ' '; _contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; _contents [row, c, 2] = 0; @@ -1048,8 +1042,8 @@ private void SetWindowPosition (int col, int row) } catch (System.ArgumentOutOfRangeException) { } } } - _top = Console.WindowTop; - _left = Console.WindowLeft; + Top = Console.WindowTop; + Left = Console.WindowLeft; } private bool EnsureBufferSize () @@ -1302,10 +1296,10 @@ void ChangeWin (Size size) } else { _largestBufferHeight = Math.Max (size.Height, _largestBufferHeight); } - _top = 0; - _left = 0; - _cols = size.Width; - _rows = _largestBufferHeight; + Top = 0; + Left = 0; + Cols = size.Width; + Rows = _largestBufferHeight; ResizeScreen (); UpdateOffScreen (); winChanging = false; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 09fe68a4ef..89d31e6d26 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -702,15 +702,10 @@ static extern Coord GetLargestConsoleWindowSize ( internal class WindowsDriver : ConsoleDriver { WindowsConsole.CharInfo [] _outputBuffer; - int _cols, _rows, _left, _top; WindowsConsole.SmallRect _damageRegion; IClipboard _clipboard; int [,,] _contents; - public override int Cols => _cols; - public override int Rows => _rows; - public override int Left => _left; - public override int Top => _top; public override bool EnableConsoleScrolling { get; set; } public override IClipboard Clipboard => _clipboard; public override int [,,] Contents => _contents; @@ -748,15 +743,15 @@ private void ChangeWin (Size e) { if (!EnableConsoleScrolling) { var w = e.Width; - if (w == _cols - 3 && e.Height < _rows) { + if (w == Cols - 3 && e.Height < Rows) { w += 3; } var newSize = WinConsole.SetConsoleWindow ( (short)Math.Max (w, 16), (short)Math.Max (e.Height, 0)); - _left = 0; - _top = 0; - _cols = newSize.Width; - _rows = newSize.Height; + Left = 0; + Top = 0; + Cols = newSize.Width; + Rows = newSize.Height; ResizeScreen (); UpdateOffScreen (); TerminalResized.Invoke (); @@ -886,13 +881,13 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) case WindowsConsole.EventType.WindowBufferSize: var winSize = WinConsole.GetConsoleBufferWindow (out Point pos); - _left = pos.X; - _top = pos.Y; - _cols = inputEvent.WindowBufferSizeEvent._size.X; + Left = pos.X; + Top = pos.Y; + Cols = inputEvent.WindowBufferSizeEvent._size.X; if (EnableConsoleScrolling) { - _rows = Math.Max (inputEvent.WindowBufferSizeEvent._size.Y, _rows); + Rows = Math.Max (inputEvent.WindowBufferSizeEvent._size.Y, Rows); } else { - _rows = inputEvent.WindowBufferSizeEvent._size.Y; + Rows = inputEvent.WindowBufferSizeEvent._size.Y; } //System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}"); ResizeScreen (); @@ -1466,8 +1461,8 @@ public override void Init (Action terminalResized) // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); - _cols = winSize.Width; - _rows = winSize.Height; + Cols = winSize.Width; + Rows = winSize.Height; WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); CurrentAttribute = MakeColor (Color.White, Color.Black); @@ -1507,10 +1502,10 @@ public override void ResizeScreen () public override void UpdateOffScreen () { - _contents = new int [_rows, _cols, 3]; - for (int row = 0; row < _rows; row++) { - for (int col = 0; col < _cols; col++) { - int position = row * _cols + col; + _contents = new int [Rows, Cols, 3]; + for (int row = 0; row < Rows; row++) { + for (int col = 0; col < Cols; col++) { + int position = row * Cols + col; _outputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal; _outputBuffer [position].Char.UnicodeChar = ' '; _contents [row, col, 0] = _outputBuffer [position].Char.UnicodeChar; diff --git a/UnitTests/ConsoleDrivers/ConsoleScrolllingTests.cs b/UnitTests/ConsoleDrivers/ConsoleScrolllingTests.cs index c84b8bd25e..6dd461af69 100644 --- a/UnitTests/ConsoleDrivers/ConsoleScrolllingTests.cs +++ b/UnitTests/ConsoleDrivers/ConsoleScrolllingTests.cs @@ -66,7 +66,7 @@ public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_BufferWid Assert.Equal (0, Console.WindowLeft); Assert.Equal (0, Console.WindowTop); - // MockDriver will now be sets to 120x25 + // MockDriver will now be set to 120x25 driver.SetBufferSize (120, 25); Assert.Equal (120, Application.Driver.Cols); Assert.Equal (25, Application.Driver.Rows); From 638e914d91e030510dde0e889b15646f480a2916 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 21:42:54 +0200 Subject: [PATCH 12/99] Made Clipboard non-virtual --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 14 +++++++------- .../CursesDriver/CursesDriver.cs | 8 +++----- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 18 +++++------------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 1 - Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 4 +--- UnitTests/UnitTests.csproj | 1 + 6 files changed, 17 insertions(+), 29 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 8b8bbb2338..6695f1916a 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -78,31 +78,31 @@ public abstract class ConsoleDriver { /// The handler fired when the terminal is resized. /// protected Action TerminalResized; - + /// - /// The current number of columns in the terminal. + /// The number of columns visible in the terminal. /// public virtual int Cols { get; internal set; } /// - /// The current number of rows in the terminal. + /// The number of rows visible in the terminal. /// public virtual int Rows { get; internal set; } /// - /// The current left in the terminal. + /// The leftmost column in the terminal. /// public virtual int Left { get; internal set; } /// - /// The current top in the terminal. + /// The topmost row in the terminal. /// public virtual int Top { get; internal set; } /// - /// Get the operation system clipboard. + /// Get the operating system clipboard. /// - public abstract IClipboard Clipboard { get; } + public IClipboard Clipboard { get; internal set; } /// /// diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 78b2dba06e..282adb48ec 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -21,11 +21,9 @@ internal class CursesDriver : ConsoleDriver { public override int Left => 0; public override int Top => 0; public override bool EnableConsoleScrolling { get; set; } - public override IClipboard Clipboard { get => _clipboard; } CursorVisibility? _initialCursorVisibility = null; CursorVisibility? _currentCursorVisibility = null; - IClipboard _clipboard; int [,,] _contents; public override int [,,] Contents => _contents; @@ -575,12 +573,12 @@ public override void Init (Action terminalResized) } if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - _clipboard = new MacOSXClipboard (); + Clipboard = new MacOSXClipboard (); } else { if (Is_WSL_Platform ()) { - _clipboard = new WSLClipboard (); + Clipboard = new WSLClipboard (); } else { - _clipboard = new CursesClipboard (); + Clipboard = new CursesClipboard (); } } diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index d23ce210c9..1cb3e20c33 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -38,15 +38,7 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN public static FakeDriver.Behaviors FakeBehaviors = new Behaviors (); - //int Cols, Rows;//, Left, Top; - //public override int Cols => Cols; - //public override int Rows => Rows; - // Only handling left here because not all terminals has a horizontal scroll bar. - //public override int Left => 0; - //public override int Top => 0; public override bool EnableConsoleScrolling { get; set; } - private IClipboard _clipboard = null; - public override IClipboard Clipboard => _clipboard; // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag int [,,] _contents; @@ -78,17 +70,17 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN public FakeDriver () { if (FakeBehaviors.UseFakeClipboard) { - _clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse); + Clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse); } else { if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { - _clipboard = new WindowsClipboard (); + Clipboard = new WindowsClipboard (); } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - _clipboard = new MacOSXClipboard (); + Clipboard = new MacOSXClipboard (); } else { if (CursesDriver.Is_WSL_Platform ()) { - _clipboard = new WSLClipboard (); + Clipboard = new WSLClipboard (); } else { - _clipboard = new CursesClipboard (); + Clipboard = new CursesClipboard (); } } } diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index e13339898d..97c9d7b18d 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -617,7 +617,6 @@ internal class NetDriver : ConsoleDriver { public override bool EnableConsoleScrolling { get; set; } public NetWinVTConsole NetWinConsole { get; } public bool IsWinPlatform { get; } - public override IClipboard Clipboard { get; } public override int [,,] Contents => _contents; int _largestBufferHeight; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 89d31e6d26..1a8dacec60 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -703,11 +703,9 @@ static extern Coord GetLargestConsoleWindowSize ( internal class WindowsDriver : ConsoleDriver { WindowsConsole.CharInfo [] _outputBuffer; WindowsConsole.SmallRect _damageRegion; - IClipboard _clipboard; int [,,] _contents; public override bool EnableConsoleScrolling { get; set; } - public override IClipboard Clipboard => _clipboard; public override int [,,] Contents => _contents; public WindowsConsole WinConsole { get; private set; } @@ -720,7 +718,7 @@ internal class WindowsDriver : ConsoleDriver { public WindowsDriver () { WinConsole = new WindowsConsole (); - _clipboard = new WindowsClipboard (); + Clipboard = new WindowsClipboard (); } public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index b2907d0916..a7dce80639 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -57,5 +57,6 @@ False + C:\Users\charlie\s\gui-cs\Terminal.Gui\UnitTests\bin\Debug\net7.0\fine-code-coverage\coverage-tool-output\UnitTests-fcc-mscodecoverage-generated.runsettings \ No newline at end of file From b853af0fc3290cc63cf6473a47cc7454cf777b4e Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 21:44:36 +0200 Subject: [PATCH 13/99] Made EnableConsoleScrolling non-virtual --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 1 - UnitTests/UnitTests.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 97c9d7b18d..eae901d6c4 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -614,7 +614,6 @@ internal class NetDriver : ConsoleDriver { const int COLOR_BRIGHT_CYAN = 96; const int COLOR_BRIGHT_WHITE = 97; - public override bool EnableConsoleScrolling { get; set; } public NetWinVTConsole NetWinConsole { get; } public bool IsWinPlatform { get; } public override int [,,] Contents => _contents; diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index a7dce80639..b2907d0916 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -57,6 +57,5 @@ False - C:\Users\charlie\s\gui-cs\Terminal.Gui\UnitTests\bin\Debug\net7.0\fine-code-coverage\coverage-tool-output\UnitTests-fcc-mscodecoverage-generated.runsettings \ No newline at end of file From 6f365ef4a52435b50fc1b3d5efa9257ea0502193 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 21:55:05 +0200 Subject: [PATCH 14/99] Made Contents non-virtual --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 10 +++- .../CursesDriver/CursesDriver.cs | 44 +++++++------- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 60 ++++++++----------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 55 ++++++++--------- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 49 +++++++-------- 5 files changed, 101 insertions(+), 117 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 6695f1916a..e6c5884f32 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -120,12 +120,16 @@ public abstract class ConsoleDriver { /// /// NOTE: This functionaliy is currently broken on Windows Terminal. /// - public abstract bool EnableConsoleScrolling { get; set; } + public bool EnableConsoleScrolling { get; set; } /// - /// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag + /// The contents of the application output. The driver writes this buffer to the terminal when + /// is called. + /// + /// The format of the array is rows, columns, and 3 values on the last column: Rune, Attribute and Dirty Flag + /// /// - public virtual int [,,] Contents { get; } + public int [,,] Contents { get; internal set; } /// /// Initializes the driver diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 282adb48ec..00db1941e9 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -20,13 +20,9 @@ internal class CursesDriver : ConsoleDriver { public override int Rows => Curses.Lines; public override int Left => 0; public override int Top => 0; - public override bool EnableConsoleScrolling { get; set; } CursorVisibility? _initialCursorVisibility = null; CursorVisibility? _currentCursorVisibility = null; - int [,,] _contents; - - public override int [,,] Contents => _contents; // Current row, and current col, tracked by Move/AddRune only int _ccol, _crow; @@ -65,47 +61,47 @@ public override void AddRune (Rune rune) _needMove = false; } if (runeWidth == 0 && _ccol > 0) { - var r = _contents [_crow, _ccol - 1, 0]; + var r = Contents [_crow, _ccol - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - _contents [_crow, _ccol - 1, 0] = c; - _contents [_crow, _ccol - 1, 1] = CurrentAttribute; - _contents [_crow, _ccol - 1, 2] = 1; + Contents [_crow, _ccol - 1, 0] = c; + Contents [_crow, _ccol - 1, 1] = CurrentAttribute; + Contents [_crow, _ccol - 1, 2] = 1; Curses.mvaddch (_crow, _ccol - 1, c); } else { if (runeWidth < 2 && _ccol > 0 - && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { + && Rune.ColumnWidth ((char)Contents [_crow, _ccol - 1, 0]) > 1) { var curAttr = CurrentAttribute; - Curses.attrset (_contents [_crow, _ccol - 1, 1]); + Curses.attrset (Contents [_crow, _ccol - 1, 1]); Curses.mvaddch (_crow, _ccol - 1, System.Text.Rune.ReplacementChar.Value); - _contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; Curses.move (_crow, _ccol); Curses.attrset (curAttr); } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { + && Rune.ColumnWidth ((char)Contents [_crow, _ccol, 0]) > 1) { var curAttr = CurrentAttribute; - Curses.attrset (_contents [_crow, _ccol + 1, 1]); + Curses.attrset (Contents [_crow, _ccol + 1, 1]); Curses.mvaddch (_crow, _ccol + 1, System.Text.Rune.ReplacementChar.Value); - _contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; Curses.move (_crow, _ccol); Curses.attrset (curAttr); } if (runeWidth > 1 && _ccol == Clip.Right - 1) { Curses.addch (System.Text.Rune.ReplacementChar.Value); - _contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; } else { Curses.addch ((int)(uint)rune); - _contents [_crow, _ccol, 0] = (int)(uint)rune; + Contents [_crow, _ccol, 0] = (int)(uint)rune; } - _contents [_crow, _ccol, 1] = CurrentAttribute; - _contents [_crow, _ccol, 2] = 1; + Contents [_crow, _ccol, 1] = CurrentAttribute; + Contents [_crow, _ccol, 2] = 1; } } else { _needMove = true; @@ -117,8 +113,8 @@ public override void AddRune (Rune rune) if (runeWidth > 1) { if (validLocation && _ccol < Clip.Right) { - _contents [_crow, _ccol, 1] = CurrentAttribute; - _contents [_crow, _ccol, 2] = 0; + Contents [_crow, _ccol, 1] = CurrentAttribute; + Contents [_crow, _ccol, 2] = 0; } _ccol++; } @@ -641,15 +637,15 @@ public override void ResizeScreen () public override void UpdateOffScreen () { - _contents = new int [Rows, Cols, 3]; + Contents = new int [Rows, Cols, 3]; for (int row = 0; row < Rows; row++) { for (int col = 0; col < Cols; col++) { //Curses.move (row, col); //Curses.attrset (Colors.TopLevel.Normal); //Curses.addch ((int)(uint)' '); - _contents [row, col, 0] = ' '; - _contents [row, col, 1] = Colors.TopLevel.Normal; - _contents [row, col, 2] = 0; + Contents [row, col, 0] = ' '; + Contents [row, col, 1] = Colors.TopLevel.Normal; + Contents [row, col, 2] = 0; } } } diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 1cb3e20c33..d597adb62c 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -38,17 +38,9 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN public static FakeDriver.Behaviors FakeBehaviors = new Behaviors (); - public override bool EnableConsoleScrolling { get; set; } - // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - int [,,] _contents; bool [] _dirtyLine; - /// - /// Assists with testing, the format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - /// - public override int [,,] Contents => _contents; - //void UpdateOffscreen () //{ // int cols = Cols; @@ -107,33 +99,33 @@ public override void AddRune (Rune rune) if (validLocation) { if (runeWidth == 0 && _ccol > 0) { - var r = _contents [_crow, _ccol - 1, 0]; + var r = Contents [_crow, _ccol - 1, 0]; var s = new string (new [] { (char)r, (char)rune }); var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - _contents [_crow, _ccol - 1, 0] = c; - _contents [_crow, _ccol - 1, 1] = CurrentAttribute; - _contents [_crow, _ccol - 1, 2] = 1; + Contents [_crow, _ccol - 1, 0] = c; + Contents [_crow, _ccol - 1, 1] = CurrentAttribute; + Contents [_crow, _ccol - 1, 2] = 1; } else { if (runeWidth < 2 && _ccol > 0 - && Rune.ColumnWidth (_contents [_crow, _ccol - 1, 0]) > 1) { + && Rune.ColumnWidth (Contents [_crow, _ccol - 1, 0]) > 1) { - _contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 - && Rune.ColumnWidth (_contents [_crow, _ccol, 0]) > 1) { + && Rune.ColumnWidth (Contents [_crow, _ccol, 0]) > 1) { - _contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; - _contents [_crow, _ccol + 1, 2] = 1; + Contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol + 1, 2] = 1; } if (runeWidth > 1 && _ccol == Clip.Right - 1) { - _contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; } else { - _contents [_crow, _ccol, 0] = (int)(uint)rune; + Contents [_crow, _ccol, 0] = (int)(uint)rune; } - _contents [_crow, _ccol, 1] = CurrentAttribute; - _contents [_crow, _ccol, 2] = 1; + Contents [_crow, _ccol, 1] = CurrentAttribute; + Contents [_crow, _ccol, 2] = 1; _dirtyLine [_crow] = true; } } @@ -144,8 +136,8 @@ public override void AddRune (Rune rune) if (runeWidth > 1) { if (validLocation && _ccol < Clip.Right) { - _contents [_crow, _ccol, 1] = CurrentAttribute; - _contents [_crow, _ccol, 2] = 0; + Contents [_crow, _ccol, 1] = CurrentAttribute; + Contents [_crow, _ccol, 2] = 0; } _ccol++; } @@ -229,23 +221,23 @@ public override void UpdateScreen () FakeConsole.CursorTop = row; FakeConsole.CursorLeft = col; for (; col < cols; col++) { - if (_contents [row, col, 2] == 0) { + if (Contents [row, col, 2] == 0) { FakeConsole.CursorLeft++; continue; } - var color = _contents [row, col, 1]; + var color = Contents [row, col, 1]; if (color != _redrawColor) { SetColor (color); } - Rune rune = _contents [row, col, 0]; + Rune rune = Contents [row, col, 0]; if (Rune.DecodeSurrogatePair (rune, out char [] spair)) { FakeConsole.Write (spair); } else { FakeConsole.Write ((char)rune); } - _contents [row, col, 2] = 0; + Contents [row, col, 2] = 0; } } } @@ -563,16 +555,16 @@ public override void ResizeScreen () public override void UpdateOffScreen () { - _contents = new int [Rows, Cols, 3]; + Contents = new int [Rows, Cols, 3]; _dirtyLine = new bool [Rows]; // Can raise an exception while is still resizing. try { for (int row = 0; row < Rows; row++) { for (int c = 0; c < Cols; c++) { - _contents [row, c, 0] = ' '; - _contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; - _contents [row, c, 2] = 0; + Contents [row, c, 0] = ' '; + Contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; + Contents [row, c, 2] = 0; _dirtyLine [row] = true; } } @@ -634,7 +626,7 @@ public override void Suspend () public class FakeClipboard : ClipboardBase { public Exception FakeException = null; - string _contents = string.Empty; + string Contents = string.Empty; bool _isSupportedAlwaysFalse = false; @@ -653,7 +645,7 @@ protected override string GetClipboardDataImpl () if (FakeException != null) { throw FakeException; } - return _contents; + return Contents; } protected override void SetClipboardDataImpl (string text) @@ -661,7 +653,7 @@ protected override void SetClipboardDataImpl (string text) if (FakeException != null) { throw FakeException; } - _contents = text; + Contents = text; } } diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index eae901d6c4..d003afcb80 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -616,7 +616,6 @@ internal class NetDriver : ConsoleDriver { public NetWinVTConsole NetWinConsole { get; } public bool IsWinPlatform { get; } - public override int [,,] Contents => _contents; int _largestBufferHeight; @@ -640,8 +639,6 @@ public NetDriver () } } - // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - int [,,] _contents; bool [] _dirtyLine; // Current row, and current col, tracked by Move/AddCh only @@ -654,7 +651,7 @@ public override void Move (int col, int row) public override void AddRune (Rune rune) { - if (_contents.Length != Rows * Cols * 3) { + if (Contents.Length != Rows * Cols * 3) { // BUGBUG: Shouldn't this throw an exception? Doing so to see what happens throw new InvalidOperationException ("Driver contents are wrong size"); return; @@ -667,33 +664,33 @@ public override void AddRune (Rune rune) if (validLocation) { if (runeWidth == 0 && _ccol > 0) { // Single column glyph beyond first col - var r = _contents [_crow, _ccol - 1, 0]; + var r = Contents [_crow, _ccol - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - _contents [_crow, _ccol - 1, 0] = c; - _contents [_crow, _ccol - 1, 1] = CurrentAttribute; - _contents [_crow, _ccol - 1, 2] = 1; + Contents [_crow, _ccol - 1, 0] = c; + Contents [_crow, _ccol - 1, 1] = CurrentAttribute; + Contents [_crow, _ccol - 1, 2] = 1; } else { if (runeWidth < 2 && _ccol > 0 - && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { + && Rune.ColumnWidth ((char)Contents [_crow, _ccol - 1, 0]) > 1) { - _contents [_crow, _ccol - 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol - 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { + && Rune.ColumnWidth ((char)Contents [_crow, _ccol, 0]) > 1) { - _contents [_crow, _ccol + 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; - _contents [_crow, _ccol + 1, 2] = 1; + Contents [_crow, _ccol + 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol + 1, 2] = 1; } if (runeWidth > 1 && _ccol == Clip.Right - 1) { - _contents [_crow, _ccol, 0] = (char)System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol, 0] = (char)System.Text.Rune.ReplacementChar.Value; } else { - _contents [_crow, _ccol, 0] = (int)(uint)rune; + Contents [_crow, _ccol, 0] = (int)(uint)rune; } - _contents [_crow, _ccol, 1] = CurrentAttribute; - _contents [_crow, _ccol, 2] = 1; + Contents [_crow, _ccol, 1] = CurrentAttribute; + Contents [_crow, _ccol, 2] = 1; } _dirtyLine [_crow] = true; @@ -705,8 +702,8 @@ public override void AddRune (Rune rune) if (runeWidth > 1) { if (validLocation && _ccol < Clip.Right) { - _contents [_crow, _ccol, 1] = CurrentAttribute; - _contents [_crow, _ccol, 2] = 0; + Contents [_crow, _ccol, 1] = CurrentAttribute; + Contents [_crow, _ccol, 2] = 0; } _ccol++; } @@ -840,17 +837,17 @@ public override void ResizeScreen () public override void UpdateOffScreen () { - _contents = new int [Rows, Cols, 3]; + Contents = new int [Rows, Cols, 3]; _dirtyLine = new bool [Rows]; - lock (_contents) { + lock (Contents) { // Can raise an exception while is still resizing. try { for (int row = 0; row < Rows; row++) { for (int c = 0; c < Cols; c++) { - _contents [row, c, 0] = ' '; - _contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; - _contents [row, c, 2] = 0; + Contents [row, c, 0] = ' '; + Contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; + Contents [row, c, 2] = 0; _dirtyLine [row] = true; } } @@ -866,7 +863,7 @@ public override void Refresh () public override void UpdateScreen () { - if (winChanging || Console.WindowHeight < 1 || _contents.Length != Rows * Cols * 3 + if (winChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols * 3 || (!EnableConsoleScrolling && Rows != Console.WindowHeight) || (EnableConsoleScrolling && Rows != _largestBufferHeight)) { return; @@ -898,7 +895,7 @@ public override void UpdateScreen () lastCol = -1; var outputWidth = 0; for (; col < cols; col++) { - if (_contents [row, col, 2] == 0) { + if (Contents [row, col, 2] == 0) { if (output.Length > 0) { SetCursorPosition (lastCol, row); Console.Write (output); @@ -916,20 +913,20 @@ public override void UpdateScreen () if (lastCol == -1) lastCol = col; - var attr = _contents [row, col, 1]; + var attr = Contents [row, col, 1]; if (attr != redrawAttr) { redrawAttr = attr; output.Append (WriteAttributes (attr)); } outputWidth++; - var rune = _contents [row, col, 0]; + var rune = Contents [row, col, 0]; char [] spair; if (Rune.DecodeSurrogatePair ((uint)rune, out spair)) { output.Append (spair); } else { output.Append ((char)rune); } - _contents [row, col, 2] = 0; + Contents [row, col, 2] = 0; } } if (output.Length > 0) { diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 1a8dacec60..f4bd391780 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -703,18 +703,13 @@ static extern Coord GetLargestConsoleWindowSize ( internal class WindowsDriver : ConsoleDriver { WindowsConsole.CharInfo [] _outputBuffer; WindowsConsole.SmallRect _damageRegion; - int [,,] _contents; - - public override bool EnableConsoleScrolling { get; set; } - public override int [,,] Contents => _contents; - - public WindowsConsole WinConsole { get; private set; } - Action _keyHandler; Action _keyDownHandler; Action _keyUpHandler; Action _mouseHandler; + public WindowsConsole WinConsole { get; private set; } + public WindowsDriver () { WinConsole = new WindowsConsole (); @@ -1500,15 +1495,15 @@ public override void ResizeScreen () public override void UpdateOffScreen () { - _contents = new int [Rows, Cols, 3]; + Contents = new int [Rows, Cols, 3]; for (int row = 0; row < Rows; row++) { for (int col = 0; col < Cols; col++) { int position = row * Cols + col; _outputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal; _outputBuffer [position].Char.UnicodeChar = ' '; - _contents [row, col, 0] = _outputBuffer [position].Char.UnicodeChar; - _contents [row, col, 1] = _outputBuffer [position].Attributes; - _contents [row, col, 2] = 0; + Contents [row, col, 0] = _outputBuffer [position].Char.UnicodeChar; + Contents [row, col, 1] = _outputBuffer [position].Attributes; + Contents [row, col, 2] = 0; } } } @@ -1537,13 +1532,13 @@ public override void AddRune (Rune nStackrune) if (validLocation) { if (runeWidth == 0 && _ccol > 0) { // Non spacing glyph beyond first col - var r = _contents [_crow, _ccol - 1, 0]; + var r = Contents [_crow, _ccol - 1, 0]; var s = new string (new char [] { (char)r, (char)rune.Value }); var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - _contents [_crow, _ccol - 1, 0] = c; - _contents [_crow, _ccol - 1, 1] = CurrentAttribute; - _contents [_crow, _ccol - 1, 2] = 1; + Contents [_crow, _ccol - 1, 0] = c; + Contents [_crow, _ccol - 1, 1] = CurrentAttribute; + Contents [_crow, _ccol - 1, 2] = 1; var prevPosition = _crow * Cols + (_ccol - 1); _outputBuffer [prevPosition].Char.UnicodeChar = c; @@ -1552,16 +1547,16 @@ public override void AddRune (Rune nStackrune) } else { if (runeWidth < 2) { // Single column glyph - if (_ccol > 0 && Rune.ColumnWidth ((char)_contents [_crow, _ccol - 1, 0]) > 1) { + if (_ccol > 0 && Rune.ColumnWidth ((char)Contents [_crow, _ccol - 1, 0]) > 1) { // glyph to left is wide; nuke it // BUGBUG: Does this make sense? - _contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; var prevPosition = _crow * Cols + (_ccol - 1); _outputBuffer [prevPosition].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; - } else if (_ccol <= Clip.Right - 1 && Rune.ColumnWidth ((char)_contents [_crow, _ccol, 0]) > 1) { + } else if (_ccol <= Clip.Right - 1 && Rune.ColumnWidth ((char)Contents [_crow, _ccol, 0]) > 1) { // we're just inside the clip rect and the glyph there is wide // nuke the existing glyph to right - _contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; var prevPosition = GetOutputBufferPosition () + 1; _outputBuffer [prevPosition].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; } @@ -1571,21 +1566,21 @@ public override void AddRune (Rune nStackrune) // Wide glyph and we're just inside the clip rect // ignore it // BUGBUG: Shouldn't we write the unknown glyph glyph? - _contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; _outputBuffer [position].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; } else { // Add the glyph (wide or not) if (rune.IsBmp) { - _contents [_crow, _ccol, 0] = rune.Value; + Contents [_crow, _ccol, 0] = rune.Value; _outputBuffer [position].Char.UnicodeChar = (char) rune.Value; } else { - _contents [_crow, _ccol, 0] = (char)System.Text.Rune.ReplacementChar.Value; + Contents [_crow, _ccol, 0] = (char)System.Text.Rune.ReplacementChar.Value; _outputBuffer [position].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; } } - _contents [_crow, _ccol, 1] = CurrentAttribute; - _contents [_crow, _ccol, 2] = 1; + Contents [_crow, _ccol, 1] = CurrentAttribute; + Contents [_crow, _ccol, 2] = 1; _outputBuffer [position].Attributes = (ushort)CurrentAttribute; WindowsConsole.SmallRect.Update (ref _damageRegion, (short)_ccol, (short)_crow); @@ -1601,9 +1596,9 @@ public override void AddRune (Rune nStackrune) position = GetOutputBufferPosition (); _outputBuffer [position].Attributes = (ushort)CurrentAttribute; _outputBuffer [position].Char.UnicodeChar = (char)0x00; - _contents [_crow, _ccol, 0] = (char)0x00; - _contents [_crow, _ccol, 1] = CurrentAttribute; - _contents [_crow, _ccol, 2] = 0; + Contents [_crow, _ccol, 0] = (char)0x00; + Contents [_crow, _ccol, 1] = CurrentAttribute; + Contents [_crow, _ccol, 2] = 0; } _ccol++; } From e173b79a6bfbc66e60f3e378c3fef69b13a144c4 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 22:37:51 +0200 Subject: [PATCH 15/99] Pulled Row/Col up --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 25 +++- .../CursesDriver/CursesDriver.cs | 123 ++++++----------- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 79 +++++------ Terminal.Gui/ConsoleDrivers/NetDriver.cs | 62 ++++----- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 129 +++++------------- 5 files changed, 164 insertions(+), 254 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index e6c5884f32..352cbcfa57 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -123,7 +123,7 @@ public abstract class ConsoleDriver { public bool EnableConsoleScrolling { get; set; } /// - /// The contents of the application output. The driver writes this buffer to the terminal when + /// The contents of the application output. The driver outputs this buffer to the terminal when /// is called. /// /// The format of the array is rows, columns, and 3 values on the last column: Rune, Attribute and Dirty Flag @@ -136,11 +136,28 @@ public abstract class ConsoleDriver { /// /// Method to invoke when the terminal is resized. public abstract void Init (Action terminalResized); + /// - /// Moves the cursor to the specified column and row. + /// Gets the column last set by . and + /// are used by and to determine where to add content. /// - /// Column to move the cursor to. - /// Row to move the cursor to. + public int Col { get; internal set; } + + /// + /// Gets the row last set by . and + /// are used by and to determine where to add content. + /// + public int Row { get; internal set; } + + /// + /// Sets and to the specified column and row in . + /// Used by and to determine where to add content. + /// + /// + /// This does not move the cursor on the screen, it only updates the internal state of the driver. + /// + /// Column to move to. + /// Row to move to. public abstract void Move (int col, int row); /// diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 00db1941e9..8ed12d49bd 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -24,25 +24,22 @@ internal class CursesDriver : ConsoleDriver { CursorVisibility? _initialCursorVisibility = null; CursorVisibility? _currentCursorVisibility = null; - // Current row, and current col, tracked by Move/AddRune only - int _ccol, _crow; - bool _needMove; + // If true, Move set Col and Row to an invalid location + bool _atValidLocation; + public override void Move (int col, int row) { - _ccol = col; - _crow = row; + Col = col; + Row = row; if (IsValidLocation (col, row)) { Curses.move (row, col); - _needMove = false; + _atValidLocation = true; } else { // Not a valid location (outside screen or clip region) - // We need to move the cursor to within clip region, and then - // later move it to the desired location in AddStr - // BUGBUG: Clip.X/Y may actually be outside of the defined clip region rects as - // currently implemented. + // Move within the clip region, then AddStr will actually move to Col, Row Curses.move (Clip.Y, Clip.X); - _needMove = true; + _atValidLocation = false; } } @@ -50,73 +47,72 @@ public override void AddRune (Rune rune) { rune = MakePrintable (rune); var runeWidth = Rune.ColumnWidth (rune); - var validLocation = IsValidLocation (_ccol, _crow); + var validLocation = IsValidLocation (Col, Row); if (validLocation) { - if (_needMove) { - // Move was called, wanting to put cursor in an invalid location. - // We need to move the cursor to the clip region, and then - // move it to the desired location. - Curses.move (_crow, _ccol); - _needMove = false; + if (!_atValidLocation) { + // Move was called with an invalid location. + // Since then, the clip changed, and now we are at a valid location. + Curses.move (Row, Col); + _atValidLocation = false; } - if (runeWidth == 0 && _ccol > 0) { - var r = Contents [_crow, _ccol - 1, 0]; + if (runeWidth == 0 && Col > 0) { + var r = Contents [Row, Col - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - Contents [_crow, _ccol - 1, 0] = c; - Contents [_crow, _ccol - 1, 1] = CurrentAttribute; - Contents [_crow, _ccol - 1, 2] = 1; + Contents [Row, Col - 1, 0] = c; + Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 2] = 1; - Curses.mvaddch (_crow, _ccol - 1, c); + Curses.mvaddch (Row, Col - 1, c); } else { - if (runeWidth < 2 && _ccol > 0 - && Rune.ColumnWidth ((char)Contents [_crow, _ccol - 1, 0]) > 1) { + if (runeWidth < 2 && Col > 0 + && Rune.ColumnWidth ((char)Contents [Row, Col - 1, 0]) > 1) { var curAttr = CurrentAttribute; - Curses.attrset (Contents [_crow, _ccol - 1, 1]); - Curses.mvaddch (_crow, _ccol - 1, System.Text.Rune.ReplacementChar.Value); - Contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; - Curses.move (_crow, _ccol); + Curses.attrset (Contents [Row, Col - 1, 1]); + Curses.mvaddch (Row, Col - 1, System.Text.Rune.ReplacementChar.Value); + Contents [Row, Col - 1, 0] = System.Text.Rune.ReplacementChar.Value; + Curses.move (Row, Col); Curses.attrset (curAttr); - } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)Contents [_crow, _ccol, 0]) > 1) { + } else if (runeWidth < 2 && Col <= Clip.Right - 1 + && Rune.ColumnWidth ((char)Contents [Row, Col, 0]) > 1) { var curAttr = CurrentAttribute; - Curses.attrset (Contents [_crow, _ccol + 1, 1]); - Curses.mvaddch (_crow, _ccol + 1, System.Text.Rune.ReplacementChar.Value); - Contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; - Curses.move (_crow, _ccol); + Curses.attrset (Contents [Row, Col + 1, 1]); + Curses.mvaddch (Row, Col + 1, System.Text.Rune.ReplacementChar.Value); + Contents [Row, Col + 1, 0] = System.Text.Rune.ReplacementChar.Value; + Curses.move (Row, Col); Curses.attrset (curAttr); } - if (runeWidth > 1 && _ccol == Clip.Right - 1) { + if (runeWidth > 1 && Col == Clip.Right - 1) { Curses.addch (System.Text.Rune.ReplacementChar.Value); - Contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [Row, Col, 0] = System.Text.Rune.ReplacementChar.Value; } else { Curses.addch ((int)(uint)rune); - Contents [_crow, _ccol, 0] = (int)(uint)rune; + Contents [Row, Col, 0] = (int)(uint)rune; } - Contents [_crow, _ccol, 1] = CurrentAttribute; - Contents [_crow, _ccol, 2] = 1; + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 1; } } else { - _needMove = true; + _atValidLocation = false; } if (runeWidth is < 0 or > 0) { - _ccol++; + Col++; } if (runeWidth > 1) { - if (validLocation && _ccol < Clip.Right) { - Contents [_crow, _ccol, 1] = CurrentAttribute; - Contents [_crow, _ccol, 2] = 0; + if (validLocation && Col < Clip.Right) { + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 0; } - _ccol++; + Col++; } } @@ -190,39 +186,6 @@ public override Attribute MakeColor (Color fore, Color back) return MakeColor ((short)MapColor (fore), (short)MapColor (back)); } - int [,] _colorPairs = new int [16, 16]; - - //public override void SetColors (ConsoleColor foreground, ConsoleColor background) - //{ - // // BUGBUG: This code is never called ?? See Issue #2300 - // int f = (short)foreground; - // int b = (short)background; - // var v = _colorPairs [f, b]; - // if ((v & 0x10000) == 0) { - // b &= 0x7; - // bool bold = (f & 0x8) != 0; - // f &= 0x7; - - // v = MakeColor ((short)f, (short)b) | (bold ? Curses.A_BOLD : 0); - // _colorPairs [(int)foreground, (int)background] = v | 0x1000; - // } - // SetAttribute (v & 0xffff); - //} - - ////Dictionary _rawPairs = new Dictionary (); - //public override void SetColors (short foreColorId, short backgroundColorId) - //{ - // throw new NotImplementedException (); - - // // BUGBUG: This code is never called ?? See Issue #2300 - // //int key = ((ushort)foreColorId << 16) | (ushort)backgroundColorId; - // //if (!_rawPairs.TryGetValue (key, out var v)) { - // // v = MakeColor (foreColorId, backgroundColorId); - // // _rawPairs [key] = v; - // //} - // //SetAttribute (v); - //} - static Key MapCursesKey (int cursesKey) { switch (cursesKey) { diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index d597adb62c..71e4318448 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -78,14 +78,13 @@ public FakeDriver () } } - // Current row, and current col, tracked by Move/AddCh only - int _ccol, _crow; public override void Move (int col, int row) { - _ccol = col; - _crow = row; + Col = col; + Row = row; - if (Clip.Contains (col, row)) { + // BUGBUG: Why is FakeDriver setting the cursor location here? + if (IsValidLocation (col, row)) { FakeConsole.CursorTop = row; FakeConsole.CursorLeft = col; } @@ -95,51 +94,51 @@ public override void AddRune (Rune rune) { rune = MakePrintable (rune); var runeWidth = Rune.ColumnWidth (rune); - var validLocation = IsValidLocation (_ccol, _crow); + var validLocation = IsValidLocation (Col, Row); if (validLocation) { - if (runeWidth == 0 && _ccol > 0) { - var r = Contents [_crow, _ccol - 1, 0]; + if (runeWidth == 0 && Col > 0) { + var r = Contents [Row, Col - 1, 0]; var s = new string (new [] { (char)r, (char)rune }); var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - Contents [_crow, _ccol - 1, 0] = c; - Contents [_crow, _ccol - 1, 1] = CurrentAttribute; - Contents [_crow, _ccol - 1, 2] = 1; + Contents [Row, Col - 1, 0] = c; + Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 2] = 1; } else { - if (runeWidth < 2 && _ccol > 0 - && Rune.ColumnWidth (Contents [_crow, _ccol - 1, 0]) > 1) { + if (runeWidth < 2 && Col > 0 + && Rune.ColumnWidth (Contents [Row, Col - 1, 0]) > 1) { - Contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [Row, Col - 1, 0] = System.Text.Rune.ReplacementChar.Value; - } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 - && Rune.ColumnWidth (Contents [_crow, _ccol, 0]) > 1) { + } else if (runeWidth < 2 && Col <= Clip.Right - 1 + && Rune.ColumnWidth (Contents [Row, Col, 0]) > 1) { - Contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; - Contents [_crow, _ccol + 1, 2] = 1; + Contents [Row, Col + 1, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [Row, Col + 1, 2] = 1; } - if (runeWidth > 1 && _ccol == Clip.Right - 1) { - Contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; + if (runeWidth > 1 && Col == Clip.Right - 1) { + Contents [Row, Col, 0] = System.Text.Rune.ReplacementChar.Value; } else { - Contents [_crow, _ccol, 0] = (int)(uint)rune; + Contents [Row, Col, 0] = (int)(uint)rune; } - Contents [_crow, _ccol, 1] = CurrentAttribute; - Contents [_crow, _ccol, 2] = 1; - _dirtyLine [_crow] = true; + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 1; + _dirtyLine [Row] = true; } } if (runeWidth < 0 || runeWidth > 0) { - _ccol++; + Col++; } if (runeWidth > 1) { - if (validLocation && _ccol < Clip.Right) { - Contents [_crow, _ccol, 1] = CurrentAttribute; - Contents [_crow, _ccol, 2] = 0; + if (validLocation && Col < Clip.Right) { + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 0; } - _ccol++; + Col++; } } @@ -158,17 +157,11 @@ public override void End () public override Attribute MakeColor (Color foreground, Color background) { - return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); - } - - static Attribute MakeColor (ConsoleColor f, ConsoleColor b) - { - // Encode the colors into the int value. return new Attribute ( - value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff), - foreground: (Color)f, - background: (Color)b - ); + value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), + foreground: (Color)foreground, + background: (Color)background + ); } public override void Init (Action terminalResized) @@ -201,7 +194,7 @@ void SetColor (int color) FakeConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff); } } - + public override void UpdateScreen () { int top = Top; @@ -462,7 +455,7 @@ public override bool SetCursorVisibility (CursorVisibility visibility) /// public override bool EnsureCursorVisibility () { - if (!(_ccol >= 0 && _crow >= 0 && _ccol < Cols && _crow < Rows)) { + if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { GetCursorVisibility (out CursorVisibility cursorVisibility); _savedCursorVisibility = cursorVisibility; SetCursorVisibility (CursorVisibility.Invisible); @@ -598,8 +591,8 @@ public override void UpdateCursor () // Prevents the exception of size changing during resizing. try { - if (_ccol >= 0 && _ccol < FakeConsole.BufferWidth && _crow >= 0 && _crow < FakeConsole.BufferHeight) { - FakeConsole.SetCursorPosition (_ccol, _crow); + if (Col >= 0 && Col < FakeConsole.BufferWidth && Row >= 0 && Row < FakeConsole.BufferHeight) { + FakeConsole.SetCursorPosition (Col, Row); } } catch (System.IO.IOException) { } catch (ArgumentOutOfRangeException) { diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index d003afcb80..17b0ef8a32 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -641,12 +641,10 @@ public NetDriver () bool [] _dirtyLine; - // Current row, and current col, tracked by Move/AddCh only - int _ccol, _crow; public override void Move (int col, int row) { - _ccol = col; - _crow = row; + Col = col; + Row = row; } public override void AddRune (Rune rune) @@ -659,53 +657,53 @@ public override void AddRune (Rune rune) rune = MakePrintable (rune); var runeWidth = Rune.ColumnWidth (rune); - var validLocation = IsValidLocation (_ccol, _crow); + var validLocation = IsValidLocation (Col, Row); if (validLocation) { - if (runeWidth == 0 && _ccol > 0) { + if (runeWidth == 0 && Col > 0) { // Single column glyph beyond first col - var r = Contents [_crow, _ccol - 1, 0]; + var r = Contents [Row, Col - 1, 0]; var s = new string (new char [] { (char)r, (char)rune }); var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - Contents [_crow, _ccol - 1, 0] = c; - Contents [_crow, _ccol - 1, 1] = CurrentAttribute; - Contents [_crow, _ccol - 1, 2] = 1; + Contents [Row, Col - 1, 0] = c; + Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 2] = 1; } else { - if (runeWidth < 2 && _ccol > 0 - && Rune.ColumnWidth ((char)Contents [_crow, _ccol - 1, 0]) > 1) { + if (runeWidth < 2 && Col > 0 + && Rune.ColumnWidth ((char)Contents [Row, Col - 1, 0]) > 1) { - Contents [_crow, _ccol - 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; + Contents [Row, Col - 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; - } else if (runeWidth < 2 && _ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)Contents [_crow, _ccol, 0]) > 1) { + } else if (runeWidth < 2 && Col <= Clip.Right - 1 + && Rune.ColumnWidth ((char)Contents [Row, Col, 0]) > 1) { - Contents [_crow, _ccol + 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; - Contents [_crow, _ccol + 1, 2] = 1; + Contents [Row, Col + 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; + Contents [Row, Col + 1, 2] = 1; } - if (runeWidth > 1 && _ccol == Clip.Right - 1) { - Contents [_crow, _ccol, 0] = (char)System.Text.Rune.ReplacementChar.Value; + if (runeWidth > 1 && Col == Clip.Right - 1) { + Contents [Row, Col, 0] = (char)System.Text.Rune.ReplacementChar.Value; } else { - Contents [_crow, _ccol, 0] = (int)(uint)rune; + Contents [Row, Col, 0] = (int)(uint)rune; } - Contents [_crow, _ccol, 1] = CurrentAttribute; - Contents [_crow, _ccol, 2] = 1; + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 1; } - _dirtyLine [_crow] = true; + _dirtyLine [Row] = true; } if (runeWidth < 0 || runeWidth > 0) { - _ccol++; + Col++; } if (runeWidth > 1) { - if (validLocation && _ccol < Clip.Right) { - Contents [_crow, _ccol, 1] = CurrentAttribute; - Contents [_crow, _ccol, 2] = 0; + if (validLocation && Col < Clip.Right) { + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 0; } - _ccol++; + Col++; } } @@ -1062,9 +1060,9 @@ public override void UpdateCursor () EnsureCursorVisibility (); //Debug.WriteLine ($"Before - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); - if (_ccol >= 0 && _ccol < Cols && _crow >= 0 && _crow < Rows) { - SetCursorPosition (_ccol, _crow); - SetWindowPosition (0, _crow); + if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) { + SetCursorPosition (Col, Row); + SetWindowPosition (0, Row); } //Debug.WriteLine ($"WindowTop: {Console.WindowTop};WindowLeft: {Console.WindowLeft}"); //Debug.WriteLine ($"After - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); @@ -1416,7 +1414,7 @@ public override bool SetCursorVisibility (CursorVisibility visibility) /// public override bool EnsureCursorVisibility () { - if (!(_ccol >= 0 && _crow >= 0 && _ccol < Cols && _crow < Rows)) { + if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { GetCursorVisibility (out CursorVisibility cursorVisibility); savedCursorVisibility = cursorVisibility; SetCursorVisibility (CursorVisibility.Invisible); diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index f4bd391780..93d26c839b 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -554,6 +554,7 @@ static extern bool ReadConsoleOutput ( ref SmallRect lpReadRegion ); + // TODO: This API is obsolete. See https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput [DllImport ("kernel32.dll", EntryPoint = "WriteConsoleOutput", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool WriteConsoleOutput ( IntPtr hConsoleOutput, @@ -932,67 +933,7 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) X = mouseEvent.MousePosition.X, Y = mouseEvent.MousePosition.Y }; - - //if (!isButtonPressed && buttonPressedCount < 2 - // && mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved - // && (mouseEvent.ButtonState == WindowsConsole.ButtonState.Button1Pressed - // || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button2Pressed - // || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button3Pressed)) { - - // lastMouseButtonPressed = mouseEvent.ButtonState; - // buttonPressedCount++; - //} else if (!isButtonPressed && buttonPressedCount > 0 && mouseEvent.ButtonState == 0 - // && mouseEvent.EventFlags == 0) { - - // buttonPressedCount++; - //} - //System.Diagnostics.Debug.WriteLine ($"isButtonPressed: {isButtonPressed};buttonPressedCount: {buttonPressedCount};lastMouseButtonPressed: {lastMouseButtonPressed}"); - //System.Diagnostics.Debug.WriteLine ($"isOneFingerDoubleClicked: {isOneFingerDoubleClicked}"); - - //if (buttonPressedCount == 1 && lastMouseButtonPressed != null && p == point - // && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed - // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed - // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) { - - // switch (lastMouseButtonPressed) { - // case WindowsConsole.ButtonState.Button1Pressed: - // mouseFlag = MouseFlags.Button1DoubleClicked; - // break; - - // case WindowsConsole.ButtonState.Button2Pressed: - // mouseFlag = MouseFlags.Button2DoubleClicked; - // break; - - // case WindowsConsole.ButtonState.Button3Pressed: - // mouseFlag = MouseFlags.Button3DoubleClicked; - // break; - // } - // isOneFingerDoubleClicked = true; - - //} else if (buttonPressedCount == 3 && lastMouseButtonPressed != null && isOneFingerDoubleClicked && p == point - // && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed - // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed - // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) { - - // switch (lastMouseButtonPressed) { - // case WindowsConsole.ButtonState.Button1Pressed: - // mouseFlag = MouseFlags.Button1TripleClicked; - // break; - - // case WindowsConsole.ButtonState.Button2Pressed: - // mouseFlag = MouseFlags.Button2TripleClicked; - // break; - - // case WindowsConsole.ButtonState.Button3Pressed: - // mouseFlag = MouseFlags.Button3TripleClicked; - // break; - // } - // buttonPressedCount = 0; - // lastMouseButtonPressed = null; - // isOneFingerDoubleClicked = false; - // isButtonReleased = false; - - //} + if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && _lastMouseButtonPressed == null && !_isButtonDoubleClicked) || (_lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.MouseMoved) && mouseEvent.ButtonState != 0 && !_isButtonReleased && !_isButtonDoubleClicked)) { @@ -1508,17 +1449,15 @@ public override void UpdateOffScreen () } } - // Current row, and current col, tracked by Move/AddCh only - int _ccol, _crow; public override void Move (int col, int row) { - _ccol = col; - _crow = row; + Col = col; + Row = row; } int GetOutputBufferPosition () { - return _crow * Cols + _ccol; + return Row * Cols + Col; } public override void AddRune (Rune nStackrune) @@ -1527,80 +1466,80 @@ public override void AddRune (Rune nStackrune) System.Text.Rune rune = (System.Text.Rune)MakePrintable (nStackrune).Value; var runeWidth = Rune.ColumnWidth (nStackrune); var position = GetOutputBufferPosition (); - var validLocation = IsValidLocation (_ccol, _crow); + var validLocation = IsValidLocation (Col, Row); if (validLocation) { - if (runeWidth == 0 && _ccol > 0) { + if (runeWidth == 0 && Col > 0) { // Non spacing glyph beyond first col - var r = Contents [_crow, _ccol - 1, 0]; + var r = Contents [Row, Col - 1, 0]; var s = new string (new char [] { (char)r, (char)rune.Value }); var sn = !s.IsNormalized () ? s.Normalize () : s; var c = sn [0]; - Contents [_crow, _ccol - 1, 0] = c; - Contents [_crow, _ccol - 1, 1] = CurrentAttribute; - Contents [_crow, _ccol - 1, 2] = 1; + Contents [Row, Col - 1, 0] = c; + Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 2] = 1; - var prevPosition = _crow * Cols + (_ccol - 1); + var prevPosition = Row * Cols + (Col - 1); _outputBuffer [prevPosition].Char.UnicodeChar = c; _outputBuffer [prevPosition].Attributes = (ushort)CurrentAttribute; - WindowsConsole.SmallRect.Update (ref _damageRegion, (short)(_ccol - 1), (short)_crow); + WindowsConsole.SmallRect.Update (ref _damageRegion, (short)(Col - 1), (short)Row); } else { if (runeWidth < 2) { // Single column glyph - if (_ccol > 0 && Rune.ColumnWidth ((char)Contents [_crow, _ccol - 1, 0]) > 1) { + if (Col > 0 && Rune.ColumnWidth ((char)Contents [Row, Col - 1, 0]) > 1) { // glyph to left is wide; nuke it // BUGBUG: Does this make sense? - Contents [_crow, _ccol - 1, 0] = System.Text.Rune.ReplacementChar.Value; - var prevPosition = _crow * Cols + (_ccol - 1); + Contents [Row, Col - 1, 0] = System.Text.Rune.ReplacementChar.Value; + var prevPosition = Row * Cols + (Col - 1); _outputBuffer [prevPosition].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; - } else if (_ccol <= Clip.Right - 1 && Rune.ColumnWidth ((char)Contents [_crow, _ccol, 0]) > 1) { + } else if (Col <= Clip.Right - 1 && Rune.ColumnWidth ((char)Contents [Row, Col, 0]) > 1) { // we're just inside the clip rect and the glyph there is wide // nuke the existing glyph to right - Contents [_crow, _ccol + 1, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [Row, Col + 1, 0] = System.Text.Rune.ReplacementChar.Value; var prevPosition = GetOutputBufferPosition () + 1; _outputBuffer [prevPosition].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; } } - if (runeWidth > 1 && _ccol == Clip.Right - 1) { + if (runeWidth > 1 && Col == Clip.Right - 1) { // Wide glyph and we're just inside the clip rect // ignore it // BUGBUG: Shouldn't we write the unknown glyph glyph? - Contents [_crow, _ccol, 0] = System.Text.Rune.ReplacementChar.Value; + Contents [Row, Col, 0] = System.Text.Rune.ReplacementChar.Value; _outputBuffer [position].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; } else { // Add the glyph (wide or not) if (rune.IsBmp) { - Contents [_crow, _ccol, 0] = rune.Value; + Contents [Row, Col, 0] = rune.Value; _outputBuffer [position].Char.UnicodeChar = (char) rune.Value; } else { - Contents [_crow, _ccol, 0] = (char)System.Text.Rune.ReplacementChar.Value; + Contents [Row, Col, 0] = (char)System.Text.Rune.ReplacementChar.Value; _outputBuffer [position].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; } } - Contents [_crow, _ccol, 1] = CurrentAttribute; - Contents [_crow, _ccol, 2] = 1; + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 1; _outputBuffer [position].Attributes = (ushort)CurrentAttribute; - WindowsConsole.SmallRect.Update (ref _damageRegion, (short)_ccol, (short)_crow); + WindowsConsole.SmallRect.Update (ref _damageRegion, (short)Col, (short)Row); } } if (runeWidth < 0 || runeWidth > 0) { - _ccol++; + Col++; } if (runeWidth > 1) { - if (rune.IsBmp && _ccol < Clip.Right) { + if (rune.IsBmp && Col < Clip.Right) { position = GetOutputBufferPosition (); _outputBuffer [position].Attributes = (ushort)CurrentAttribute; _outputBuffer [position].Char.UnicodeChar = (char)0x00; - Contents [_crow, _ccol, 0] = (char)0x00; - Contents [_crow, _ccol, 1] = CurrentAttribute; - Contents [_crow, _ccol, 2] = 0; + Contents [Row, Col, 0] = (char)0x00; + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 0; } - _ccol++; + Col++; } } @@ -1686,7 +1625,7 @@ public override void UpdateScreen () public override void UpdateCursor () { - if (_ccol < 0 || _crow < 0 || _ccol > Cols || _crow > Rows) { + if (Col < 0 || Row < 0 || Col > Cols || Row > Rows) { GetCursorVisibility (out CursorVisibility cursorVisibility); savedCursorVisibility = cursorVisibility; SetCursorVisibility (CursorVisibility.Invisible); @@ -1695,8 +1634,8 @@ public override void UpdateCursor () SetCursorVisibility (savedCursorVisibility); var position = new WindowsConsole.Coord () { - X = (short)_ccol, - Y = (short)_crow + X = (short)Col, + Y = (short)Row }; WinConsole.SetCursorPosition (position); } From bc61b86492880bce8f9cbcb9354fecf2ca67f7ab Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 22:46:19 +0200 Subject: [PATCH 16/99] Made MoveTo virtual; fixed stupid FakeDriver cursor issue --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 8 ++++++-- .../ConsoleDrivers/CursesDriver/CursesDriver.cs | 3 +-- Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs | 12 ------------ Terminal.Gui/ConsoleDrivers/NetDriver.cs | 6 ------ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 8 +------- UnitTests/ConsoleDrivers/ConsoleDriverTests.cs | 2 -- 6 files changed, 8 insertions(+), 31 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 352cbcfa57..65ca4e8efa 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -150,7 +150,7 @@ public abstract class ConsoleDriver { public int Row { get; internal set; } /// - /// Sets and to the specified column and row in . + /// Updates and to the specified column and row in . /// Used by and to determine where to add content. /// /// @@ -158,7 +158,11 @@ public abstract class ConsoleDriver { /// /// Column to move to. /// Row to move to. - public abstract void Move (int col, int row); + public virtual void Move (int col, int row) + { + Col = col; + Row = row; + } /// /// Adds the specified rune to the display at the current cursor position. diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 8ed12d49bd..4484f31f7d 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -29,8 +29,7 @@ internal class CursesDriver : ConsoleDriver { public override void Move (int col, int row) { - Col = col; - Row = row; + base.Move (col, row); if (IsValidLocation (col, row)) { Curses.move (row, col); diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 71e4318448..e6cacbb714 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -78,18 +78,6 @@ public FakeDriver () } } - public override void Move (int col, int row) - { - Col = col; - Row = row; - - // BUGBUG: Why is FakeDriver setting the cursor location here? - if (IsValidLocation (col, row)) { - FakeConsole.CursorTop = row; - FakeConsole.CursorLeft = col; - } - } - public override void AddRune (Rune rune) { rune = MakePrintable (rune); diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 17b0ef8a32..4df207f276 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -641,12 +641,6 @@ public NetDriver () bool [] _dirtyLine; - public override void Move (int col, int row) - { - Col = col; - Row = row; - } - public override void AddRune (Rune rune) { if (Contents.Length != Rows * Cols * 3) { diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 93d26c839b..245f6ec019 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1448,13 +1448,7 @@ public override void UpdateOffScreen () } } } - - public override void Move (int col, int row) - { - Col = col; - Row = row; - } - + int GetOutputBufferPosition () { return Row * Cols + Col; diff --git a/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs b/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs index 03858df3e8..6f43bc0ae1 100644 --- a/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs +++ b/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs @@ -57,8 +57,6 @@ public void End_Cleans_Up (Type driverType) Console.BackgroundColor = ConsoleColor.Green; Assert.Equal (ConsoleColor.Green, Console.BackgroundColor); driver.Move (2, 3); - Assert.Equal (2, Console.CursorLeft); - Assert.Equal (3, Console.CursorTop); driver.End (); Assert.Equal (0, Console.CursorLeft); From 93d81a187c228e8ca0c0520b2c900aecb3ab8f56 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 8 May 2023 23:44:40 +0200 Subject: [PATCH 17/99] Made CurrentAttribute non-virtual --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 42 +-- .../CursesDriver/CursesDriver.cs | 17 +- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 13 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 64 ++--- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 244 ++++++++---------- 5 files changed, 158 insertions(+), 222 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 65ca4e8efa..2ec99a5e82 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -92,12 +92,12 @@ public abstract class ConsoleDriver { /// /// The leftmost column in the terminal. /// - public virtual int Left { get; internal set; } + public virtual int Left { get; internal set; } = 0; /// /// The topmost row in the terminal. /// - public virtual int Top { get; internal set; } + public virtual int Top { get; internal set; } = 0; /// /// Get the operating system clipboard. @@ -191,7 +191,12 @@ public static Rune MakePrintable (Rune c) /// Adds the to the display at the cursor position. /// /// String. - public abstract void AddStr (ustring str); + public virtual void AddStr (ustring str) + { + foreach (var rune in str) { + AddRune (rune); + } + } /// /// Prepare the driver and set the key and mouse events handlers. @@ -209,28 +214,28 @@ public static Rune MakePrintable (Rune c) public abstract void Refresh (); /// - /// Updates the location of the cursor position + /// Sets the position of the terminal cursor to and . /// public abstract void UpdateCursor (); /// - /// Retreive the cursor caret visibility + /// Gets the terminal cursor visibility. /// /// The current - /// true upon success + /// upon success public abstract bool GetCursorVisibility (out CursorVisibility visibility); /// - /// Change the cursor caret visibility + /// Sets the terminal cursor visibility. /// /// The wished - /// true upon success + /// upon success public abstract bool SetCursorVisibility (CursorVisibility visibility); /// - /// Ensure the cursor visibility + /// Determines if the terminal cursor should be visible or not and sets it accordingly. /// - /// true upon success + /// upon success public abstract bool EnsureCursorVisibility (); /// @@ -239,12 +244,7 @@ public static Rune MakePrintable (Rune c) public abstract void End (); /// - /// Resizes the clip area when the screen is resized. - /// - public abstract void ResizeScreen (); - - /// - /// Reset and recreate the contents and the driver buffer. + /// Clears the buffer and the driver buffer. /// public abstract void UpdateOffScreen (); @@ -254,9 +254,9 @@ public static Rune MakePrintable (Rune c) public abstract void UpdateScreen (); /// - /// The current attribute the driver is using. + /// The that will be used for the next or call. /// - public virtual Attribute CurrentAttribute { + public Attribute CurrentAttribute { get => _currentAttribute; set { if (!value.Initialized && value.HasValidColors && Application.Driver != null) { @@ -280,7 +280,7 @@ public virtual void SetAttribute (Attribute c) { CurrentAttribute = c; } - + /// /// Gets the foreground and background colors based on the value. /// @@ -379,7 +379,7 @@ public Rect Clip { get => _clip; set => _clip = value; } - + /// /// Start of mouse moves. @@ -390,7 +390,7 @@ public Rect Clip { /// Stop reporting mouses moves. /// public abstract void StopReportingMouseMoves (); - + Attribute _currentAttribute; /// diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 4484f31f7d..41ea3a59ed 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -18,15 +18,13 @@ namespace Terminal.Gui { internal class CursesDriver : ConsoleDriver { public override int Cols => Curses.Cols; public override int Rows => Curses.Lines; - public override int Left => 0; - public override int Top => 0; CursorVisibility? _initialCursorVisibility = null; CursorVisibility? _currentCursorVisibility = null; // If true, Move set Col and Row to an invalid location bool _atValidLocation; - + public override void Move (int col, int row) { base.Move (col, row); @@ -36,7 +34,7 @@ public override void Move (int col, int row) _atValidLocation = true; } else { // Not a valid location (outside screen or clip region) - // Move within the clip region, then AddStr will actually move to Col, Row + // Move within the clip region, then AddRune will actually move to Col, Row Curses.move (Clip.Y, Clip.X); _atValidLocation = false; } @@ -118,9 +116,7 @@ public override void AddRune (Rune rune) public override void AddStr (ustring str) { // TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly - foreach (var rune in str) { - AddRune (rune); - } + base.AddStr (str); } public override void Refresh () @@ -591,7 +587,7 @@ public override void Init (Action terminalResized) } - public override void ResizeScreen () + public virtual void ResizeScreen () { Clip = new Rect (0, 0, Cols, Rows); Curses.refresh (); @@ -602,9 +598,6 @@ public override void UpdateOffScreen () Contents = new int [Rows, Cols, 3]; for (int row = 0; row < Rows; row++) { for (int col = 0; col < Cols; col++) { - //Curses.move (row, col); - //Curses.attrset (Colors.TopLevel.Normal); - //Curses.addch ((int)(uint)' '); Contents [row, col, 0] = ' '; Contents [row, col, 1] = Colors.TopLevel.Normal; Contents [row, col, 2] = 0; @@ -728,7 +721,7 @@ public override void StopReportingMouseMoves () { Console.Out.Write (EscSeqUtils.DisableMouseEvents); } - + /// public override bool GetCursorVisibility (out CursorVisibility visibility) { diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index e6cacbb714..1e5c7f0b88 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -130,13 +130,6 @@ public override void AddRune (Rune rune) } } - public override void AddStr (ustring str) - { - foreach (var rune in str) { - AddRune (rune); - } - } - public override void End () { FakeConsole.ResetColor (); @@ -502,7 +495,7 @@ void ProcessResize () TerminalResized?.Invoke (); } - public override void ResizeScreen () + public virtual void ResizeScreen () { if (!EnableConsoleScrolling) { if (FakeConsole.WindowHeight > 0) { @@ -574,11 +567,13 @@ public override bool GetColors (int value, out Color foreground, out Color backg public override void UpdateCursor () { - if (!EnsureCursorVisibility ()) + if (!EnsureCursorVisibility ()) { return; + } // Prevents the exception of size changing during resizing. try { + // BUGBUG: Why is this using BufferWidth/Height and now Cols/Rows? if (Col >= 0 && Col < FakeConsole.BufferWidth && Row >= 0 && Row < FakeConsole.BufferHeight) { FakeConsole.SetCursorPosition (Col, Row); } diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 4df207f276..0c6a420e78 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -701,13 +701,6 @@ public override void AddRune (Rune rune) } } - public override void AddStr (ustring str) - { - foreach (var rune in str) { - AddRune (rune); - } - } - public override void End () { _mainLoop._netEvents.StopTasks (); @@ -776,7 +769,7 @@ public override void Init (Action terminalResized) StartReportingMouseMoves (); } - public override void ResizeScreen () + public virtual void ResizeScreen () { if (!EnableConsoleScrolling && Console.WindowHeight > 0) { // Not supported on Unix. @@ -1052,16 +1045,38 @@ private bool EnsureBufferSize () public override void UpdateCursor () { EnsureCursorVisibility (); - //Debug.WriteLine ($"Before - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) { SetCursorPosition (Col, Row); SetWindowPosition (0, Row); } - //Debug.WriteLine ($"WindowTop: {Console.WindowTop};WindowLeft: {Console.WindowLeft}"); - //Debug.WriteLine ($"After - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); } + public override bool GetCursorVisibility (out CursorVisibility visibility) + { + visibility = savedCursorVisibility ?? CursorVisibility.Default; + return visibility == CursorVisibility.Default; + } + + public override bool SetCursorVisibility (CursorVisibility visibility) + { + savedCursorVisibility = visibility; + return Console.CursorVisible = visibility == CursorVisibility.Default; + } + + public override bool EnsureCursorVisibility () + { + if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { + GetCursorVisibility (out CursorVisibility cursorVisibility); + savedCursorVisibility = cursorVisibility; + SetCursorVisibility (CursorVisibility.Invisible); + return false; + } + + SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default); + return savedCursorVisibility == CursorVisibility.Default; + } + public override void StartReportingMouseMoves () { Console.Out.Write (EscSeqUtils.EnableMouseEvents); @@ -1391,33 +1406,6 @@ MouseEvent ToDriverMouse (NetEvents.MouseEvent me) }; } - /// - public override bool GetCursorVisibility (out CursorVisibility visibility) - { - visibility = savedCursorVisibility ?? CursorVisibility.Default; - return visibility == CursorVisibility.Default; - } - - /// - public override bool SetCursorVisibility (CursorVisibility visibility) - { - savedCursorVisibility = visibility; - return Console.CursorVisible = visibility == CursorVisibility.Default; - } - - /// - public override bool EnsureCursorVisibility () - { - if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { - GetCursorVisibility (out CursorVisibility cursorVisibility); - savedCursorVisibility = cursorVisibility; - SetCursorVisibility (CursorVisibility.Invisible); - return false; - } - - SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default); - return savedCursorVisibility == CursorVisibility.Default; - } public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) { diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 245f6ec019..ef2d82b3bb 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -492,7 +492,6 @@ public static void MakeEmpty (ref SmallRect rect) public static void Update (ref SmallRect rect, short col, short row) { if (rect.Left == -1) { - //System.Diagnostics.Debugger.Log (0, "debug", $"damager From Empty {col},{row}\n"); rect.Left = rect.Right = col; rect.Bottom = rect.Top = row; return; @@ -507,7 +506,6 @@ public static void Update (ref SmallRect rect, short col, short row) rect.Top = row; if (row > rect.Bottom) rect.Bottom = row; - //System.Diagnostics.Debugger.Log (0, "debug", $"Expanding {rect.ToString ()}\n"); } public override string ToString () @@ -1376,79 +1374,6 @@ private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) return keyMod != Key.Null ? keyMod | key : key; } - public override void Init (Action terminalResized) - { - TerminalResized = terminalResized; - - try { - // Needed for Windows Terminal - // ESC [ ? 1047 h Activate xterm alternative buffer (no backscroll) - // ESC [ ? 1047 l Restore xterm working buffer (with backscroll) - // ESC [ ? 1048 h Save cursor position - // ESC [ ? 1048 l Restore cursor position - // ESC [ ? 1049 h Save cursor position and activate xterm alternative buffer (no backscroll) - // ESC [ ? 1049 l Restore cursor position and restore xterm working buffer (with backscroll) - // Per Issue #2264 using the alterantive screen buffer is required for Windows Terminal to not - // wipe out the backscroll buffer when the application exits. - Console.Out.Write ("\x1b[?1047h"); - - // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 - - var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); - Cols = winSize.Width; - Rows = winSize.Height; - WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - - CurrentAttribute = MakeColor (Color.White, Color.Black); - InitializeColorSchemes (); - - CurrentAttribute = MakeColor (Color.White, Color.Black); - InitializeColorSchemes (); - - ResizeScreen (); - UpdateOffScreen (); - } catch (Win32Exception e) { - throw new InvalidOperationException ("The Windows Console output window is not available.", e); - } - } - - public override void ResizeScreen () - { - _outputBuffer = new WindowsConsole.CharInfo [Rows * Cols]; - Clip = new Rect (0, 0, Cols, Rows); - _damageRegion = new WindowsConsole.SmallRect () { - Top = 0, - Left = 0, - Bottom = (short)Rows, - Right = (short)Cols - }; - WinConsole.ForceRefreshCursorVisibility (); - if (!EnableConsoleScrolling) { - // ANSI ESC "[xJ" Clears part of the screen. - // If n is 0 (or missing), clear from cursor to end of screen. - // If n is 1, clear from cursor to beginning of the screen. - // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). - // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer - // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer - Console.Out.Write ("\x1b[3J"); - } - } - - public override void UpdateOffScreen () - { - Contents = new int [Rows, Cols, 3]; - for (int row = 0; row < Rows; row++) { - for (int col = 0; col < Cols; col++) { - int position = row * Cols + col; - _outputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal; - _outputBuffer [position].Char.UnicodeChar = ' '; - Contents [row, col, 0] = _outputBuffer [position].Char.UnicodeChar; - Contents [row, col, 1] = _outputBuffer [position].Attributes; - Contents [row, col, 2] = 0; - } - } - } - int GetOutputBufferPosition () { return Row * Cols + Col; @@ -1537,13 +1462,6 @@ public override void AddRune (Rune nStackrune) } } - public override void AddStr (ustring str) - { - foreach (var rune in str) { - AddRune (rune); - } - } - public override Attribute MakeColor (Color foreground, Color background) { return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); @@ -1558,32 +1476,95 @@ Attribute MakeColor (ConsoleColor f, ConsoleColor b) background: (Color)b ); } + + public override bool GetColors (int value, out Color foreground, out Color background) + { + bool hasColor = false; + foreground = default; + background = default; + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains ((value >> 4) & 0xffff)) { + hasColor = true; + background = (Color)(ConsoleColor)((value >> 4) & 0xffff); + } + if (values.Contains (value - ((int)background << 4))) { + hasColor = true; + foreground = (Color)(ConsoleColor)(value - ((int)background << 4)); + } + return hasColor; + } - public override void Refresh () + + public override void Init (Action terminalResized) { - UpdateScreen (); + TerminalResized = terminalResized; - WinConsole.SetInitialCursorVisibility (); + try { + // Needed for Windows Terminal + // ESC [ ? 1047 h Activate xterm alternative buffer (no backscroll) + // ESC [ ? 1047 l Restore xterm working buffer (with backscroll) + // ESC [ ? 1048 h Save cursor position + // ESC [ ? 1048 l Restore cursor position + // ESC [ ? 1049 h Save cursor position and activate xterm alternative buffer (no backscroll) + // ESC [ ? 1049 l Restore cursor position and restore xterm working buffer (with backscroll) + // Per Issue #2264 using the alterantive screen buffer is required for Windows Terminal to not + // wipe out the backscroll buffer when the application exits. + Console.Out.Write ("\x1b[?1047h"); - UpdateCursor (); -#if false - var bufferCoords = new WindowsConsole.Coord (){ - X = (short)Clip.Width, - Y = (short)Clip.Height - }; + var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); + Cols = winSize.Width; + Rows = winSize.Height; + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); + + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitializeColorSchemes (); + + ResizeScreen (); + UpdateOffScreen (); + } catch (Win32Exception e) { + throw new InvalidOperationException ("The Windows Console output window is not available.", e); + } + } - var window = new WindowsConsole.SmallRect (){ + public virtual void ResizeScreen () + { + _outputBuffer = new WindowsConsole.CharInfo [Rows * Cols]; + Clip = new Rect (0, 0, Cols, Rows); + _damageRegion = new WindowsConsole.SmallRect () { Top = 0, Left = 0, - Right = (short)Clip.Right, - Bottom = (short)Clip.Bottom + Bottom = (short)Rows, + Right = (short)Cols }; - - UpdateCursor(); - WinConsole.WriteToConsole (OutputBuffer, bufferCoords, window); -#endif + WinConsole.ForceRefreshCursorVisibility (); + if (!EnableConsoleScrolling) { + // ANSI ESC "[xJ" Clears part of the screen. + // If n is 0 (or missing), clear from cursor to end of screen. + // If n is 1, clear from cursor to beginning of the screen. + // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer + Console.Out.Write ("\x1b[3J"); + } } + public override void UpdateOffScreen () + { + Contents = new int [Rows, Cols, 3]; + for (int row = 0; row < Rows; row++) { + for (int col = 0; col < Cols; col++) { + int position = row * Cols + col; + _outputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal; + _outputBuffer [position].Char.UnicodeChar = ' '; + Contents [row, col, 0] = _outputBuffer [position].Char.UnicodeChar; + Contents [row, col, 1] = _outputBuffer [position].Attributes; + Contents [row, col, 2] = 0; + } + } + } + public override void UpdateScreen () { if (_damageRegion.Left == -1) { @@ -1602,19 +1583,17 @@ public override void UpdateScreen () Y = (short)Clip.Height }; - //var window = new WindowsConsole.SmallRect () { - // Top = 0, - // Left = 0, - // Right = (short)Clip.Right, - // Bottom = (short)Clip.Bottom - //}; - WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion); - - // System.Diagnostics.Debugger.Log (0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n"); WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); } + public override void Refresh () + { + UpdateScreen (); + WinConsole.SetInitialCursorVisibility (); + UpdateCursor (); + } + CursorVisibility savedCursorVisibility; public override void UpdateCursor () @@ -1634,24 +1613,6 @@ public override void UpdateCursor () WinConsole.SetCursorPosition (position); } - public override void End () - { - WinConsole.Cleanup (); - WinConsole = null; - - // Needed for Windows Terminal - // Clear the alternative screen buffer from the cursor to the - // end of the screen. - // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE - // backbuffer! So we need to use [0J instead. - Console.Out.Write ("\x1b[0J"); - - // Disable alternative screen buffer. - Console.Out.Write ("\x1b[?1047l"); - - // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 - } - /// public override bool GetCursorVisibility (out CursorVisibility visibility) { @@ -1723,24 +1684,23 @@ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool al ProcessInput (input); } } - - public override bool GetColors (int value, out Color foreground, out Color background) + + public override void End () { - bool hasColor = false; - foreground = default; - background = default; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains ((value >> 4) & 0xffff)) { - hasColor = true; - background = (Color)(ConsoleColor)((value >> 4) & 0xffff); - } - if (values.Contains (value - ((int)background << 4))) { - hasColor = true; - foreground = (Color)(ConsoleColor)(value - ((int)background << 4)); - } - return hasColor; + WinConsole.Cleanup (); + WinConsole = null; + + // Needed for Windows Terminal + // Clear the alternative screen buffer from the cursor to the + // end of the screen. + // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE + // backbuffer! So we need to use [0J instead. + Console.Out.Write ("\x1b[0J"); + + // Disable alternative screen buffer. + Console.Out.Write ("\x1b[?1047l"); + + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 } #region Not Implemented From af8a6687af04326adb1f372a72fc4177acb23f9a Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 9 May 2023 00:06:36 +0200 Subject: [PATCH 18/99] Made SetAttribute non-virtual --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 5 +- .../CursesDriver/CursesDriver.cs | 78 +++++++++---------- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 2ec99a5e82..cafa2a8f87 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -276,10 +276,7 @@ public Attribute CurrentAttribute { /// Implementations should call base.SetAttribute(c). /// /// C. - public virtual void SetAttribute (Attribute c) - { - CurrentAttribute = c; - } + public void SetAttribute (Attribute c) => CurrentAttribute = c; /// /// Gets the foreground and background colors based on the value. diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 41ea3a59ed..946825c75f 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -61,10 +61,12 @@ public override void AddRune (Rune rune) Contents [Row, Col - 1, 0] = c; Contents [Row, Col - 1, 1] = CurrentAttribute; Contents [Row, Col - 1, 2] = 1; - + Curses.attrset (Contents [Row, Col - 1, 1]); Curses.mvaddch (Row, Col - 1, c); - } else { + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 1; + if (runeWidth < 2 && Col > 0 && Rune.ColumnWidth ((char)Contents [Row, Col - 1, 0]) > 1) { @@ -90,11 +92,12 @@ public override void AddRune (Rune rune) Curses.addch (System.Text.Rune.ReplacementChar.Value); Contents [Row, Col, 0] = System.Text.Rune.ReplacementChar.Value; } else { + var curAttr = CurrentAttribute; + Curses.attrset (Contents [Row, Col, 1]); Curses.addch ((int)(uint)rune); + Curses.attrset (curAttr); Contents [Row, Col, 0] = (int)(uint)rune; } - Contents [Row, Col, 1] = CurrentAttribute; - Contents [Row, Col, 2] = 1; } } else { _atValidLocation = false; @@ -147,13 +150,7 @@ public override void End () } public override void UpdateScreen () => _window.redrawwin (); - - public override void SetAttribute (Attribute c) - { - base.SetAttribute (c); - Curses.attrset (CurrentAttribute); - } - + public Curses.Window _window; /// @@ -551,35 +548,36 @@ public override void Init (Action terminalResized) InitializeColorSchemes (); } else { - InitializeColorSchemes (false); - - // BUGBUG: This is a hack to make the colors work on the Mac? - // The new Theme support overwrites these colors, so this is not needed? - Colors.TopLevel.Normal = Curses.COLOR_GREEN; - Colors.TopLevel.Focus = Curses.COLOR_WHITE; - Colors.TopLevel.HotNormal = Curses.COLOR_YELLOW; - Colors.TopLevel.HotFocus = Curses.COLOR_YELLOW; - Colors.TopLevel.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - Colors.Base.Normal = Curses.A_NORMAL; - Colors.Base.Focus = Curses.A_REVERSE; - Colors.Base.HotNormal = Curses.A_BOLD; - Colors.Base.HotFocus = Curses.A_BOLD | Curses.A_REVERSE; - Colors.Base.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - Colors.Menu.Normal = Curses.A_REVERSE; - Colors.Menu.Focus = Curses.A_NORMAL; - Colors.Menu.HotNormal = Curses.A_BOLD; - Colors.Menu.HotFocus = Curses.A_NORMAL; - Colors.Menu.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - Colors.Dialog.Normal = Curses.A_REVERSE; - Colors.Dialog.Focus = Curses.A_NORMAL; - Colors.Dialog.HotNormal = Curses.A_BOLD; - Colors.Dialog.HotFocus = Curses.A_NORMAL; - Colors.Dialog.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - Colors.Error.Normal = Curses.A_BOLD; - Colors.Error.Focus = Curses.A_BOLD | Curses.A_REVERSE; - Colors.Error.HotNormal = Curses.A_BOLD | Curses.A_REVERSE; - Colors.Error.HotFocus = Curses.A_REVERSE; - Colors.Error.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does."); + //InitializeColorSchemes (false); + + //// BUGBUG: This is a hack to make the colors work on the Mac? + //// The new Theme support overwrites these colors, so this is not needed? + //Colors.TopLevel.Normal = Curses.COLOR_GREEN; + //Colors.TopLevel.Focus = Curses.COLOR_WHITE; + //Colors.TopLevel.HotNormal = Curses.COLOR_YELLOW; + //Colors.TopLevel.HotFocus = Curses.COLOR_YELLOW; + //Colors.TopLevel.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + //Colors.Base.Normal = Curses.A_NORMAL; + //Colors.Base.Focus = Curses.A_REVERSE; + //Colors.Base.HotNormal = Curses.A_BOLD; + //Colors.Base.HotFocus = Curses.A_BOLD | Curses.A_REVERSE; + //Colors.Base.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + //Colors.Menu.Normal = Curses.A_REVERSE; + //Colors.Menu.Focus = Curses.A_NORMAL; + //Colors.Menu.HotNormal = Curses.A_BOLD; + //Colors.Menu.HotFocus = Curses.A_NORMAL; + //Colors.Menu.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + //Colors.Dialog.Normal = Curses.A_REVERSE; + //Colors.Dialog.Focus = Curses.A_NORMAL; + //Colors.Dialog.HotNormal = Curses.A_BOLD; + //Colors.Dialog.HotFocus = Curses.A_NORMAL; + //Colors.Dialog.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + //Colors.Error.Normal = Curses.A_BOLD; + //Colors.Error.Focus = Curses.A_BOLD | Curses.A_REVERSE; + //Colors.Error.HotNormal = Curses.A_BOLD | Curses.A_REVERSE; + //Colors.Error.HotFocus = Curses.A_REVERSE; + //Colors.Error.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; } ResizeScreen (); From bf25b7923f12683bc24258bf3ebc267884e40e18 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 9 May 2023 00:28:09 +0200 Subject: [PATCH 19/99] Moved clipboard code out --- Terminal.Gui/Clipboard/Clipboard.cs | 234 ++++-- Terminal.Gui/Clipboard/IClipboard.cs | 4 - Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 714 +++++++----------- .../CursesDriver/ClipboardImpl.cs | 225 ++++++ .../CursesDriver/CursesDriver.cs | 177 +---- .../ConsoleDrivers/CursorVisibility.cs | 59 ++ Terminal.Gui/ConsoleDrivers/NetDriver.cs | 4 +- 7 files changed, 735 insertions(+), 682 deletions(-) create mode 100644 Terminal.Gui/ConsoleDrivers/CursesDriver/ClipboardImpl.cs create mode 100644 Terminal.Gui/ConsoleDrivers/CursorVisibility.cs diff --git a/Terminal.Gui/Clipboard/Clipboard.cs b/Terminal.Gui/Clipboard/Clipboard.cs index 4aed183da0..1510c4f5e1 100644 --- a/Terminal.Gui/Clipboard/Clipboard.cs +++ b/Terminal.Gui/Clipboard/Clipboard.cs @@ -1,100 +1,172 @@ using NStack; using System; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Terminal.Gui; + +/// +/// Provides cut, copy, and paste support for the OS clipboard. +/// +/// +/// +/// On Windows, the class uses the Windows Clipboard APIs via P/Invoke. +/// +/// +/// On Linux, when not running under Windows Subsystem for Linux (WSL), +/// the class uses the xclip command line tool. If xclip is not installed, +/// the clipboard will not work. +/// +/// +/// On Linux, when running under Windows Subsystem for Linux (WSL), +/// the class launches Windows' powershell.exe via WSL interop and uses the +/// "Set-Clipboard" and "Get-Clipboard" Powershell CmdLets. +/// +/// +/// On the Mac, the class uses the MacO OS X pbcopy and pbpaste command line tools +/// and the Mac clipboard APIs vai P/Invoke. +/// +/// +public static class Clipboard { + static ustring contents; -namespace Terminal.Gui { /// - /// Provides cut, copy, and paste support for the OS clipboard. + /// Gets (copies from) or sets (pastes to) the contents of the OS clipboard. /// - /// - /// - /// On Windows, the class uses the Windows Clipboard APIs via P/Invoke. - /// - /// - /// On Linux, when not running under Windows Subsystem for Linux (WSL), - /// the class uses the xclip command line tool. If xclip is not installed, - /// the clipboard will not work. - /// - /// - /// On Linux, when running under Windows Subsystem for Linux (WSL), - /// the class launches Windows' powershell.exe via WSL interop and uses the - /// "Set-Clipboard" and "Get-Clipboard" Powershell CmdLets. - /// - /// - /// On the Mac, the class uses the MacO OS X pbcopy and pbpaste command line tools - /// and the Mac clipboard APIs vai P/Invoke. - /// - /// - public static class Clipboard { - static ustring contents; - - /// - /// Gets (copies from) or sets (pastes to) the contents of the OS clipboard. - /// - public static ustring Contents { - get { - try { - if (IsSupported) { - return contents = ustring.Make (Application.Driver.Clipboard.GetClipboardData ()); - } else { - return contents; - } - } catch (Exception) { + public static ustring Contents { + get { + try { + if (IsSupported) { + return contents = ustring.Make (Application.Driver.Clipboard.GetClipboardData ()); + } else { return contents; } + } catch (Exception) { + return contents; } - set { - try { - if (IsSupported) { - if (value == null) { - value = string.Empty; - } - Application.Driver.Clipboard.SetClipboardData (value.ToString ()); + } + set { + try { + if (IsSupported) { + if (value == null) { + value = string.Empty; } - contents = value; - } catch (NotSupportedException e) { - throw e; - } catch (Exception) { - contents = value; + Application.Driver.Clipboard.SetClipboardData (value.ToString ()); } + contents = value; + } catch (NotSupportedException e) { + throw e; + } catch (Exception) { + contents = value; } } + } - /// - /// Returns true if the environmental dependencies are in place to interact with the OS clipboard. - /// - /// - /// - public static bool IsSupported { get => Application.Driver.Clipboard.IsSupported; } - - /// - /// Copies the contents of the OS clipboard to if possible. - /// - /// The contents of the OS clipboard if successful, if not. - /// the OS clipboard was retrieved, otherwise. - public static bool TryGetClipboardData (out string result) - { - if (IsSupported && Application.Driver.Clipboard.TryGetClipboardData (out result)) { - if (contents != result) { - contents = result; - } - return true; + /// + /// Returns true if the environmental dependencies are in place to interact with the OS clipboard. + /// + /// + /// + public static bool IsSupported { get => Application.Driver.Clipboard.IsSupported; } + + /// + /// Copies the contents of the OS clipboard to if possible. + /// + /// The contents of the OS clipboard if successful, if not. + /// the OS clipboard was retrieved, otherwise. + public static bool TryGetClipboardData (out string result) + { + if (IsSupported && Application.Driver.Clipboard.TryGetClipboardData (out result)) { + if (contents != result) { + contents = result; } - result = string.Empty; - return false; + return true; } + result = string.Empty; + return false; + } - /// - /// Pastes the to the OS clipboard if possible. - /// - /// The text to paste to the OS clipboard. - /// the OS clipboard was set, otherwise. - public static bool TrySetClipboardData (string text) - { - if (IsSupported && Application.Driver.Clipboard.TrySetClipboardData (text)) { - contents = text; - return true; - } - return false; + /// + /// Pastes the to the OS clipboard if possible. + /// + /// The text to paste to the OS clipboard. + /// the OS clipboard was set, otherwise. + public static bool TrySetClipboardData (string text) + { + if (IsSupported && Application.Driver.Clipboard.TrySetClipboardData (text)) { + contents = text; + return true; } + return false; } } + +/// +/// Helper class for console drivers to invoke shell commands to interact with the clipboard. +/// Used primarily by CursesDriver, but also used in Unit tests which is why it is in +/// ConsoleDriver.cs. +/// +internal static class ClipboardProcessRunner { + public static (int exitCode, string result) Bash (string commandLine, string inputText = "", bool waitForOutput = false) + { + var arguments = $"-c \"{commandLine}\""; + var (exitCode, result) = Process ("bash", arguments, inputText, waitForOutput); + + return (exitCode, result.TrimEnd ()); + } + + public static (int exitCode, string result) Process (string cmd, string arguments, string input = null, bool waitForOutput = true) + { + var output = string.Empty; + + using (Process process = new Process { + StartInfo = new ProcessStartInfo { + FileName = cmd, + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }) { + var eventHandled = new TaskCompletionSource (); + process.Start (); + if (!string.IsNullOrEmpty (input)) { + process.StandardInput.Write (input); + process.StandardInput.Close (); + } + + if (!process.WaitForExit (5000)) { + var timeoutError = $@"Process timed out. Command line: {process.StartInfo.FileName} {process.StartInfo.Arguments}."; + throw new TimeoutException (timeoutError); + } + + if (waitForOutput && process.StandardOutput.Peek () != -1) { + output = process.StandardOutput.ReadToEnd (); + } + + if (process.ExitCode > 0) { + output = $@"Process failed to run. Command line: {cmd} {arguments}. + Output: {output} + Error: {process.StandardError.ReadToEnd ()}"; + } + + return (process.ExitCode, output); + } + } + + public static bool DoubleWaitForExit (this System.Diagnostics.Process process) + { + var result = process.WaitForExit (500); + if (result) { + process.WaitForExit (); + } + return result; + } + + public static bool FileExists (this string value) + { + return !string.IsNullOrEmpty (value) && !value.Contains ("not found"); + } +} \ No newline at end of file diff --git a/Terminal.Gui/Clipboard/IClipboard.cs b/Terminal.Gui/Clipboard/IClipboard.cs index 9619d8125b..3b2b5f7be2 100644 --- a/Terminal.Gui/Clipboard/IClipboard.cs +++ b/Terminal.Gui/Clipboard/IClipboard.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Terminal.Gui { /// diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index cafa2a8f87..fb29666fb0 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -3,499 +3,347 @@ // using NStack; using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; -using System.Threading.Tasks; - -namespace Terminal.Gui { - /// - /// Cursors Visibility that are displayed - /// - // - // Hexa value are set as 0xAABBCCDD where : - // - // AA stand for the TERMINFO DECSUSR parameter value to be used under Linux & MacOS - // BB stand for the NCurses curs_set parameter value to be used under Linux & MacOS - // CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows - // DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows - // - public enum CursorVisibility { - /// - /// Cursor caret has default - /// - /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking. - Default = 0x00010119, - - /// - /// Cursor caret is hidden - /// - Invisible = 0x03000019, - - /// - /// Cursor caret is normally shown as a blinking underline bar _ - /// - Underline = 0x03010119, - - /// - /// Cursor caret is normally shown as a underline bar _ - /// - /// Under Windows, this is equivalent to - UnderlineFix = 0x04010119, - - /// - /// Cursor caret is displayed a blinking vertical bar | - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - Vertical = 0x05010119, - /// - /// Cursor caret is displayed a blinking vertical bar | - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - VerticalFix = 0x06010119, +namespace Terminal.Gui; - /// - /// Cursor caret is displayed as a blinking block ▉ - /// - Box = 0x01020164, +/// +/// ConsoleDriver is an abstract class that defines the requirements for a console driver. +/// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. +/// +public abstract class ConsoleDriver { + /// + /// The handler fired when the terminal is resized. + /// + protected Action TerminalResized; - /// - /// Cursor caret is displayed a block ▉ - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - BoxFix = 0x02020164, - } + /// + /// The number of columns visible in the terminal. + /// + public virtual int Cols { get; internal set; } /// - /// ConsoleDriver is an abstract class that defines the requirements for a console driver. - /// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. + /// The number of rows visible in the terminal. /// - public abstract class ConsoleDriver { - /// - /// The handler fired when the terminal is resized. - /// - protected Action TerminalResized; + public virtual int Rows { get; internal set; } - /// - /// The number of columns visible in the terminal. - /// - public virtual int Cols { get; internal set; } + /// + /// The leftmost column in the terminal. + /// + public virtual int Left { get; internal set; } = 0; - /// - /// The number of rows visible in the terminal. - /// - public virtual int Rows { get; internal set; } + /// + /// The topmost row in the terminal. + /// + public virtual int Top { get; internal set; } = 0; - /// - /// The leftmost column in the terminal. - /// - public virtual int Left { get; internal set; } = 0; + /// + /// Get the operating system clipboard. + /// + public IClipboard Clipboard { get; internal set; } - /// - /// The topmost row in the terminal. - /// - public virtual int Top { get; internal set; } = 0; + /// + /// + /// If (the default) the height of the Terminal.Gui application () + /// tracks to the height of the visible console view when the console is resized. In this case + /// scrolling in the console will be disabled and all will remain visible. + /// + /// + /// If then height of the Terminal.Gui application only tracks + /// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). + /// In this case console scrolling is enabled and the contents ( high) will scroll + /// as the console scrolls. + /// + /// + /// + /// NOTE: This functionaliy is currently broken on Windows Terminal. + /// + public bool EnableConsoleScrolling { get; set; } - /// - /// Get the operating system clipboard. - /// - public IClipboard Clipboard { get; internal set; } + /// + /// The contents of the application output. The driver outputs this buffer to the terminal when + /// is called. + /// + /// The format of the array is rows, columns, and 3 values on the last column: Rune, Attribute and Dirty Flag + /// + /// + public int [,,] Contents { get; internal set; } - /// - /// - /// If (the default) the height of the Terminal.Gui application () - /// tracks to the height of the visible console view when the console is resized. In this case - /// scrolling in the console will be disabled and all will remain visible. - /// - /// - /// If then height of the Terminal.Gui application only tracks - /// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). - /// In this case console scrolling is enabled and the contents ( high) will scroll - /// as the console scrolls. - /// - /// - /// - /// NOTE: This functionaliy is currently broken on Windows Terminal. - /// - public bool EnableConsoleScrolling { get; set; } + /// + /// Initializes the driver + /// + /// Method to invoke when the terminal is resized. + public abstract void Init (Action terminalResized); - /// - /// The contents of the application output. The driver outputs this buffer to the terminal when - /// is called. - /// - /// The format of the array is rows, columns, and 3 values on the last column: Rune, Attribute and Dirty Flag - /// - /// - public int [,,] Contents { get; internal set; } + /// + /// Gets the column last set by . and + /// are used by and to determine where to add content. + /// + public int Col { get; internal set; } - /// - /// Initializes the driver - /// - /// Method to invoke when the terminal is resized. - public abstract void Init (Action terminalResized); + /// + /// Gets the row last set by . and + /// are used by and to determine where to add content. + /// + public int Row { get; internal set; } - /// - /// Gets the column last set by . and - /// are used by and to determine where to add content. - /// - public int Col { get; internal set; } + /// + /// Updates and to the specified column and row in . + /// Used by and to determine where to add content. + /// + /// + /// This does not move the cursor on the screen, it only updates the internal state of the driver. + /// + /// Column to move to. + /// Row to move to. + public virtual void Move (int col, int row) + { + Col = col; + Row = row; + } - /// - /// Gets the row last set by . and - /// are used by and to determine where to add content. - /// - public int Row { get; internal set; } + /// + /// Adds the specified rune to the display at the current cursor position. + /// + /// Rune to add. + public abstract void AddRune (Rune rune); - /// - /// Updates and to the specified column and row in . - /// Used by and to determine where to add content. - /// - /// - /// This does not move the cursor on the screen, it only updates the internal state of the driver. - /// - /// Column to move to. - /// Row to move to. - public virtual void Move (int col, int row) - { - Col = col; - Row = row; + /// + /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 + /// to equivalent, printable, Unicode chars. + /// + /// Rune to translate + /// + public static Rune MakePrintable (Rune c) + { + if (c <= 0x1F || (c >= 0X7F && c <= 0x9F)) { + // ASCII (C0) control characters. + // C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1) + return new Rune (c + 0x2400); } - /// - /// Adds the specified rune to the display at the current cursor position. - /// - /// Rune to add. - public abstract void AddRune (Rune rune); - - /// - /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 - /// to equivalent, printable, Unicode chars. - /// - /// Rune to translate - /// - public static Rune MakePrintable (Rune c) - { - if (c <= 0x1F || (c >= 0X7F && c <= 0x9F)) { - // ASCII (C0) control characters. - // C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1) - return new Rune (c + 0x2400); - } - - return c; - } + return c; + } - /// - /// Adds the to the display at the cursor position. - /// - /// String. - public virtual void AddStr (ustring str) - { - foreach (var rune in str) { - AddRune (rune); - } + /// + /// Adds the to the display at the cursor position. + /// + /// String. + public virtual void AddStr (ustring str) + { + foreach (var rune in str) { + AddRune (rune); } + } - /// - /// Prepare the driver and set the key and mouse events handlers. - /// - /// The main loop. - /// The handler for ProcessKey - /// The handler for key down events - /// The handler for key up events - /// The handler for mouse events - public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler); + /// + /// Prepare the driver and set the key and mouse events handlers. + /// + /// The main loop. + /// The handler for ProcessKey + /// The handler for key down events + /// The handler for key up events + /// The handler for mouse events + public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler); - /// - /// Updates the screen to reflect all the changes that have been done to the display buffer - /// - public abstract void Refresh (); + /// + /// Updates the screen to reflect all the changes that have been done to the display buffer + /// + public abstract void Refresh (); - /// - /// Sets the position of the terminal cursor to and . - /// - public abstract void UpdateCursor (); + /// + /// Sets the position of the terminal cursor to and . + /// + public abstract void UpdateCursor (); - /// - /// Gets the terminal cursor visibility. - /// - /// The current - /// upon success - public abstract bool GetCursorVisibility (out CursorVisibility visibility); + /// + /// Gets the terminal cursor visibility. + /// + /// The current + /// upon success + public abstract bool GetCursorVisibility (out CursorVisibility visibility); - /// - /// Sets the terminal cursor visibility. - /// - /// The wished - /// upon success - public abstract bool SetCursorVisibility (CursorVisibility visibility); + /// + /// Sets the terminal cursor visibility. + /// + /// The wished + /// upon success + public abstract bool SetCursorVisibility (CursorVisibility visibility); - /// - /// Determines if the terminal cursor should be visible or not and sets it accordingly. - /// - /// upon success - public abstract bool EnsureCursorVisibility (); + /// + /// Determines if the terminal cursor should be visible or not and sets it accordingly. + /// + /// upon success + public abstract bool EnsureCursorVisibility (); - /// - /// Ends the execution of the console driver. - /// - public abstract void End (); + /// + /// Ends the execution of the console driver. + /// + public abstract void End (); - /// - /// Clears the buffer and the driver buffer. - /// - public abstract void UpdateOffScreen (); + /// + /// Clears the buffer and the driver buffer. + /// + public abstract void UpdateOffScreen (); - /// - /// Redraws the physical screen with the contents that have been queued up via any of the printing commands. - /// - public abstract void UpdateScreen (); + /// + /// Redraws the physical screen with the contents that have been queued up via any of the printing commands. + /// + public abstract void UpdateScreen (); - /// - /// The that will be used for the next or call. - /// - public Attribute CurrentAttribute { - get => _currentAttribute; - set { - if (!value.Initialized && value.HasValidColors && Application.Driver != null) { - CurrentAttribute = Application.Driver.MakeAttribute (value.Foreground, value.Background); - return; - } - if (!value.Initialized) Debug.WriteLine ("ConsoleDriver.CurrentAttribute: Attributes must be initialized before use."); - - _currentAttribute = value; + /// + /// The that will be used for the next or call. + /// + public Attribute CurrentAttribute { + get => _currentAttribute; + set { + if (!value.Initialized && value.HasValidColors && Application.Driver != null) { + CurrentAttribute = Application.Driver.MakeAttribute (value.Foreground, value.Background); + return; } - } + if (!value.Initialized) Debug.WriteLine ("ConsoleDriver.CurrentAttribute: Attributes must be initialized before use."); - /// - /// Selects the specified attribute as the attribute to use for future calls to AddRune and AddString. - /// - /// - /// Implementations should call base.SetAttribute(c). - /// - /// C. - public void SetAttribute (Attribute c) => CurrentAttribute = c; + _currentAttribute = value; + } + } - /// - /// Gets the foreground and background colors based on the value. - /// - /// The value. - /// The foreground. - /// The background. - /// - public abstract bool GetColors (int value, out Color foreground, out Color background); + /// + /// Selects the specified attribute as the attribute to use for future calls to AddRune and AddString. + /// + /// + /// Implementations should call base.SetAttribute(c). + /// + /// C. + public void SetAttribute (Attribute c) => CurrentAttribute = c; - /// - /// Allows sending keys without typing on a keyboard. - /// - /// The character key. - /// The key. - /// If shift key is sending. - /// If alt key is sending. - /// If control key is sending. - public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control); + /// + /// Gets the foreground and background colors based on a platform-dependent color value. + /// + /// The platform-dependent color value. + /// The foreground. + /// The background. + /// + public abstract bool GetColors (int value, out Color foreground, out Color background); - /// - /// Set the handler when the terminal is resized. - /// - /// - public void SetTerminalResized (Action terminalResized) - { - TerminalResized = terminalResized; - } + /// + /// Simulates a key press. + /// + /// The key character. + /// The key. + /// If simulates the Shift key being pressed. + /// If simulates the Alt key being pressed. + /// If simulates the Ctrl key being pressed. + public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl); - /// - /// Fills the specified rectangle with the specified rune. - /// - /// - /// - public virtual void FillRect (Rect rect, System.Rune rune = default) - { - for (var r = rect.Y; r < rect.Y + rect.Height; r++) { - for (var c = rect.X; c < rect.X + rect.Width; c++) { - Application.Driver.Move (c, r); - Application.Driver.AddRune (rune == default ? ' ' : rune); - } + /// + /// Fills the specified rectangle with the specified rune. + /// + /// + /// + public void FillRect (Rect rect, Rune rune = default) + { + for (var r = rect.Y; r < rect.Y + rect.Height; r++) { + for (var c = rect.X; c < rect.X + rect.Width; c++) { + Application.Driver.Move (c, r); + Application.Driver.AddRune (rune == default ? ' ' : rune); } } + } + /// + /// Enables diagnostic functions + /// + [Flags] + public enum DiagnosticFlags : uint { /// - /// Enables diagnostic functions - /// - [Flags] - public enum DiagnosticFlags : uint { - /// - /// All diagnostics off - /// - Off = 0b_0000_0000, - /// - /// When enabled, will draw a - /// ruler in the frame for any side with a padding value greater than 0. - /// - FrameRuler = 0b_0000_0001, - /// - /// When enabled, will draw a - /// 'L', 'R', 'T', and 'B' when clearing 's instead of ' '. - /// - FramePadding = 0b_0000_0010, - } - - /// - /// Set flags to enable/disable diagnostics. + /// All diagnostics off /// - public static DiagnosticFlags Diagnostics { get; set; } - + Off = 0b_0000_0000, /// - /// Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver. + /// When enabled, will draw a + /// ruler in the frame for any side with a padding value greater than 0. /// - /// This is only implemented in . - public abstract void Suspend (); - - Rect _clip; - + FrameRuler = 0b_0000_0001, /// - /// Tests whether the specified coordinate are valid for drawing. + /// When enabled, will draw a + /// 'L', 'R', 'T', and 'B' when clearing 's instead of ' '. /// - /// The column. - /// The row. - /// if the coordinate is outside of the - /// screen bounds or outside of . otherwise. - public bool IsValidLocation (int col, int row) => - col >= 0 && row >= 0 && - col < Cols && row < Rows && - Clip.Contains (col, row); - - /// - /// Gets or sets the clip rectangle that and are - /// subject to. - /// - /// The rectangle describing the bounds of . - public Rect Clip { - get => _clip; - set => _clip = value; - } - + FramePadding = 0b_0000_0010, + } - /// - /// Start of mouse moves. - /// - public abstract void StartReportingMouseMoves (); + /// + /// Set flags to enable/disable diagnostics. + /// + public static DiagnosticFlags Diagnostics { get; set; } - /// - /// Stop reporting mouses moves. - /// - public abstract void StopReportingMouseMoves (); + /// + /// Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver. + /// + /// This is only implemented in . + public abstract void Suspend (); - Attribute _currentAttribute; + Rect _clip; - /// - /// Make the attribute for the foreground and background colors. - /// - /// Foreground. - /// Background. - /// - public virtual Attribute MakeAttribute (Color fore, Color back) - { - return MakeColor (fore, back); - } + /// + /// Tests whether the specified coordinate are valid for drawing. + /// + /// The column. + /// The row. + /// if the coordinate is outside of the + /// screen bounds or outside of . otherwise. + public bool IsValidLocation (int col, int row) => + col >= 0 && row >= 0 && + col < Cols && row < Rows && + Clip.Contains (col, row); - /// - /// Gets the current . - /// - /// The current attribute. - public Attribute GetAttribute () => CurrentAttribute; + /// + /// Gets or sets the clip rectangle that and are + /// subject to. + /// + /// The rectangle describing the bounds of . + public Rect Clip { + get => _clip; + set => _clip = value; + } - /// - /// Make the for the . - /// - /// The foreground color. - /// The background color. - /// The attribute for the foreground and background colors. - public abstract Attribute MakeColor (Color foreground, Color background); + Attribute _currentAttribute; - /// - /// Ensures all s in are correctly - /// initialized by the driver. - /// - /// Flag indicating if colors are supported (not used). - public void InitializeColorSchemes (bool supportsColors = true) - { - // Ensure all Attributes are initialized by the driver - foreach (var s in Colors.ColorSchemes) { - s.Value.Initialize (); - } - } + /// + /// Make the attribute for the foreground and background colors. + /// + /// Foreground. + /// Background. + /// + public virtual Attribute MakeAttribute (Color fore, Color back) + { + return MakeColor (fore, back); } /// - /// Helper class for console drivers to invoke shell commands to interact with the clipboard. - /// Used primarily by CursesDriver, but also used in Unit tests which is why it is in - /// ConsoleDriver.cs. + /// Gets the current . /// - internal static class ClipboardProcessRunner { - public static (int exitCode, string result) Bash (string commandLine, string inputText = "", bool waitForOutput = false) - { - var arguments = $"-c \"{commandLine}\""; - var (exitCode, result) = Process ("bash", arguments, inputText, waitForOutput); + /// The current attribute. + public Attribute GetAttribute () => CurrentAttribute; - return (exitCode, result.TrimEnd ()); - } - - public static (int exitCode, string result) Process (string cmd, string arguments, string input = null, bool waitForOutput = true) - { - var output = string.Empty; - - using (Process process = new Process { - StartInfo = new ProcessStartInfo { - FileName = cmd, - Arguments = arguments, - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true, - UseShellExecute = false, - CreateNoWindow = true, - } - }) { - var eventHandled = new TaskCompletionSource (); - process.Start (); - if (!string.IsNullOrEmpty (input)) { - process.StandardInput.Write (input); - process.StandardInput.Close (); - } - - if (!process.WaitForExit (5000)) { - var timeoutError = $@"Process timed out. Command line: {process.StartInfo.FileName} {process.StartInfo.Arguments}."; - throw new TimeoutException (timeoutError); - } - - if (waitForOutput && process.StandardOutput.Peek () != -1) { - output = process.StandardOutput.ReadToEnd (); - } - - if (process.ExitCode > 0) { - output = $@"Process failed to run. Command line: {cmd} {arguments}. - Output: {output} - Error: {process.StandardError.ReadToEnd ()}"; - } - - return (process.ExitCode, output); - } - } - - public static bool DoubleWaitForExit (this System.Diagnostics.Process process) - { - var result = process.WaitForExit (500); - if (result) { - process.WaitForExit (); - } - return result; - } + /// + /// Makes an . + /// + /// The foreground color. + /// The background color. + /// The attribute for the foreground and background colors. + public abstract Attribute MakeColor (Color foreground, Color background); - public static bool FileExists (this string value) - { - return !string.IsNullOrEmpty (value) && !value.Contains ("not found"); + /// + /// Ensures all s in are correctly + /// initialized by the driver. + /// + /// Flag indicating if colors are supported (not used). + public void InitializeColorSchemes (bool supportsColors = true) + { + // Ensure all Attributes are initialized by the driver + foreach (var s in Colors.ColorSchemes) { + s.Value.Initialize (); } } } + diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/ClipboardImpl.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/ClipboardImpl.cs new file mode 100644 index 0000000000..ddf5811851 --- /dev/null +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/ClipboardImpl.cs @@ -0,0 +1,225 @@ +using System; +using System.Runtime.InteropServices; +using Unix.Terminal; + +namespace Terminal.Gui; + +/// +/// A clipboard implementation for Linux. +/// This implementation uses the xclip command to access the clipboard. +/// +/// +/// If xclip is not installed, this implementation will not work. +/// +class CursesClipboard : ClipboardBase { + public CursesClipboard () + { + IsSupported = CheckSupport (); + } + + string _xclipPath = string.Empty; + public override bool IsSupported { get; } + + bool CheckSupport () + { +#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. + try { + var (exitCode, result) = ClipboardProcessRunner.Bash ("which xclip", waitForOutput: true); + if (exitCode == 0 && result.FileExists ()) { + _xclipPath = result; + return true; + } + } catch (Exception) { + // Permissions issue. + } +#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. + return false; + } + + protected override string GetClipboardDataImpl () + { + var tempFileName = System.IO.Path.GetTempFileName (); + var xclipargs = "-selection clipboard -o"; + + try { + var (exitCode, result) = ClipboardProcessRunner.Bash ($"{_xclipPath} {xclipargs} > {tempFileName}", waitForOutput: false); + if (exitCode == 0) { + if (Application.Driver is CursesDriver) { + Curses.raw (); + Curses.noecho (); + } + return System.IO.File.ReadAllText (tempFileName); + } + } catch (Exception e) { + throw new NotSupportedException ($"\"{_xclipPath} {xclipargs}\" failed.", e); + } finally { + System.IO.File.Delete (tempFileName); + } + return string.Empty; + } + + protected override void SetClipboardDataImpl (string text) + { + var xclipargs = "-selection clipboard -i"; + try { + var (exitCode, _) = ClipboardProcessRunner.Bash ($"{_xclipPath} {xclipargs}", text, waitForOutput: false); + if (exitCode == 0 && Application.Driver is CursesDriver) { + Curses.raw (); + Curses.noecho (); + } + } catch (Exception e) { + throw new NotSupportedException ($"\"{_xclipPath} {xclipargs} < {text}\" failed", e); + } + } +} +/// +/// A clipboard implementation for MacOSX. +/// This implementation uses the Mac clipboard API (via P/Invoke) to copy/paste. +/// The existance of the Mac pbcopy and pbpaste commands +/// is used to determine if copy/paste is supported. +/// +class MacOSXClipboard : ClipboardBase { + IntPtr _nsString = objc_getClass ("NSString"); + IntPtr _nsPasteboard = objc_getClass ("NSPasteboard"); + IntPtr _utfTextType; + IntPtr _generalPasteboard; + IntPtr _initWithUtf8Register = sel_registerName ("initWithUTF8String:"); + IntPtr _allocRegister = sel_registerName ("alloc"); + IntPtr _setStringRegister = sel_registerName ("setString:forType:"); + IntPtr _stringForTypeRegister = sel_registerName ("stringForType:"); + IntPtr _utf8Register = sel_registerName ("UTF8String"); + IntPtr _nsStringPboardType; + IntPtr _generalPasteboardRegister = sel_registerName ("generalPasteboard"); + IntPtr _clearContentsRegister = sel_registerName ("clearContents"); + + public MacOSXClipboard () + { + _utfTextType = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, "public.utf8-plain-text"); + _nsStringPboardType = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, "NSStringPboardType"); + _generalPasteboard = objc_msgSend (_nsPasteboard, _generalPasteboardRegister); + IsSupported = CheckSupport (); + } + + public override bool IsSupported { get; } + + bool CheckSupport () + { + var (exitCode, result) = ClipboardProcessRunner.Bash ("which pbcopy", waitForOutput: true); + if (exitCode != 0 || !result.FileExists ()) { + return false; + } + (exitCode, result) = ClipboardProcessRunner.Bash ("which pbpaste", waitForOutput: true); + return exitCode == 0 && result.FileExists (); + } + + protected override string GetClipboardDataImpl () + { + var ptr = objc_msgSend (_generalPasteboard, _stringForTypeRegister, _nsStringPboardType); + var charArray = objc_msgSend (ptr, _utf8Register); + return Marshal.PtrToStringAnsi (charArray); + } + + protected override void SetClipboardDataImpl (string text) + { + IntPtr str = default; + try { + str = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, text); + objc_msgSend (_generalPasteboard, _clearContentsRegister); + objc_msgSend (_generalPasteboard, _setStringRegister, str, _utfTextType); + } finally { + if (str != default) { + objc_msgSend (str, sel_registerName ("release")); + } + } + } + + [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] + static extern IntPtr objc_getClass (string className); + + [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] + static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector); + + [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] + static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, string arg1); + + [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] + static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, IntPtr arg1); + + [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] + static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2); + + [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] + static extern IntPtr sel_registerName (string selectorName); +} + +/// +/// A clipboard implementation for Linux, when running under WSL. +/// This implementation uses the Windows clipboard to store the data, and uses Windows' +/// powershell.exe (launched via WSL interop services) to set/get the Windows +/// clipboard. +/// +class WSLClipboard : ClipboardBase { + public WSLClipboard () + { + } + + public override bool IsSupported { + get { + return CheckSupport (); + } + } + + private static string _powershellPath = string.Empty; + + bool CheckSupport () + { + if (string.IsNullOrEmpty (_powershellPath)) { + // Specify pwsh.exe (not pwsh) to ensure we get the Windows version (invoked via WSL) + var (exitCode, result) = ClipboardProcessRunner.Bash ("which pwsh.exe", waitForOutput: true); + if (exitCode > 0) { + (exitCode, result) = ClipboardProcessRunner.Bash ("which powershell.exe", waitForOutput: true); + } + + if (exitCode == 0) { + _powershellPath = result; + } + } + return !string.IsNullOrEmpty (_powershellPath); + } + + protected override string GetClipboardDataImpl () + { + if (!IsSupported) { + return string.Empty; + } + + var (exitCode, output) = ClipboardProcessRunner.Process (_powershellPath, "-noprofile -command \"Get-Clipboard\""); + if (exitCode == 0) { + if (Application.Driver is CursesDriver) { + Curses.raw (); + Curses.noecho (); + } + + if (output.EndsWith ("\r\n")) { + output = output.Substring (0, output.Length - 2); + } + return output; + } + return string.Empty; + } + + protected override void SetClipboardDataImpl (string text) + { + if (!IsSupported) { + return; + } + + var (exitCode, output) = ClipboardProcessRunner.Process (_powershellPath, $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\""); + if (exitCode == 0) { + if (Application.Driver is CursesDriver) { + Curses.raw (); + Curses.noecho (); + } + } + } +} diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 946825c75f..ae3509caad 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -68,7 +68,7 @@ public override void AddRune (Rune rune) Contents [Row, Col, 2] = 1; if (runeWidth < 2 && Col > 0 - && Rune.ColumnWidth ((char)Contents [Row, Col - 1, 0]) > 1) { + && Rune.ColumnWidth ((char)Contents [Row, Col - 1, 0]) > 1) { var curAttr = CurrentAttribute; Curses.attrset (Contents [Row, Col - 1, 1]); @@ -78,7 +78,7 @@ public override void AddRune (Rune rune) Curses.attrset (curAttr); } else if (runeWidth < 2 && Col <= Clip.Right - 1 - && Rune.ColumnWidth ((char)Contents [Row, Col, 0]) > 1) { + && Rune.ColumnWidth ((char)Contents [Row, Col, 0]) > 1) { var curAttr = CurrentAttribute; Curses.attrset (Contents [Row, Col + 1, 1]); @@ -150,7 +150,7 @@ public override void End () } public override void UpdateScreen () => _window.redrawwin (); - + public Curses.Window _window; /// @@ -307,19 +307,24 @@ void ProcessInput () return; } k = MapCursesKey (wch); - if (wch >= 277 && wch <= 288) { // Shift+(F1 - F12) + if (wch >= 277 && wch <= 288) { + // Shift+(F1 - F12) wch -= 12; k = Key.ShiftMask | MapCursesKey (wch); - } else if (wch >= 289 && wch <= 300) { // Ctrl+(F1 - F12) + } else if (wch >= 289 && wch <= 300) { + // Ctrl+(F1 - F12) wch -= 24; k = Key.CtrlMask | MapCursesKey (wch); - } else if (wch >= 301 && wch <= 312) { // Ctrl+Shift+(F1 - F12) + } else if (wch >= 301 && wch <= 312) { + // Ctrl+Shift+(F1 - F12) wch -= 36; k = Key.CtrlMask | Key.ShiftMask | MapCursesKey (wch); - } else if (wch >= 313 && wch <= 324) { // Alt+(F1 - F12) + } else if (wch >= 313 && wch <= 324) { + // Alt+(F1 - F12) wch -= 48; k = Key.AltMask | MapCursesKey (wch); - } else if (wch >= 325 && wch <= 327) { // Shift+Alt+(F1 - F3) + } else if (wch >= 325 && wch <= 327) { + // Shift+Alt+(F1 - F3) wch -= 60; k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch); } @@ -710,12 +715,12 @@ public override void Suspend () StartReportingMouseMoves (); } - public override void StartReportingMouseMoves () + public void StartReportingMouseMoves () { Console.Out.Write (EscSeqUtils.EnableMouseEvents); } - public override void StopReportingMouseMoves () + public void StopReportingMouseMoves () { Console.Out.Write (EscSeqUtils.DisableMouseEvents); } @@ -954,156 +959,4 @@ protected override void SetClipboardDataImpl (string text) } } } - - /// - /// A clipboard implementation for MacOSX. - /// This implementation uses the Mac clipboard API (via P/Invoke) to copy/paste. - /// The existance of the Mac pbcopy and pbpaste commands - /// is used to determine if copy/paste is supported. - /// - class MacOSXClipboard : ClipboardBase { - IntPtr _nsString = objc_getClass ("NSString"); - IntPtr _nsPasteboard = objc_getClass ("NSPasteboard"); - IntPtr _utfTextType; - IntPtr _generalPasteboard; - IntPtr _initWithUtf8Register = sel_registerName ("initWithUTF8String:"); - IntPtr _allocRegister = sel_registerName ("alloc"); - IntPtr _setStringRegister = sel_registerName ("setString:forType:"); - IntPtr _stringForTypeRegister = sel_registerName ("stringForType:"); - IntPtr _utf8Register = sel_registerName ("UTF8String"); - IntPtr _nsStringPboardType; - IntPtr _generalPasteboardRegister = sel_registerName ("generalPasteboard"); - IntPtr _clearContentsRegister = sel_registerName ("clearContents"); - - public MacOSXClipboard () - { - _utfTextType = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, "public.utf8-plain-text"); - _nsStringPboardType = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, "NSStringPboardType"); - _generalPasteboard = objc_msgSend (_nsPasteboard, _generalPasteboardRegister); - IsSupported = CheckSupport (); - } - - public override bool IsSupported { get; } - - bool CheckSupport () - { - var (exitCode, result) = ClipboardProcessRunner.Bash ("which pbcopy", waitForOutput: true); - if (exitCode != 0 || !result.FileExists ()) { - return false; - } - (exitCode, result) = ClipboardProcessRunner.Bash ("which pbpaste", waitForOutput: true); - return exitCode == 0 && result.FileExists (); - } - - protected override string GetClipboardDataImpl () - { - var ptr = objc_msgSend (_generalPasteboard, _stringForTypeRegister, _nsStringPboardType); - var charArray = objc_msgSend (ptr, _utf8Register); - return Marshal.PtrToStringAnsi (charArray); - } - - protected override void SetClipboardDataImpl (string text) - { - IntPtr str = default; - try { - str = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, text); - objc_msgSend (_generalPasteboard, _clearContentsRegister); - objc_msgSend (_generalPasteboard, _setStringRegister, str, _utfTextType); - } finally { - if (str != default) { - objc_msgSend (str, sel_registerName ("release")); - } - } - } - - [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] - static extern IntPtr objc_getClass (string className); - - [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] - static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector); - - [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] - static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, string arg1); - - [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] - static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, IntPtr arg1); - - [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] - static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2); - - [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")] - static extern IntPtr sel_registerName (string selectorName); - } - - /// - /// A clipboard implementation for Linux, when running under WSL. - /// This implementation uses the Windows clipboard to store the data, and uses Windows' - /// powershell.exe (launched via WSL interop services) to set/get the Windows - /// clipboard. - /// - class WSLClipboard : ClipboardBase { - public WSLClipboard () - { - } - - public override bool IsSupported { - get { - return CheckSupport (); - } - } - - private static string _powershellPath = string.Empty; - - bool CheckSupport () - { - if (string.IsNullOrEmpty (_powershellPath)) { - // Specify pwsh.exe (not pwsh) to ensure we get the Windows version (invoked via WSL) - var (exitCode, result) = ClipboardProcessRunner.Bash ("which pwsh.exe", waitForOutput: true); - if (exitCode > 0) { - (exitCode, result) = ClipboardProcessRunner.Bash ("which powershell.exe", waitForOutput: true); - } - - if (exitCode == 0) { - _powershellPath = result; - } - } - return !string.IsNullOrEmpty (_powershellPath); - } - - protected override string GetClipboardDataImpl () - { - if (!IsSupported) { - return string.Empty; - } - - var (exitCode, output) = ClipboardProcessRunner.Process (_powershellPath, "-noprofile -command \"Get-Clipboard\""); - if (exitCode == 0) { - if (Application.Driver is CursesDriver) { - Curses.raw (); - Curses.noecho (); - } - - if (output.EndsWith ("\r\n")) { - output = output.Substring (0, output.Length - 2); - } - return output; - } - return string.Empty; - } - - protected override void SetClipboardDataImpl (string text) - { - if (!IsSupported) { - return; - } - - var (exitCode, output) = ClipboardProcessRunner.Process (_powershellPath, $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\""); - if (exitCode == 0) { - if (Application.Driver is CursesDriver) { - Curses.raw (); - Curses.noecho (); - } - } - } - } } diff --git a/Terminal.Gui/ConsoleDrivers/CursorVisibility.cs b/Terminal.Gui/ConsoleDrivers/CursorVisibility.cs new file mode 100644 index 0000000000..56cba036a6 --- /dev/null +++ b/Terminal.Gui/ConsoleDrivers/CursorVisibility.cs @@ -0,0 +1,59 @@ +namespace Terminal.Gui; + +/// +/// Cursors Visibility that are displayed +/// +// +// Hexa value are set as 0xAABBCCDD where : +// +// AA stand for the TERMINFO DECSUSR parameter value to be used under Linux & MacOS +// BB stand for the NCurses curs_set parameter value to be used under Linux & MacOS +// CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows +// DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows +// +public enum CursorVisibility { + /// + /// Cursor caret has default + /// + /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking. + Default = 0x00010119, + + /// + /// Cursor caret is hidden + /// + Invisible = 0x03000019, + + /// + /// Cursor caret is normally shown as a blinking underline bar _ + /// + Underline = 0x03010119, + + /// + /// Cursor caret is normally shown as a underline bar _ + /// + /// Under Windows, this is equivalent to + UnderlineFix = 0x04010119, + + /// + /// Cursor caret is displayed a blinking vertical bar | + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + Vertical = 0x05010119, + + /// + /// Cursor caret is displayed a blinking vertical bar | + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + VerticalFix = 0x06010119, + + /// + /// Cursor caret is displayed as a blinking block ▉ + /// + Box = 0x01020164, + + /// + /// Cursor caret is displayed a block ▉ + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + BoxFix = 0x02020164, +} \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 0c6a420e78..4655918222 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1077,12 +1077,12 @@ public override bool EnsureCursorVisibility () return savedCursorVisibility == CursorVisibility.Default; } - public override void StartReportingMouseMoves () + public void StartReportingMouseMoves () { Console.Out.Write (EscSeqUtils.EnableMouseEvents); } - public override void StopReportingMouseMoves () + public void StopReportingMouseMoves () { Console.Out.Write (EscSeqUtils.DisableMouseEvents); } From 43e70ca6337dc7e80b3e3a818c9c25ba462b4c08 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 9 May 2023 00:52:14 +0200 Subject: [PATCH 20/99] Code cleanup --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 223 +++++++++++------- .../CursesDriver/CursesDriver.cs | 77 +----- .../ConsoleDrivers/CursorVisibility.cs | 59 ----- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 10 - Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 10 - 5 files changed, 144 insertions(+), 235 deletions(-) delete mode 100644 Terminal.Gui/ConsoleDrivers/CursorVisibility.cs diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index fb29666fb0..d47cce8f22 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -1,17 +1,30 @@ -// -// ConsoleDriver.cs: Base class for Terminal.Gui ConsoleDriver implementations. -// -using NStack; +using NStack; using System; using System.Diagnostics; namespace Terminal.Gui; /// -/// ConsoleDriver is an abstract class that defines the requirements for a console driver. -/// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. +/// Base class for Terminal.Gui ConsoleDriver implementations. /// +/// +/// There are currently four implementations: +/// - (for Unix and Mac) +/// - +/// - that uses the .NET Console API +/// - for unit testing. +/// public abstract class ConsoleDriver { + /// + /// Prepare the driver and set the key and mouse events handlers. + /// + /// The main loop. + /// The handler for ProcessKey + /// The handler for key down events + /// The handler for key up events + /// The handler for mouse events + public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler); + /// /// The handler fired when the terminal is resized. /// @@ -129,22 +142,36 @@ public static Rune MakePrintable (Rune c) /// Adds the to the display at the cursor position. /// /// String. - public virtual void AddStr (ustring str) + public void AddStr (ustring str) { foreach (var rune in str) { AddRune (rune); } } + Rect _clip; + /// - /// Prepare the driver and set the key and mouse events handlers. + /// Tests whether the specified coordinate are valid for drawing. /// - /// The main loop. - /// The handler for ProcessKey - /// The handler for key down events - /// The handler for key up events - /// The handler for mouse events - public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler); + /// The column. + /// The row. + /// if the coordinate is outside of the + /// screen bounds or outside of . otherwise. + public bool IsValidLocation (int col, int row) => + col >= 0 && row >= 0 && + col < Cols && row < Rows && + Clip.Contains (col, row); + + /// + /// Gets or sets the clip rectangle that and are + /// subject to. + /// + /// The rectangle describing the bounds of . + public Rect Clip { + get => _clip; + set => _clip = value; + } /// /// Updates the screen to reflect all the changes that have been done to the display buffer @@ -176,11 +203,6 @@ public virtual void AddStr (ustring str) /// upon success public abstract bool EnsureCursorVisibility (); - /// - /// Ends the execution of the console driver. - /// - public abstract void End (); - /// /// Clears the buffer and the driver buffer. /// @@ -191,6 +213,8 @@ public virtual void AddStr (ustring str) /// public abstract void UpdateScreen (); + Attribute _currentAttribute; + /// /// The that will be used for the next or call. /// @@ -216,6 +240,17 @@ public Attribute CurrentAttribute { /// C. public void SetAttribute (Attribute c) => CurrentAttribute = c; + /// + /// Make the attribute for the foreground and background colors. + /// + /// Foreground. + /// Background. + /// + public virtual Attribute MakeAttribute (Color fore, Color back) + { + return MakeColor (fore, back); + } + /// /// Gets the foreground and background colors based on a platform-dependent color value. /// @@ -226,27 +261,29 @@ public Attribute CurrentAttribute { public abstract bool GetColors (int value, out Color foreground, out Color background); /// - /// Simulates a key press. + /// Gets the current . /// - /// The key character. - /// The key. - /// If simulates the Shift key being pressed. - /// If simulates the Alt key being pressed. - /// If simulates the Ctrl key being pressed. - public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl); + /// The current attribute. + public Attribute GetAttribute () => CurrentAttribute; /// - /// Fills the specified rectangle with the specified rune. + /// Makes an . /// - /// - /// - public void FillRect (Rect rect, Rune rune = default) + /// The foreground color. + /// The background color. + /// The attribute for the foreground and background colors. + public abstract Attribute MakeColor (Color foreground, Color background); + + /// + /// Ensures all s in are correctly + /// initialized by the driver. + /// + /// Flag indicating if colors are supported (not used). + public void InitializeColorSchemes (bool supportsColors = true) { - for (var r = rect.Y; r < rect.Y + rect.Height; r++) { - for (var c = rect.X; c < rect.X + rect.Width; c++) { - Application.Driver.Move (c, r); - Application.Driver.AddRune (rune == default ? ' ' : rune); - } + // Ensure all Attributes are initialized by the driver + foreach (var s in Colors.ColorSchemes) { + s.Value.Initialize (); } } @@ -282,68 +319,94 @@ public enum DiagnosticFlags : uint { /// This is only implemented in . public abstract void Suspend (); - Rect _clip; - /// - /// Tests whether the specified coordinate are valid for drawing. + /// Simulates a key press. /// - /// The column. - /// The row. - /// if the coordinate is outside of the - /// screen bounds or outside of . otherwise. - public bool IsValidLocation (int col, int row) => - col >= 0 && row >= 0 && - col < Cols && row < Rows && - Clip.Contains (col, row); + /// The key character. + /// The key. + /// If simulates the Shift key being pressed. + /// If simulates the Alt key being pressed. + /// If simulates the Ctrl key being pressed. + public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl); + // TODO: Move FillRect to ./Drawing /// - /// Gets or sets the clip rectangle that and are - /// subject to. + /// Fills the specified rectangle with the specified rune. /// - /// The rectangle describing the bounds of . - public Rect Clip { - get => _clip; - set => _clip = value; + /// + /// + public void FillRect (Rect rect, Rune rune = default) + { + for (var r = rect.Y; r < rect.Y + rect.Height; r++) { + for (var c = rect.X; c < rect.X + rect.Width; c++) { + Application.Driver.Move (c, r); + Application.Driver.AddRune (rune == default ? ' ' : rune); + } + } } - Attribute _currentAttribute; + /// + /// Ends the execution of the console driver. + /// + public abstract void End (); +} + + +/// +/// Terminal Cursor Visibility settings. +/// +/// +/// Hex value are set as 0xAABBCCDD where : +/// +/// AA stand for the TERMINFO DECSUSR parameter value to be used under Linux and MacOS +/// BB stand for the NCurses curs_set parameter value to be used under Linux and MacOS +/// CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows +/// DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows +/// +public enum CursorVisibility { /// - /// Make the attribute for the foreground and background colors. + /// Cursor caret has default /// - /// Foreground. - /// Background. - /// - public virtual Attribute MakeAttribute (Color fore, Color back) - { - return MakeColor (fore, back); - } + /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking. + Default = 0x00010119, /// - /// Gets the current . + /// Cursor caret is hidden /// - /// The current attribute. - public Attribute GetAttribute () => CurrentAttribute; + Invisible = 0x03000019, /// - /// Makes an . + /// Cursor caret is normally shown as a blinking underline bar _ /// - /// The foreground color. - /// The background color. - /// The attribute for the foreground and background colors. - public abstract Attribute MakeColor (Color foreground, Color background); + Underline = 0x03010119, /// - /// Ensures all s in are correctly - /// initialized by the driver. + /// Cursor caret is normally shown as a underline bar _ /// - /// Flag indicating if colors are supported (not used). - public void InitializeColorSchemes (bool supportsColors = true) - { - // Ensure all Attributes are initialized by the driver - foreach (var s in Colors.ColorSchemes) { - s.Value.Initialize (); - } - } -} + /// Under Windows, this is equivalent to + UnderlineFix = 0x04010119, + /// + /// Cursor caret is displayed a blinking vertical bar | + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + Vertical = 0x05010119, + + /// + /// Cursor caret is displayed a blinking vertical bar | + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + VerticalFix = 0x06010119, + + /// + /// Cursor caret is displayed as a blinking block ▉ + /// + Box = 0x01020164, + + /// + /// Cursor caret is displayed a block ▉ + /// + /// Works under Xterm-like terminal otherwise this is equivalent to + BoxFix = 0x02020164, +} diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index ae3509caad..4b3a60f5eb 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -116,12 +116,6 @@ public override void AddRune (Rune rune) } } - public override void AddStr (ustring str) - { - // TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly - base.AddStr (str); - } - public override void Refresh () { Curses.raw (); @@ -159,7 +153,7 @@ public override void End () /// Contains the curses attributes for the foreground (color, plus any attributes) /// Contains the curses attributes for the background (color, plus any attributes) /// - public static Attribute MakeColor (short foreground, short background) + static Attribute MakeColor (short foreground, short background) { var v = (short)((int)foreground | background << 4); //Curses.InitColorPair (++last_color_pair, foreground, background); @@ -890,73 +884,4 @@ static public bool Suspend () return true; } } - - /// - /// A clipboard implementation for Linux. - /// This implementation uses the xclip command to access the clipboard. - /// - /// - /// If xclip is not installed, this implementation will not work. - /// - class CursesClipboard : ClipboardBase { - public CursesClipboard () - { - IsSupported = CheckSupport (); - } - - string _xclipPath = string.Empty; - public override bool IsSupported { get; } - - bool CheckSupport () - { -#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. - try { - var (exitCode, result) = ClipboardProcessRunner.Bash ("which xclip", waitForOutput: true); - if (exitCode == 0 && result.FileExists ()) { - _xclipPath = result; - return true; - } - } catch (Exception) { - // Permissions issue. - } -#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. - return false; - } - - protected override string GetClipboardDataImpl () - { - var tempFileName = System.IO.Path.GetTempFileName (); - var xclipargs = "-selection clipboard -o"; - - try { - var (exitCode, result) = ClipboardProcessRunner.Bash ($"{_xclipPath} {xclipargs} > {tempFileName}", waitForOutput: false); - if (exitCode == 0) { - if (Application.Driver is CursesDriver) { - Curses.raw (); - Curses.noecho (); - } - return System.IO.File.ReadAllText (tempFileName); - } - } catch (Exception e) { - throw new NotSupportedException ($"\"{_xclipPath} {xclipargs}\" failed.", e); - } finally { - System.IO.File.Delete (tempFileName); - } - return string.Empty; - } - - protected override void SetClipboardDataImpl (string text) - { - var xclipargs = "-selection clipboard -i"; - try { - var (exitCode, _) = ClipboardProcessRunner.Bash ($"{_xclipPath} {xclipargs}", text, waitForOutput: false); - if (exitCode == 0 && Application.Driver is CursesDriver) { - Curses.raw (); - Curses.noecho (); - } - } catch (Exception e) { - throw new NotSupportedException ($"\"{_xclipPath} {xclipargs} < {text}\" failed", e); - } - } - } } diff --git a/Terminal.Gui/ConsoleDrivers/CursorVisibility.cs b/Terminal.Gui/ConsoleDrivers/CursorVisibility.cs deleted file mode 100644 index 56cba036a6..0000000000 --- a/Terminal.Gui/ConsoleDrivers/CursorVisibility.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace Terminal.Gui; - -/// -/// Cursors Visibility that are displayed -/// -// -// Hexa value are set as 0xAABBCCDD where : -// -// AA stand for the TERMINFO DECSUSR parameter value to be used under Linux & MacOS -// BB stand for the NCurses curs_set parameter value to be used under Linux & MacOS -// CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows -// DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows -// -public enum CursorVisibility { - /// - /// Cursor caret has default - /// - /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking. - Default = 0x00010119, - - /// - /// Cursor caret is hidden - /// - Invisible = 0x03000019, - - /// - /// Cursor caret is normally shown as a blinking underline bar _ - /// - Underline = 0x03010119, - - /// - /// Cursor caret is normally shown as a underline bar _ - /// - /// Under Windows, this is equivalent to - UnderlineFix = 0x04010119, - - /// - /// Cursor caret is displayed a blinking vertical bar | - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - Vertical = 0x05010119, - - /// - /// Cursor caret is displayed a blinking vertical bar | - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - VerticalFix = 0x06010119, - - /// - /// Cursor caret is displayed as a blinking block ▉ - /// - Box = 0x01020164, - - /// - /// Cursor caret is displayed a block ▉ - /// - /// Works under Xterm-like terminal otherwise this is equivalent to - BoxFix = 0x02020164, -} \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 1e5c7f0b88..69ed10017b 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -583,16 +583,6 @@ public override void UpdateCursor () } #region Not Implemented - public override void StartReportingMouseMoves () - { - throw new NotImplementedException (); - } - - public override void StopReportingMouseMoves () - { - throw new NotImplementedException (); - } - public override void Suspend () { throw new NotImplementedException (); diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index ef2d82b3bb..82d13f5d02 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1708,16 +1708,6 @@ public override void Suspend () { throw new NotImplementedException (); } - - public override void StartReportingMouseMoves () - { - throw new NotImplementedException (); - } - - public override void StopReportingMouseMoves () - { - throw new NotImplementedException (); - } #endregion } From 0c0d4341349607c02fa10d060a8098acc746d649 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 9 May 2023 15:40:10 +0200 Subject: [PATCH 21/99] Removes dependecy on NStack from ConsoleDrivers - WIP --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 89 +- .../CursesDriver/CursesDriver.cs | 1549 +++---- .../ConsoleDrivers/FakeDriver/FakeConsole.cs | 3882 +++++++++-------- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 1025 ++--- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 2627 +++++------ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 3403 ++++++++------- UICatalog/Scenarios/CharacterMap.cs | 13 +- UnitTests/ConsoleDrivers/ContentsTests.cs | 50 + UnitTests/TestHelpers.cs | 2 +- UnitTests/Text/UnicodeTests.cs | 19 +- UnitTests/UnitTests.csproj | 1 + UnitTests/Views/TextViewTests.cs | 2 +- 12 files changed, 6452 insertions(+), 6210 deletions(-) create mode 100644 UnitTests/ConsoleDrivers/ContentsTests.cs diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index d47cce8f22..51f4c3f2b6 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -1,9 +1,51 @@ -using NStack; +using Rune = System.Text.Rune; +using NStack; using System; using System.Diagnostics; +using System.Text; +using System.Globalization; namespace Terminal.Gui; +/// +/// Extends to support TUI specific text manipulation. +/// +public static class RuneExtensions { + + + public static bool IsCombiningMark (this System.Text.Rune rune) + { + // Convert Rune to string + UnicodeCategory category = Rune.GetUnicodeCategory (rune); + + // Check if the category corresponds to a combining mark + return category == UnicodeCategory.NonSpacingMark + || category == UnicodeCategory.SpacingCombiningMark + || category == UnicodeCategory.EnclosingMark; + } + + /// + /// Extension method for System.Text.Rune to retrieve the number of column positions of a wide-character code. + /// This is used to measure runes as displayed by text-based terminals. + /// + /// The width in columns, 0 if the argument is the null character, -1 if the value is not printable, + /// otherwise the number of columns that the rune occupies. + /// The rune. + public static int GetColumnWidth (this System.Text.Rune rune) + { + return System.Rune.ColumnWidth (rune.Value); + } + + /// + /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 + /// to equivalent, printable, Unicode chars. + /// + /// + /// + /// + public static Rune MakePrintable (this System.Text.Rune rune) => Rune.IsControl (rune) ? new Rune (rune.Value + 0x2400) : rune; +} + /// /// Base class for Terminal.Gui ConsoleDriver implementations. /// @@ -116,36 +158,41 @@ public virtual void Move (int col, int row) } /// - /// Adds the specified rune to the display at the current cursor position. + /// Adds the specified rune to the display at the current cursor position. /// + /// + /// + /// When the method returns, will be incremented by the number of columns required, + /// unless the new column value is outside of the or screen dimensions defined by . + /// + /// + /// If requires more than one column, and plus the number of columns needed + /// exceeds the or screen dimensions, the default Unicode replacement character (U+FFFD) will be added instead. + /// + /// /// Rune to add. - public abstract void AddRune (Rune rune); - - /// - /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 - /// to equivalent, printable, Unicode chars. - /// - /// Rune to translate - /// - public static Rune MakePrintable (Rune c) + public virtual void AddRune (System.Rune rune) { - if (c <= 0x1F || (c >= 0X7F && c <= 0x9F)) { - // ASCII (C0) control characters. - // C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1) - return new Rune (c + 0x2400); - } - return c; } /// /// Adds the to the display at the cursor position. /// + /// + /// + /// When the method returns, will be incremented by the number of columns required, + /// unless the new column value is outside of the or screen dimensions defined by . + /// + /// + /// If requires more columns than are available, the output will be clipped. + /// + /// /// String. public void AddStr (ustring str) { - foreach (var rune in str) { - AddRune (rune); + foreach (var c in str) { + AddRune (new System.Rune (c)); } } @@ -335,12 +382,12 @@ public enum DiagnosticFlags : uint { /// /// /// - public void FillRect (Rect rect, Rune rune = default) + public void FillRect (Rect rect, System.Rune rune = default) { for (var r = rect.Y; r < rect.Y + rect.Height; r++) { for (var c = rect.X; c < rect.X + rect.Width; c++) { Application.Driver.Move (c, r); - Application.Driver.AddRune (rune == default ? ' ' : rune); + Application.Driver.AddRune (rune == default ? new System.Rune (' ') : rune); } } } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 4b3a60f5eb..9cf9245348 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -7,881 +7,914 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; -using NStack; +using Rune = System.Text.Rune; using Unix.Terminal; +using System.Buffers; + +namespace Terminal.Gui; + +/// +/// This is the Curses driver for the gui.cs/Terminal framework. +/// +internal class CursesDriver : ConsoleDriver { + public override int Cols => Curses.Cols; + public override int Rows => Curses.Lines; + + CursorVisibility? _initialCursorVisibility = null; + CursorVisibility? _currentCursorVisibility = null; + + // If true, Move set Col and Row to an invalid location + bool _atValidLocation; + + public override void Move (int col, int row) + { + base.Move (col, row); + + if (IsValidLocation (col, row)) { + Curses.move (row, col); + _atValidLocation = true; + } else { + // Not a valid location (outside screen or clip region) + // Move within the clip region, then AddRune will actually move to Col, Row + Curses.move (Clip.Y, Clip.X); + _atValidLocation = false; + } + } -namespace Terminal.Gui { - - /// - /// This is the Curses driver for the gui.cs/Terminal framework. - /// - internal class CursesDriver : ConsoleDriver { - public override int Cols => Curses.Cols; - public override int Rows => Curses.Lines; - - CursorVisibility? _initialCursorVisibility = null; - CursorVisibility? _currentCursorVisibility = null; - - // If true, Move set Col and Row to an invalid location - bool _atValidLocation; - - public override void Move (int col, int row) - { - base.Move (col, row); - - if (IsValidLocation (col, row)) { - Curses.move (row, col); - _atValidLocation = true; - } else { - // Not a valid location (outside screen or clip region) - // Move within the clip region, then AddRune will actually move to Col, Row - Curses.move (Clip.Y, Clip.X); + public override void AddRune (System.Rune systemRune) + { + var rune = new Rune(systemRune).MakePrintable (); + var runeWidth = rune.GetColumnWidth (); + var validLocation = IsValidLocation (Col, Row); + + if (validLocation) { + if (!_atValidLocation) { + // Move was called with an invalid location. + // Since then, the clip changed, and now we are at a valid location. + Curses.move (Row, Col); _atValidLocation = false; } - } - - public override void AddRune (Rune rune) - { - rune = MakePrintable (rune); - var runeWidth = Rune.ColumnWidth (rune); - var validLocation = IsValidLocation (Col, Row); + if (runeWidth == 0 && Col > 0) { + // This is a combining character, and we are not at the beginning of the line. + var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); + var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; + Contents [Row, Col - 1, 0] = normalized [0]; + Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 2] = 1; + Curses.attrset (Contents [Row, Col - 1, 1]); + Curses.mvaddch (Row, Col - 1, normalized [0]); + } else { + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 1; - if (validLocation) { - if (!_atValidLocation) { - // Move was called with an invalid location. - // Since then, the clip changed, and now we are at a valid location. + if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumnWidth() > 1) { + // This is a single-width character, and we are not at the beginning of the line. + var curAttr = CurrentAttribute; + Curses.attrset (Contents [Row, Col - 1, 1]); + Curses.mvaddch (Row, Col - 1, Rune.ReplacementChar.Value); + Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; Curses.move (Row, Col); - _atValidLocation = false; + Curses.attrset (curAttr); + + } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumnWidth() > 1) { + // This is a single-width character, and we are not at the end of the line. + var curAttr = CurrentAttribute; + Curses.attrset (Contents [Row, Col + 1, 1]); + Curses.mvaddch (Row, Col + 1, Rune.ReplacementChar.Value); + Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; + Curses.move (Row, Col); + Curses.attrset (curAttr); + } - if (runeWidth == 0 && Col > 0) { - var r = Contents [Row, Col - 1, 0]; - var s = new string (new char [] { (char)r, (char)rune }); - var sn = !s.IsNormalized () ? s.Normalize () : s; - var c = sn [0]; - Contents [Row, Col - 1, 0] = c; - Contents [Row, Col - 1, 1] = CurrentAttribute; - Contents [Row, Col - 1, 2] = 1; - Curses.attrset (Contents [Row, Col - 1, 1]); - Curses.mvaddch (Row, Col - 1, c); + if (runeWidth > 1 && Col == Clip.Right - 1) { + // This is a double-width character, and we are at the end of the line. + Curses.addch (Rune.ReplacementChar.Value); + Contents [Row, Col, 0] = Rune.ReplacementChar.Value; } else { - Contents [Row, Col, 1] = CurrentAttribute; - Contents [Row, Col, 2] = 1; + // This is a single-width character, or we are not at the end of the line. - if (runeWidth < 2 && Col > 0 - && Rune.ColumnWidth ((char)Contents [Row, Col - 1, 0]) > 1) { + var curAttr = CurrentAttribute; + Curses.attrset (Contents [Row, Col, 1]); - var curAttr = CurrentAttribute; - Curses.attrset (Contents [Row, Col - 1, 1]); - Curses.mvaddch (Row, Col - 1, System.Text.Rune.ReplacementChar.Value); - Contents [Row, Col - 1, 0] = System.Text.Rune.ReplacementChar.Value; - Curses.move (Row, Col); - Curses.attrset (curAttr); - - } else if (runeWidth < 2 && Col <= Clip.Right - 1 - && Rune.ColumnWidth ((char)Contents [Row, Col, 0]) > 1) { - - var curAttr = CurrentAttribute; - Curses.attrset (Contents [Row, Col + 1, 1]); - Curses.mvaddch (Row, Col + 1, System.Text.Rune.ReplacementChar.Value); - Contents [Row, Col + 1, 0] = System.Text.Rune.ReplacementChar.Value; - Curses.move (Row, Col); - Curses.attrset (curAttr); - - } - if (runeWidth > 1 && Col == Clip.Right - 1) { - Curses.addch (System.Text.Rune.ReplacementChar.Value); - Contents [Row, Col, 0] = System.Text.Rune.ReplacementChar.Value; + if (rune.IsBmp) { + Contents [Row, Col, 0] = rune.Value; + Curses.addch (Contents [Row, Col, 0]); } else { - var curAttr = CurrentAttribute; - Curses.attrset (Contents [Row, Col, 1]); - Curses.addch ((int)(uint)rune); - Curses.attrset (curAttr); - Contents [Row, Col, 0] = (int)(uint)rune; + var column = Col; + ReadOnlySpan remainingInput = rune.ToString ().AsSpan (); + while (!remainingInput.IsEmpty) { + // Decode + OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); + + if (opStatus != OperationStatus.Done) { + result = Rune.ReplacementChar; + } + Contents [Row, column, 0] = result.Value; + Contents [Row, column, 1] = CurrentAttribute; + + Curses.attrset (Contents [Row, column, 1]); + // BUGBUG: workaround curses not supporting non BMP? # + Curses.mvaddch (Row, column, Rune.ReplacementChar.Value); + //Curses.mvaddch (Row, column, Contents [Row, column, 0]); + + // Slice and loop again + remainingInput = remainingInput.Slice (charsConsumed); + column++; + } + Curses.move (Row, Col); } + Curses.attrset (curAttr); } - } else { - _atValidLocation = false; - } - - if (runeWidth is < 0 or > 0) { - Col++; - } - - if (runeWidth > 1) { - if (validLocation && Col < Clip.Right) { - Contents [Row, Col, 1] = CurrentAttribute; - Contents [Row, Col, 2] = 0; - } - Col++; } + } else { + _atValidLocation = false; } - public override void Refresh () - { - Curses.raw (); - Curses.noecho (); - Curses.refresh (); - ProcessWinChange (); + if (runeWidth is < 0 or > 0) { + Col++; } - private void ProcessWinChange () - { - if (Curses.CheckWinChange ()) { - ResizeScreen (); - UpdateOffScreen (); - TerminalResized?.Invoke (); + if (runeWidth > 1) { + // This is a double-width character, and we are not at the end of the line. + if (validLocation && Col < Clip.Right) { + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 0; + + //if (rune.IsBmp) { + // // BUGBUG: workaround #2610 + // Contents [Row, Col, 0] = (char)0x00; + // Curses.addch (Contents [Row, Col, 0]); + //} } + Col++; } + } - public override void UpdateCursor () => Refresh (); - - public override void End () - { - StopReportingMouseMoves (); - SetCursorVisibility (CursorVisibility.Default); - - Curses.endwin (); - } - - public override void UpdateScreen () => _window.redrawwin (); - - public Curses.Window _window; - - /// - /// Creates a curses color from the provided foreground and background colors - /// - /// Contains the curses attributes for the foreground (color, plus any attributes) - /// Contains the curses attributes for the background (color, plus any attributes) - /// - static Attribute MakeColor (short foreground, short background) - { - var v = (short)((int)foreground | background << 4); - //Curses.InitColorPair (++last_color_pair, foreground, background); - Curses.InitColorPair (v, foreground, background); - return new Attribute ( - //value: Curses.ColorPair (last_color_pair), - value: Curses.ColorPair (v), - //foreground: (Color)foreground, - foreground: MapCursesColor (foreground), - //background: (Color)background); - background: MapCursesColor (background)); - } - - public override Attribute MakeColor (Color fore, Color back) - { - return MakeColor ((short)MapColor (fore), (short)MapColor (back)); - } - - static Key MapCursesKey (int cursesKey) - { - switch (cursesKey) { - case Curses.KeyF1: return Key.F1; - case Curses.KeyF2: return Key.F2; - case Curses.KeyF3: return Key.F3; - case Curses.KeyF4: return Key.F4; - case Curses.KeyF5: return Key.F5; - case Curses.KeyF6: return Key.F6; - case Curses.KeyF7: return Key.F7; - case Curses.KeyF8: return Key.F8; - case Curses.KeyF9: return Key.F9; - case Curses.KeyF10: return Key.F10; - case Curses.KeyF11: return Key.F11; - case Curses.KeyF12: return Key.F12; - case Curses.KeyUp: return Key.CursorUp; - case Curses.KeyDown: return Key.CursorDown; - case Curses.KeyLeft: return Key.CursorLeft; - case Curses.KeyRight: return Key.CursorRight; - case Curses.KeyHome: return Key.Home; - case Curses.KeyEnd: return Key.End; - case Curses.KeyNPage: return Key.PageDown; - case Curses.KeyPPage: return Key.PageUp; - case Curses.KeyDeleteChar: return Key.DeleteChar; - case Curses.KeyInsertChar: return Key.InsertChar; - case Curses.KeyTab: return Key.Tab; - case Curses.KeyBackTab: return Key.BackTab; - case Curses.KeyBackspace: return Key.Backspace; - case Curses.ShiftKeyUp: return Key.CursorUp | Key.ShiftMask; - case Curses.ShiftKeyDown: return Key.CursorDown | Key.ShiftMask; - case Curses.ShiftKeyLeft: return Key.CursorLeft | Key.ShiftMask; - case Curses.ShiftKeyRight: return Key.CursorRight | Key.ShiftMask; - case Curses.ShiftKeyHome: return Key.Home | Key.ShiftMask; - case Curses.ShiftKeyEnd: return Key.End | Key.ShiftMask; - case Curses.ShiftKeyNPage: return Key.PageDown | Key.ShiftMask; - case Curses.ShiftKeyPPage: return Key.PageUp | Key.ShiftMask; - case Curses.AltKeyUp: return Key.CursorUp | Key.AltMask; - case Curses.AltKeyDown: return Key.CursorDown | Key.AltMask; - case Curses.AltKeyLeft: return Key.CursorLeft | Key.AltMask; - case Curses.AltKeyRight: return Key.CursorRight | Key.AltMask; - case Curses.AltKeyHome: return Key.Home | Key.AltMask; - case Curses.AltKeyEnd: return Key.End | Key.AltMask; - case Curses.AltKeyNPage: return Key.PageDown | Key.AltMask; - case Curses.AltKeyPPage: return Key.PageUp | Key.AltMask; - case Curses.CtrlKeyUp: return Key.CursorUp | Key.CtrlMask; - case Curses.CtrlKeyDown: return Key.CursorDown | Key.CtrlMask; - case Curses.CtrlKeyLeft: return Key.CursorLeft | Key.CtrlMask; - case Curses.CtrlKeyRight: return Key.CursorRight | Key.CtrlMask; - case Curses.CtrlKeyHome: return Key.Home | Key.CtrlMask; - case Curses.CtrlKeyEnd: return Key.End | Key.CtrlMask; - case Curses.CtrlKeyNPage: return Key.PageDown | Key.CtrlMask; - case Curses.CtrlKeyPPage: return Key.PageUp | Key.CtrlMask; - case Curses.ShiftCtrlKeyUp: return Key.CursorUp | Key.ShiftMask | Key.CtrlMask; - case Curses.ShiftCtrlKeyDown: return Key.CursorDown | Key.ShiftMask | Key.CtrlMask; - case Curses.ShiftCtrlKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.CtrlMask; - case Curses.ShiftCtrlKeyRight: return Key.CursorRight | Key.ShiftMask | Key.CtrlMask; - case Curses.ShiftCtrlKeyHome: return Key.Home | Key.ShiftMask | Key.CtrlMask; - case Curses.ShiftCtrlKeyEnd: return Key.End | Key.ShiftMask | Key.CtrlMask; - case Curses.ShiftCtrlKeyNPage: return Key.PageDown | Key.ShiftMask | Key.CtrlMask; - case Curses.ShiftCtrlKeyPPage: return Key.PageUp | Key.ShiftMask | Key.CtrlMask; - case Curses.ShiftAltKeyUp: return Key.CursorUp | Key.ShiftMask | Key.AltMask; - case Curses.ShiftAltKeyDown: return Key.CursorDown | Key.ShiftMask | Key.AltMask; - case Curses.ShiftAltKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.AltMask; - case Curses.ShiftAltKeyRight: return Key.CursorRight | Key.ShiftMask | Key.AltMask; - case Curses.ShiftAltKeyNPage: return Key.PageDown | Key.ShiftMask | Key.AltMask; - case Curses.ShiftAltKeyPPage: return Key.PageUp | Key.ShiftMask | Key.AltMask; - case Curses.ShiftAltKeyHome: return Key.Home | Key.ShiftMask | Key.AltMask; - case Curses.ShiftAltKeyEnd: return Key.End | Key.ShiftMask | Key.AltMask; - case Curses.AltCtrlKeyNPage: return Key.PageDown | Key.AltMask | Key.CtrlMask; - case Curses.AltCtrlKeyPPage: return Key.PageUp | Key.AltMask | Key.CtrlMask; - case Curses.AltCtrlKeyHome: return Key.Home | Key.AltMask | Key.CtrlMask; - case Curses.AltCtrlKeyEnd: return Key.End | Key.AltMask | Key.CtrlMask; - default: return Key.Unknown; - } + public override void Refresh () + { + Curses.raw (); + Curses.noecho (); + Curses.refresh (); + ProcessWinChange (); + } + + private void ProcessWinChange () + { + if (Curses.CheckWinChange ()) { + ResizeScreen (); + UpdateOffScreen (); + TerminalResized?.Invoke (); } + } - KeyModifiers _keyModifiers; + public override void UpdateCursor () => Refresh (); - KeyModifiers MapKeyModifiers (Key key) - { - if (_keyModifiers == null) { - _keyModifiers = new KeyModifiers (); - } + public override void End () + { + StopReportingMouseMoves (); + SetCursorVisibility (CursorVisibility.Default); - if (!_keyModifiers.Shift && (key & Key.ShiftMask) != 0) { - _keyModifiers.Shift = true; - } - if (!_keyModifiers.Alt && (key & Key.AltMask) != 0) { - _keyModifiers.Alt = true; - } - if (!_keyModifiers.Ctrl && (key & Key.CtrlMask) != 0) { - _keyModifiers.Ctrl = true; - } + Curses.endwin (); + } + + public override void UpdateScreen () => _window.redrawwin (); - return _keyModifiers; + public Curses.Window _window; + + /// + /// Creates a curses color from the provided foreground and background colors + /// + /// Contains the curses attributes for the foreground (color, plus any attributes) + /// Contains the curses attributes for the background (color, plus any attributes) + /// + static Attribute MakeColor (short foreground, short background) + { + var v = (short)((int)foreground | background << 4); + //Curses.InitColorPair (++last_color_pair, foreground, background); + Curses.InitColorPair (v, foreground, background); + return new Attribute ( + //value: Curses.ColorPair (last_color_pair), + value: Curses.ColorPair (v), + //foreground: (Color)foreground, + foreground: MapCursesColor (foreground), + //background: (Color)background); + background: MapCursesColor (background)); + } + + public override Attribute MakeColor (Color fore, Color back) + { + return MakeColor ((short)MapColor (fore), (short)MapColor (back)); + } + + static Key MapCursesKey (int cursesKey) + { + switch (cursesKey) { + case Curses.KeyF1: return Key.F1; + case Curses.KeyF2: return Key.F2; + case Curses.KeyF3: return Key.F3; + case Curses.KeyF4: return Key.F4; + case Curses.KeyF5: return Key.F5; + case Curses.KeyF6: return Key.F6; + case Curses.KeyF7: return Key.F7; + case Curses.KeyF8: return Key.F8; + case Curses.KeyF9: return Key.F9; + case Curses.KeyF10: return Key.F10; + case Curses.KeyF11: return Key.F11; + case Curses.KeyF12: return Key.F12; + case Curses.KeyUp: return Key.CursorUp; + case Curses.KeyDown: return Key.CursorDown; + case Curses.KeyLeft: return Key.CursorLeft; + case Curses.KeyRight: return Key.CursorRight; + case Curses.KeyHome: return Key.Home; + case Curses.KeyEnd: return Key.End; + case Curses.KeyNPage: return Key.PageDown; + case Curses.KeyPPage: return Key.PageUp; + case Curses.KeyDeleteChar: return Key.DeleteChar; + case Curses.KeyInsertChar: return Key.InsertChar; + case Curses.KeyTab: return Key.Tab; + case Curses.KeyBackTab: return Key.BackTab; + case Curses.KeyBackspace: return Key.Backspace; + case Curses.ShiftKeyUp: return Key.CursorUp | Key.ShiftMask; + case Curses.ShiftKeyDown: return Key.CursorDown | Key.ShiftMask; + case Curses.ShiftKeyLeft: return Key.CursorLeft | Key.ShiftMask; + case Curses.ShiftKeyRight: return Key.CursorRight | Key.ShiftMask; + case Curses.ShiftKeyHome: return Key.Home | Key.ShiftMask; + case Curses.ShiftKeyEnd: return Key.End | Key.ShiftMask; + case Curses.ShiftKeyNPage: return Key.PageDown | Key.ShiftMask; + case Curses.ShiftKeyPPage: return Key.PageUp | Key.ShiftMask; + case Curses.AltKeyUp: return Key.CursorUp | Key.AltMask; + case Curses.AltKeyDown: return Key.CursorDown | Key.AltMask; + case Curses.AltKeyLeft: return Key.CursorLeft | Key.AltMask; + case Curses.AltKeyRight: return Key.CursorRight | Key.AltMask; + case Curses.AltKeyHome: return Key.Home | Key.AltMask; + case Curses.AltKeyEnd: return Key.End | Key.AltMask; + case Curses.AltKeyNPage: return Key.PageDown | Key.AltMask; + case Curses.AltKeyPPage: return Key.PageUp | Key.AltMask; + case Curses.CtrlKeyUp: return Key.CursorUp | Key.CtrlMask; + case Curses.CtrlKeyDown: return Key.CursorDown | Key.CtrlMask; + case Curses.CtrlKeyLeft: return Key.CursorLeft | Key.CtrlMask; + case Curses.CtrlKeyRight: return Key.CursorRight | Key.CtrlMask; + case Curses.CtrlKeyHome: return Key.Home | Key.CtrlMask; + case Curses.CtrlKeyEnd: return Key.End | Key.CtrlMask; + case Curses.CtrlKeyNPage: return Key.PageDown | Key.CtrlMask; + case Curses.CtrlKeyPPage: return Key.PageUp | Key.CtrlMask; + case Curses.ShiftCtrlKeyUp: return Key.CursorUp | Key.ShiftMask | Key.CtrlMask; + case Curses.ShiftCtrlKeyDown: return Key.CursorDown | Key.ShiftMask | Key.CtrlMask; + case Curses.ShiftCtrlKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.CtrlMask; + case Curses.ShiftCtrlKeyRight: return Key.CursorRight | Key.ShiftMask | Key.CtrlMask; + case Curses.ShiftCtrlKeyHome: return Key.Home | Key.ShiftMask | Key.CtrlMask; + case Curses.ShiftCtrlKeyEnd: return Key.End | Key.ShiftMask | Key.CtrlMask; + case Curses.ShiftCtrlKeyNPage: return Key.PageDown | Key.ShiftMask | Key.CtrlMask; + case Curses.ShiftCtrlKeyPPage: return Key.PageUp | Key.ShiftMask | Key.CtrlMask; + case Curses.ShiftAltKeyUp: return Key.CursorUp | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyDown: return Key.CursorDown | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyRight: return Key.CursorRight | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyNPage: return Key.PageDown | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyPPage: return Key.PageUp | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyHome: return Key.Home | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyEnd: return Key.End | Key.ShiftMask | Key.AltMask; + case Curses.AltCtrlKeyNPage: return Key.PageDown | Key.AltMask | Key.CtrlMask; + case Curses.AltCtrlKeyPPage: return Key.PageUp | Key.AltMask | Key.CtrlMask; + case Curses.AltCtrlKeyHome: return Key.Home | Key.AltMask | Key.CtrlMask; + case Curses.AltCtrlKeyEnd: return Key.End | Key.AltMask | Key.CtrlMask; + default: return Key.Unknown; } + } - void ProcessInput () - { - int wch; - var code = Curses.get_wch (out wch); - //System.Diagnostics.Debug.WriteLine ($"code: {code}; wch: {wch}"); - if (code == Curses.ERR) { - return; - } + KeyModifiers _keyModifiers; + KeyModifiers MapKeyModifiers (Key key) + { + if (_keyModifiers == null) { _keyModifiers = new KeyModifiers (); - Key k = Key.Null; + } - if (code == Curses.KEY_CODE_YES) { - if (wch == Curses.KeyResize) { - ProcessWinChange (); - } - if (wch == Curses.KeyMouse) { - int wch2 = wch; + if (!_keyModifiers.Shift && (key & Key.ShiftMask) != 0) { + _keyModifiers.Shift = true; + } + if (!_keyModifiers.Alt && (key & Key.AltMask) != 0) { + _keyModifiers.Alt = true; + } + if (!_keyModifiers.Ctrl && (key & Key.CtrlMask) != 0) { + _keyModifiers.Ctrl = true; + } + + return _keyModifiers; + } + + void ProcessInput () + { + int wch; + var code = Curses.get_wch (out wch); + //System.Diagnostics.Debug.WriteLine ($"code: {code}; wch: {wch}"); + if (code == Curses.ERR) { + return; + } - while (wch2 == Curses.KeyMouse) { - KeyEvent key = null; - ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { + _keyModifiers = new KeyModifiers (); + Key k = Key.Null; + + if (code == Curses.KEY_CODE_YES) { + if (wch == Curses.KeyResize) { + ProcessWinChange (); + } + if (wch == Curses.KeyMouse) { + int wch2 = wch; + + while (wch2 == Curses.KeyMouse) { + KeyEvent key = null; + ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { new ConsoleKeyInfo ((char)Key.Esc, 0, false, false, false), new ConsoleKeyInfo ('[', 0, false, false, false), new ConsoleKeyInfo ('<', 0, false, false, false) }; - code = 0; - GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); - } - return; + code = 0; + GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); } - k = MapCursesKey (wch); - if (wch >= 277 && wch <= 288) { - // Shift+(F1 - F12) - wch -= 12; - k = Key.ShiftMask | MapCursesKey (wch); - } else if (wch >= 289 && wch <= 300) { - // Ctrl+(F1 - F12) - wch -= 24; - k = Key.CtrlMask | MapCursesKey (wch); - } else if (wch >= 301 && wch <= 312) { - // Ctrl+Shift+(F1 - F12) - wch -= 36; - k = Key.CtrlMask | Key.ShiftMask | MapCursesKey (wch); - } else if (wch >= 313 && wch <= 324) { - // Alt+(F1 - F12) - wch -= 48; - k = Key.AltMask | MapCursesKey (wch); - } else if (wch >= 325 && wch <= 327) { - // Shift+Alt+(F1 - F3) - wch -= 60; - k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch); - } - _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); - _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); - _keyUpHandler (new KeyEvent (k, MapKeyModifiers (k))); return; } + k = MapCursesKey (wch); + if (wch >= 277 && wch <= 288) { + // Shift+(F1 - F12) + wch -= 12; + k = Key.ShiftMask | MapCursesKey (wch); + } else if (wch >= 289 && wch <= 300) { + // Ctrl+(F1 - F12) + wch -= 24; + k = Key.CtrlMask | MapCursesKey (wch); + } else if (wch >= 301 && wch <= 312) { + // Ctrl+Shift+(F1 - F12) + wch -= 36; + k = Key.CtrlMask | Key.ShiftMask | MapCursesKey (wch); + } else if (wch >= 313 && wch <= 324) { + // Alt+(F1 - F12) + wch -= 48; + k = Key.AltMask | MapCursesKey (wch); + } else if (wch >= 325 && wch <= 327) { + // Shift+Alt+(F1 - F3) + wch -= 60; + k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch); + } + _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyUpHandler (new KeyEvent (k, MapKeyModifiers (k))); + return; + } - // Special handling for ESC, we want to try to catch ESC+letter to simulate alt-letter as well as Alt-Fkey - if (wch == 27) { - Curses.timeout (10); - - code = Curses.get_wch (out int wch2); + // Special handling for ESC, we want to try to catch ESC+letter to simulate alt-letter as well as Alt-Fkey + if (wch == 27) { + Curses.timeout (10); - if (code == Curses.KEY_CODE_YES) { - k = Key.AltMask | MapCursesKey (wch); - } - if (code == 0) { - KeyEvent key = null; + code = Curses.get_wch (out int wch2); - // The ESC-number handling, debatable. - // Simulates the AltMask itself by pressing Alt + Space. - if (wch2 == (int)Key.Space) { - k = Key.AltMask; - } else if (wch2 - (int)Key.Space >= (uint)Key.A && wch2 - (int)Key.Space <= (uint)Key.Z) { - k = (Key)((uint)Key.AltMask + (wch2 - (int)Key.Space)); - } else if (wch2 >= (uint)Key.A - 64 && wch2 <= (uint)Key.Z - 64) { - k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + (wch2 + 64)); - } else if (wch2 >= (uint)Key.D0 && wch2 <= (uint)Key.D9) { - k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - (uint)Key.D0)); - } else if (wch2 == Curses.KeyCSI) { - ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { + if (code == Curses.KEY_CODE_YES) { + k = Key.AltMask | MapCursesKey (wch); + } + if (code == 0) { + KeyEvent key = null; + + // The ESC-number handling, debatable. + // Simulates the AltMask itself by pressing Alt + Space. + if (wch2 == (int)Key.Space) { + k = Key.AltMask; + } else if (wch2 - (int)Key.Space >= (uint)Key.A && wch2 - (int)Key.Space <= (uint)Key.Z) { + k = (Key)((uint)Key.AltMask + (wch2 - (int)Key.Space)); + } else if (wch2 >= (uint)Key.A - 64 && wch2 <= (uint)Key.Z - 64) { + k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + (wch2 + 64)); + } else if (wch2 >= (uint)Key.D0 && wch2 <= (uint)Key.D9) { + k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - (uint)Key.D0)); + } else if (wch2 == Curses.KeyCSI) { + ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { new ConsoleKeyInfo ((char)Key.Esc, 0, false, false, false), new ConsoleKeyInfo ('[', 0, false, false, false) }; - GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); - return; + GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); + return; + } else { + // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa. + if (((Key)wch2 & Key.CtrlMask) != 0) { + _keyModifiers.Ctrl = true; + } + if (wch2 == 0) { + k = Key.CtrlMask | Key.AltMask | Key.Space; + } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) { + _keyModifiers.Shift = true; + _keyModifiers.Alt = true; + } else if (wch2 < 256) { + k = (Key)wch2; + _keyModifiers.Alt = true; } else { - // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa. - if (((Key)wch2 & Key.CtrlMask) != 0) { - _keyModifiers.Ctrl = true; - } - if (wch2 == 0) { - k = Key.CtrlMask | Key.AltMask | Key.Space; - } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) { - _keyModifiers.Shift = true; - _keyModifiers.Alt = true; - } else if (wch2 < 256) { - k = (Key)wch2; - _keyModifiers.Alt = true; - } else { - k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2); - } + k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2); } - key = new KeyEvent (k, MapKeyModifiers (k)); - _keyDownHandler (key); - _keyHandler (key); - } else { - k = Key.Esc; - _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); } - } else if (wch == Curses.KeyTab) { - k = MapCursesKey (wch); - _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); - _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); + key = new KeyEvent (k, MapKeyModifiers (k)); + _keyDownHandler (key); + _keyHandler (key); } else { - // Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa. - k = (Key)wch; - if (wch == 0) { - k = Key.CtrlMask | Key.Space; - } else if (wch >= (uint)Key.A - 64 && wch <= (uint)Key.Z - 64) { - if ((Key)(wch + 64) != Key.J) { - k = Key.CtrlMask | (Key)(wch + 64); - } - } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) { - _keyModifiers.Shift = true; - } - _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); + k = Key.Esc; _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); - _keyUpHandler (new KeyEvent (k, MapKeyModifiers (k))); } - // Cause OnKeyUp and OnKeyPressed. Note that the special handling for ESC above - // will not impact KeyUp. - // This is causing ESC firing even if another keystroke was handled. - //if (wch == Curses.KeyTab) { - // keyUpHandler (new KeyEvent (MapCursesKey (wch), keyModifiers)); - //} else { - // keyUpHandler (new KeyEvent ((Key)wch, keyModifiers)); - //} - } - - void GetEscSeq (ref int code, ref Key k, ref int wch2, ref KeyEvent key, ref ConsoleKeyInfo [] cki) - { - ConsoleKey ck = 0; - ConsoleModifiers mod = 0; - while (code == 0) { - code = Curses.get_wch (out wch2); - var consoleKeyInfo = new ConsoleKeyInfo ((char)wch2, 0, false, false, false); - if (wch2 == 0 || wch2 == 27 || wch2 == Curses.KeyMouse) { - EscSeqUtils.DecodeEscSeq (null, ref consoleKeyInfo, ref ck, cki, ref mod, out _, out _, out _, out _, out bool isKeyMouse, out List mouseFlags, out Point pos, out _, ProcessContinuousButtonPressed); - if (isKeyMouse) { - foreach (var mf in mouseFlags) { - ProcessMouseEvent (mf, pos); - } - cki = null; - if (wch2 == 27) { - cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, - false, false, false), cki); - } - } else { - k = ConsoleKeyMapping.MapConsoleKeyToKey (consoleKeyInfo.Key, out _); - k = ConsoleKeyMapping.MapKeyModifiers (consoleKeyInfo, k); - key = new KeyEvent (k, MapKeyModifiers (k)); - _keyDownHandler (key); - _keyHandler (key); - } - } else { - cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki); + } else if (wch == Curses.KeyTab) { + k = MapCursesKey (wch); + _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); + } else { + // Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa. + k = (Key)wch; + if (wch == 0) { + k = Key.CtrlMask | Key.Space; + } else if (wch >= (uint)Key.A - 64 && wch <= (uint)Key.Z - 64) { + if ((Key)(wch + 64) != Key.J) { + k = Key.CtrlMask | (Key)(wch + 64); } + } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) { + _keyModifiers.Shift = true; } + _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyHandler (new KeyEvent (k, MapKeyModifiers (k))); + _keyUpHandler (new KeyEvent (k, MapKeyModifiers (k))); } + // Cause OnKeyUp and OnKeyPressed. Note that the special handling for ESC above + // will not impact KeyUp. + // This is causing ESC firing even if another keystroke was handled. + //if (wch == Curses.KeyTab) { + // keyUpHandler (new KeyEvent (MapCursesKey (wch), keyModifiers)); + //} else { + // keyUpHandler (new KeyEvent ((Key)wch, keyModifiers)); + //} + } - void ProcessMouseEvent (MouseFlags mouseFlag, Point pos) - { - var me = new MouseEvent () { - Flags = mouseFlag, - X = pos.X, - Y = pos.Y - }; - _mouseHandler (me); - } - - void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) - { - ProcessMouseEvent (mouseFlag, pos); + void GetEscSeq (ref int code, ref Key k, ref int wch2, ref KeyEvent key, ref ConsoleKeyInfo [] cki) + { + ConsoleKey ck = 0; + ConsoleModifiers mod = 0; + while (code == 0) { + code = Curses.get_wch (out wch2); + var consoleKeyInfo = new ConsoleKeyInfo ((char)wch2, 0, false, false, false); + if (wch2 == 0 || wch2 == 27 || wch2 == Curses.KeyMouse) { + EscSeqUtils.DecodeEscSeq (null, ref consoleKeyInfo, ref ck, cki, ref mod, out _, out _, out _, out _, out bool isKeyMouse, out List mouseFlags, out Point pos, out _, ProcessContinuousButtonPressed); + if (isKeyMouse) { + foreach (var mf in mouseFlags) { + ProcessMouseEvent (mf, pos); + } + cki = null; + if (wch2 == 27) { + cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, + false, false, false), cki); + } + } else { + k = ConsoleKeyMapping.MapConsoleKeyToKey (consoleKeyInfo.Key, out _); + k = ConsoleKeyMapping.MapKeyModifiers (consoleKeyInfo, k); + key = new KeyEvent (k, MapKeyModifiers (k)); + _keyDownHandler (key); + _keyHandler (key); + } + } else { + cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki); + } } + } - Action _keyHandler; - Action _keyDownHandler; - Action _keyUpHandler; - Action _mouseHandler; - - public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) - { - // Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called - Curses.timeout (0); - this._keyHandler = keyHandler; - this._keyDownHandler = keyDownHandler; - this._keyUpHandler = keyUpHandler; - this._mouseHandler = mouseHandler; + void ProcessMouseEvent (MouseFlags mouseFlag, Point pos) + { + var me = new MouseEvent () { + Flags = mouseFlag, + X = pos.X, + Y = pos.Y + }; + _mouseHandler (me); + } - var mLoop = mainLoop.Driver as UnixMainLoop; + void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) + { + ProcessMouseEvent (mouseFlag, pos); + } - mLoop.AddWatch (0, UnixMainLoop.Condition.PollIn, x => { - ProcessInput (); - return true; - }); + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; - mLoop.WinChanged += () => { - ProcessWinChange (); - }; - } + public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) + { + // Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called + Curses.timeout (0); + this._keyHandler = keyHandler; + this._keyDownHandler = keyDownHandler; + this._keyUpHandler = keyUpHandler; + this._mouseHandler = mouseHandler; - public override void Init (Action terminalResized) - { - if (_window != null) { - return; - } + var mLoop = mainLoop.Driver as UnixMainLoop; - try { - _window = Curses.initscr (); - Curses.set_escdelay (10); - } catch (Exception e) { - throw new Exception ($"Curses failed to initialize, the exception is: {e.Message}"); - } - - // Ensures that all procedures are performed at some previous closing. - Curses.doupdate (); + mLoop.AddWatch (0, UnixMainLoop.Condition.PollIn, x => { + ProcessInput (); + return true; + }); - // - // We are setting Invisible as default so we could ignore XTerm DECSUSR setting - // - switch (Curses.curs_set (0)) { - case 0: - _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Invisible; - break; + mLoop.WinChanged += () => { + ProcessWinChange (); + }; + } - case 1: - _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Underline; - Curses.curs_set (1); - break; + public override void Init (Action terminalResized) + { + if (_window != null) { + return; + } - case 2: - _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Box; - Curses.curs_set (2); - break; + try { + _window = Curses.initscr (); + Curses.set_escdelay (10); + } catch (Exception e) { + throw new Exception ($"Curses failed to initialize, the exception is: {e.Message}"); + } - default: - _currentCursorVisibility = _initialCursorVisibility = null; - break; - } + // Ensures that all procedures are performed at some previous closing. + Curses.doupdate (); + + // + // We are setting Invisible as default so we could ignore XTerm DECSUSR setting + // + switch (Curses.curs_set (0)) { + case 0: + _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Invisible; + break; + + case 1: + _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Underline; + Curses.curs_set (1); + break; + + case 2: + _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Box; + Curses.curs_set (2); + break; + + default: + _currentCursorVisibility = _initialCursorVisibility = null; + break; + } - if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - Clipboard = new MacOSXClipboard (); + if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { + Clipboard = new MacOSXClipboard (); + } else { + if (Is_WSL_Platform ()) { + Clipboard = new WSLClipboard (); } else { - if (Is_WSL_Platform ()) { - Clipboard = new WSLClipboard (); - } else { - Clipboard = new CursesClipboard (); - } + Clipboard = new CursesClipboard (); } + } - Curses.raw (); - Curses.noecho (); + Curses.raw (); + Curses.noecho (); + + Curses.Window.Standard.keypad (true); + TerminalResized = terminalResized; + StartReportingMouseMoves (); + + CurrentAttribute = MakeColor (Color.White, Color.Black); + + if (Curses.HasColors) { + Curses.StartColor (); + Curses.UseDefaultColors (); + + InitializeColorSchemes (); + } else { + throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does."); + //InitializeColorSchemes (false); + + //// BUGBUG: This is a hack to make the colors work on the Mac? + //// The new Theme support overwrites these colors, so this is not needed? + //Colors.TopLevel.Normal = Curses.COLOR_GREEN; + //Colors.TopLevel.Focus = Curses.COLOR_WHITE; + //Colors.TopLevel.HotNormal = Curses.COLOR_YELLOW; + //Colors.TopLevel.HotFocus = Curses.COLOR_YELLOW; + //Colors.TopLevel.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + //Colors.Base.Normal = Curses.A_NORMAL; + //Colors.Base.Focus = Curses.A_REVERSE; + //Colors.Base.HotNormal = Curses.A_BOLD; + //Colors.Base.HotFocus = Curses.A_BOLD | Curses.A_REVERSE; + //Colors.Base.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + //Colors.Menu.Normal = Curses.A_REVERSE; + //Colors.Menu.Focus = Curses.A_NORMAL; + //Colors.Menu.HotNormal = Curses.A_BOLD; + //Colors.Menu.HotFocus = Curses.A_NORMAL; + //Colors.Menu.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + //Colors.Dialog.Normal = Curses.A_REVERSE; + //Colors.Dialog.Focus = Curses.A_NORMAL; + //Colors.Dialog.HotNormal = Curses.A_BOLD; + //Colors.Dialog.HotFocus = Curses.A_NORMAL; + //Colors.Dialog.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + //Colors.Error.Normal = Curses.A_BOLD; + //Colors.Error.Focus = Curses.A_BOLD | Curses.A_REVERSE; + //Colors.Error.HotNormal = Curses.A_BOLD | Curses.A_REVERSE; + //Colors.Error.HotFocus = Curses.A_REVERSE; + //Colors.Error.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + } - Curses.Window.Standard.keypad (true); - TerminalResized = terminalResized; - StartReportingMouseMoves (); + ResizeScreen (); + UpdateOffScreen (); - CurrentAttribute = MakeColor (Color.White, Color.Black); + } - if (Curses.HasColors) { - Curses.StartColor (); - Curses.UseDefaultColors (); + public virtual void ResizeScreen () + { + Clip = new Rect (0, 0, Cols, Rows); + Curses.refresh (); + } - InitializeColorSchemes (); - } else { - throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does."); - //InitializeColorSchemes (false); - - //// BUGBUG: This is a hack to make the colors work on the Mac? - //// The new Theme support overwrites these colors, so this is not needed? - //Colors.TopLevel.Normal = Curses.COLOR_GREEN; - //Colors.TopLevel.Focus = Curses.COLOR_WHITE; - //Colors.TopLevel.HotNormal = Curses.COLOR_YELLOW; - //Colors.TopLevel.HotFocus = Curses.COLOR_YELLOW; - //Colors.TopLevel.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - //Colors.Base.Normal = Curses.A_NORMAL; - //Colors.Base.Focus = Curses.A_REVERSE; - //Colors.Base.HotNormal = Curses.A_BOLD; - //Colors.Base.HotFocus = Curses.A_BOLD | Curses.A_REVERSE; - //Colors.Base.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - //Colors.Menu.Normal = Curses.A_REVERSE; - //Colors.Menu.Focus = Curses.A_NORMAL; - //Colors.Menu.HotNormal = Curses.A_BOLD; - //Colors.Menu.HotFocus = Curses.A_NORMAL; - //Colors.Menu.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - //Colors.Dialog.Normal = Curses.A_REVERSE; - //Colors.Dialog.Focus = Curses.A_NORMAL; - //Colors.Dialog.HotNormal = Curses.A_BOLD; - //Colors.Dialog.HotFocus = Curses.A_NORMAL; - //Colors.Dialog.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - //Colors.Error.Normal = Curses.A_BOLD; - //Colors.Error.Focus = Curses.A_BOLD | Curses.A_REVERSE; - //Colors.Error.HotNormal = Curses.A_BOLD | Curses.A_REVERSE; - //Colors.Error.HotFocus = Curses.A_REVERSE; - //Colors.Error.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; + public override void UpdateOffScreen () + { + Contents = new int [Rows, Cols, 3]; + for (int row = 0; row < Rows; row++) { + for (int col = 0; col < Cols; col++) { + Contents [row, col, 0] = ' '; + Contents [row, col, 1] = Colors.TopLevel.Normal; + Contents [row, col, 2] = 0; } - - ResizeScreen (); - UpdateOffScreen (); - } + } - public virtual void ResizeScreen () - { - Clip = new Rect (0, 0, Cols, Rows); - Curses.refresh (); + public static bool Is_WSL_Platform () + { + // xclip does not work on WSL, so we need to use the Windows clipboard vis Powershell + //if (new CursesClipboard ().IsSupported) { + // // If xclip is installed on Linux under WSL, this will return true. + // return false; + //} + var (exitCode, result) = ClipboardProcessRunner.Bash ("uname -a", waitForOutput: true); + if (exitCode == 0 && result.Contains ("microsoft") && result.Contains ("WSL")) { + return true; } + return false; + } - public override void UpdateOffScreen () - { - Contents = new int [Rows, Cols, 3]; - for (int row = 0; row < Rows; row++) { - for (int col = 0; col < Cols; col++) { - Contents [row, col, 0] = ' '; - Contents [row, col, 1] = Colors.TopLevel.Normal; - Contents [row, col, 2] = 0; - } - } + static int MapColor (Color color) + { + switch (color) { + case Color.Black: + return Curses.COLOR_BLACK; + case Color.Blue: + return Curses.COLOR_BLUE; + case Color.Green: + return Curses.COLOR_GREEN; + case Color.Cyan: + return Curses.COLOR_CYAN; + case Color.Red: + return Curses.COLOR_RED; + case Color.Magenta: + return Curses.COLOR_MAGENTA; + case Color.Brown: + return Curses.COLOR_YELLOW; + case Color.Gray: + return Curses.COLOR_WHITE; + case Color.DarkGray: + //return Curses.COLOR_BLACK | Curses.A_BOLD; + return Curses.COLOR_GRAY; + case Color.BrightBlue: + return Curses.COLOR_BLUE | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightGreen: + return Curses.COLOR_GREEN | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightCyan: + return Curses.COLOR_CYAN | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightRed: + return Curses.COLOR_RED | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightMagenta: + return Curses.COLOR_MAGENTA | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightYellow: + return Curses.COLOR_YELLOW | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.White: + return Curses.COLOR_WHITE | Curses.A_BOLD | Curses.COLOR_GRAY; } + throw new ArgumentException ("Invalid color code"); + } - public static bool Is_WSL_Platform () - { - // xclip does not work on WSL, so we need to use the Windows clipboard vis Powershell - //if (new CursesClipboard ().IsSupported) { - // // If xclip is installed on Linux under WSL, this will return true. - // return false; - //} - var (exitCode, result) = ClipboardProcessRunner.Bash ("uname -a", waitForOutput: true); - if (exitCode == 0 && result.Contains ("microsoft") && result.Contains ("WSL")) { - return true; - } - return false; + static Color MapCursesColor (int color) + { + switch (color) { + case Curses.COLOR_BLACK: + return Color.Black; + case Curses.COLOR_BLUE: + return Color.Blue; + case Curses.COLOR_GREEN: + return Color.Green; + case Curses.COLOR_CYAN: + return Color.Cyan; + case Curses.COLOR_RED: + return Color.Red; + case Curses.COLOR_MAGENTA: + return Color.Magenta; + case Curses.COLOR_YELLOW: + return Color.Brown; + case Curses.COLOR_WHITE: + return Color.Gray; + case Curses.COLOR_GRAY: + return Color.DarkGray; + case Curses.COLOR_BLUE | Curses.COLOR_GRAY: + return Color.BrightBlue; + case Curses.COLOR_GREEN | Curses.COLOR_GRAY: + return Color.BrightGreen; + case Curses.COLOR_CYAN | Curses.COLOR_GRAY: + return Color.BrightCyan; + case Curses.COLOR_RED | Curses.COLOR_GRAY: + return Color.BrightRed; + case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY: + return Color.BrightMagenta; + case Curses.COLOR_YELLOW | Curses.COLOR_GRAY: + return Color.BrightYellow; + case Curses.COLOR_WHITE | Curses.COLOR_GRAY: + return Color.White; } + throw new ArgumentException ("Invalid curses color code"); + } - static int MapColor (Color color) - { - switch (color) { - case Color.Black: - return Curses.COLOR_BLACK; - case Color.Blue: - return Curses.COLOR_BLUE; - case Color.Green: - return Curses.COLOR_GREEN; - case Color.Cyan: - return Curses.COLOR_CYAN; - case Color.Red: - return Curses.COLOR_RED; - case Color.Magenta: - return Curses.COLOR_MAGENTA; - case Color.Brown: - return Curses.COLOR_YELLOW; - case Color.Gray: - return Curses.COLOR_WHITE; - case Color.DarkGray: - //return Curses.COLOR_BLACK | Curses.A_BOLD; - return Curses.COLOR_GRAY; - case Color.BrightBlue: - return Curses.COLOR_BLUE | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightGreen: - return Curses.COLOR_GREEN | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightCyan: - return Curses.COLOR_CYAN | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightRed: - return Curses.COLOR_RED | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightMagenta: - return Curses.COLOR_MAGENTA | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightYellow: - return Curses.COLOR_YELLOW | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.White: - return Curses.COLOR_WHITE | Curses.A_BOLD | Curses.COLOR_GRAY; - } - throw new ArgumentException ("Invalid color code"); - } - - static Color MapCursesColor (int color) - { - switch (color) { - case Curses.COLOR_BLACK: - return Color.Black; - case Curses.COLOR_BLUE: - return Color.Blue; - case Curses.COLOR_GREEN: - return Color.Green; - case Curses.COLOR_CYAN: - return Color.Cyan; - case Curses.COLOR_RED: - return Color.Red; - case Curses.COLOR_MAGENTA: - return Color.Magenta; - case Curses.COLOR_YELLOW: - return Color.Brown; - case Curses.COLOR_WHITE: - return Color.Gray; - case Curses.COLOR_GRAY: - return Color.DarkGray; - case Curses.COLOR_BLUE | Curses.COLOR_GRAY: - return Color.BrightBlue; - case Curses.COLOR_GREEN | Curses.COLOR_GRAY: - return Color.BrightGreen; - case Curses.COLOR_CYAN | Curses.COLOR_GRAY: - return Color.BrightCyan; - case Curses.COLOR_RED | Curses.COLOR_GRAY: - return Color.BrightRed; - case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY: - return Color.BrightMagenta; - case Curses.COLOR_YELLOW | Curses.COLOR_GRAY: - return Color.BrightYellow; - case Curses.COLOR_WHITE | Curses.COLOR_GRAY: - return Color.White; - } - throw new ArgumentException ("Invalid curses color code"); - } + public override Attribute MakeAttribute (Color fore, Color back) + { + return MakeColor ((short)(MapColor (fore) & 0xffff), (short)MapColor (back)); + } - public override Attribute MakeAttribute (Color fore, Color back) - { - return MakeColor ((short)(MapColor (fore) & 0xffff), (short)MapColor (back)); - } + public override void Suspend () + { + StopReportingMouseMoves (); + Platform.Suspend (); + Curses.Window.Standard.redrawwin (); + Curses.refresh (); + StartReportingMouseMoves (); + } - public override void Suspend () - { - StopReportingMouseMoves (); - Platform.Suspend (); - Curses.Window.Standard.redrawwin (); - Curses.refresh (); - StartReportingMouseMoves (); - } + public void StartReportingMouseMoves () + { + Console.Out.Write (EscSeqUtils.EnableMouseEvents); + } - public void StartReportingMouseMoves () - { - Console.Out.Write (EscSeqUtils.EnableMouseEvents); - } + public void StopReportingMouseMoves () + { + Console.Out.Write (EscSeqUtils.DisableMouseEvents); + } - public void StopReportingMouseMoves () - { - Console.Out.Write (EscSeqUtils.DisableMouseEvents); - } + /// + public override bool GetCursorVisibility (out CursorVisibility visibility) + { + visibility = CursorVisibility.Invisible; - /// - public override bool GetCursorVisibility (out CursorVisibility visibility) - { - visibility = CursorVisibility.Invisible; + if (!_currentCursorVisibility.HasValue) + return false; - if (!_currentCursorVisibility.HasValue) - return false; + visibility = _currentCursorVisibility.Value; - visibility = _currentCursorVisibility.Value; + return true; + } - return true; + /// + public override bool SetCursorVisibility (CursorVisibility visibility) + { + if (_initialCursorVisibility.HasValue == false) { + return false; } - /// - public override bool SetCursorVisibility (CursorVisibility visibility) - { - if (_initialCursorVisibility.HasValue == false) { - return false; - } - - Curses.curs_set (((int)visibility >> 16) & 0x000000FF); - - if (visibility != CursorVisibility.Invisible) { - Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF); - } - - _currentCursorVisibility = visibility; + Curses.curs_set (((int)visibility >> 16) & 0x000000FF); - return true; + if (visibility != CursorVisibility.Invisible) { + Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF); } - /// - public override bool EnsureCursorVisibility () - { - return false; - } + _currentCursorVisibility = visibility; - public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control) - { - Key key; + return true; + } - if (consoleKey == ConsoleKey.Packet) { - ConsoleModifiers mod = new ConsoleModifiers (); - if (shift) { - mod |= ConsoleModifiers.Shift; - } - if (alt) { - mod |= ConsoleModifiers.Alt; - } - if (control) { - mod |= ConsoleModifiers.Control; - } - var kchar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyChar, mod, out uint ckey, out _); - key = ConsoleKeyMapping.MapConsoleKeyToKey ((ConsoleKey)ckey, out bool mappable); - if (mappable) { - key = (Key)kchar; - } - } else { - key = (Key)keyChar; - } + /// + public override bool EnsureCursorVisibility () + { + return false; + } + + public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control) + { + Key key; - KeyModifiers km = new KeyModifiers (); + if (consoleKey == ConsoleKey.Packet) { + ConsoleModifiers mod = new ConsoleModifiers (); if (shift) { - if (keyChar == 0) { - key |= Key.ShiftMask; - } - km.Shift = shift; + mod |= ConsoleModifiers.Shift; } if (alt) { - key |= Key.AltMask; - km.Alt = alt; + mod |= ConsoleModifiers.Alt; } if (control) { - key |= Key.CtrlMask; - km.Ctrl = control; + mod |= ConsoleModifiers.Control; } - _keyDownHandler (new KeyEvent (key, km)); - _keyHandler (new KeyEvent (key, km)); - _keyUpHandler (new KeyEvent (key, km)); - } - - public override bool GetColors (int value, out Color foreground, out Color background) - { - bool hasColor = false; - foreground = default; - background = default; - int back = -1; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains ((value >> 12) & 0xffff)) { - hasColor = true; - back = (value >> 12) & 0xffff; - background = MapCursesColor (back); + var kchar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyChar, mod, out uint ckey, out _); + key = ConsoleKeyMapping.MapConsoleKeyToKey ((ConsoleKey)ckey, out bool mappable); + if (mappable) { + key = (Key)kchar; } - if (values.Contains ((value - (back << 12)) >> 8)) { - hasColor = true; - foreground = MapCursesColor ((value - (back << 12)) >> 8); + } else { + key = (Key)keyChar; + } + + KeyModifiers km = new KeyModifiers (); + if (shift) { + if (keyChar == 0) { + key |= Key.ShiftMask; } - return hasColor; + km.Shift = shift; + } + if (alt) { + key |= Key.AltMask; + km.Alt = alt; + } + if (control) { + key |= Key.CtrlMask; + km.Ctrl = control; } + _keyDownHandler (new KeyEvent (key, km)); + _keyHandler (new KeyEvent (key, km)); + _keyUpHandler (new KeyEvent (key, km)); } - internal static class Platform { - [DllImport ("libc")] - static extern int uname (IntPtr buf); + public override bool GetColors (int value, out Color foreground, out Color background) + { + bool hasColor = false; + foreground = default; + background = default; + int back = -1; + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains ((value >> 12) & 0xffff)) { + hasColor = true; + back = (value >> 12) & 0xffff; + background = MapCursesColor (back); + } + if (values.Contains ((value - (back << 12)) >> 8)) { + hasColor = true; + foreground = MapCursesColor ((value - (back << 12)) >> 8); + } + return hasColor; + } +} - [DllImport ("libc")] - static extern int killpg (int pgrp, int pid); +internal static class Platform { + [DllImport ("libc")] + static extern int uname (IntPtr buf); - static int _suspendSignal; + [DllImport ("libc")] + static extern int killpg (int pgrp, int pid); - static int GetSuspendSignal () - { - if (_suspendSignal != 0) { - return _suspendSignal; - } + static int _suspendSignal; + + static int GetSuspendSignal () + { + if (_suspendSignal != 0) { + return _suspendSignal; + } - IntPtr buf = Marshal.AllocHGlobal (8192); - if (uname (buf) != 0) { - Marshal.FreeHGlobal (buf); + IntPtr buf = Marshal.AllocHGlobal (8192); + if (uname (buf) != 0) { + Marshal.FreeHGlobal (buf); + _suspendSignal = -1; + return _suspendSignal; + } + try { + switch (Marshal.PtrToStringAnsi (buf)) { + case "Darwin": + case "DragonFly": + case "FreeBSD": + case "NetBSD": + case "OpenBSD": + _suspendSignal = 18; + break; + case "Linux": + // TODO: should fetch the machine name and + // if it is MIPS return 24 + _suspendSignal = 20; + break; + case "Solaris": + _suspendSignal = 24; + break; + default: _suspendSignal = -1; - return _suspendSignal; - } - try { - switch (Marshal.PtrToStringAnsi (buf)) { - case "Darwin": - case "DragonFly": - case "FreeBSD": - case "NetBSD": - case "OpenBSD": - _suspendSignal = 18; - break; - case "Linux": - // TODO: should fetch the machine name and - // if it is MIPS return 24 - _suspendSignal = 20; - break; - case "Solaris": - _suspendSignal = 24; - break; - default: - _suspendSignal = -1; - break; - } - return _suspendSignal; - } finally { - Marshal.FreeHGlobal (buf); + break; } + return _suspendSignal; + } finally { + Marshal.FreeHGlobal (buf); } + } - /// - /// Suspends the process by sending SIGTSTP to itself - /// - /// The suspend. - static public bool Suspend () - { - int signal = GetSuspendSignal (); - if (signal == -1) { - return false; - } - killpg (0, signal); - return true; + /// + /// Suspends the process by sending SIGTSTP to itself + /// + /// The suspend. + static public bool Suspend () + { + int signal = GetSuspendSignal (); + if (signal == -1) { + return false; } + killpg (0, signal); + return true; } -} +} \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs index b4fc5fc0b0..79008ccbbf 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs @@ -5,2014 +5,2018 @@ using System.Collections.Generic; using System.IO; using System.Text; +using Rune = System.Text.Rune; -namespace Terminal.Gui { +namespace Terminal.Gui; #pragma warning disable RCS1138 // Add summary to documentation comment. +/// +/// +/// +public static class FakeConsole { +#pragma warning restore RCS1138 // Add summary to documentation comment. + + // + // Summary: + // Gets or sets the width of the console window. + // + // Returns: + // The width of the console window measured in columns. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight + // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight + // property plus the value of the System.Console.WindowTop property is greater than + // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth + // property or the value of the System.Console.WindowHeight property is greater + // than the largest possible window width or height for the current screen resolution + // and console font. + // + // T:System.IO.IOException: + // Error reading or writing information. +#pragma warning disable RCS1138 // Add summary to documentation comment. + + /// + /// Specifies the initial console width. + /// + public const int WIDTH = 80; + + /// + /// Specifies the initial console height. + /// + public const int HEIGHT = 25; + /// /// /// - public static class FakeConsole { -#pragma warning restore RCS1138 // Add summary to documentation comment. + public static int WindowWidth { get; set; } = WIDTH; + // + // Summary: + // Gets a value that indicates whether output has been redirected from the standard + // output stream. + // + // Returns: + // true if output is redirected; otherwise, false. + /// + /// + /// + public static bool IsOutputRedirected { get; } + // + // Summary: + // Gets a value that indicates whether the error output stream has been redirected + // from the standard error stream. + // + // Returns: + // true if error output is redirected; otherwise, false. + /// + /// + /// + public static bool IsErrorRedirected { get; } + // + // Summary: + // Gets the standard input stream. + // + // Returns: + // A System.IO.TextReader that represents the standard input stream. + /// + /// + /// + public static TextReader In { get; } + // + // Summary: + // Gets the standard output stream. + // + // Returns: + // A System.IO.TextWriter that represents the standard output stream. + /// + /// + /// + public static TextWriter Out { get; } + // + // Summary: + // Gets the standard error output stream. + // + // Returns: + // A System.IO.TextWriter that represents the standard error output stream. + /// + /// + /// + public static TextWriter Error { get; } + // + // Summary: + // Gets or sets the encoding the console uses to read input. + // + // Returns: + // The encoding used to read console input. + // + // Exceptions: + // T:System.ArgumentNullException: + // The property value in a set operation is null. + // + // T:System.IO.IOException: + // An error occurred during the execution of this operation. + // + // T:System.Security.SecurityException: + // Your application does not have permission to perform this operation. + /// + /// + /// + public static Encoding InputEncoding { get; set; } + // + // Summary: + // Gets or sets the encoding the console uses to write output. + // + // Returns: + // The encoding used to write console output. + // + // Exceptions: + // T:System.ArgumentNullException: + // The property value in a set operation is null. + // + // T:System.IO.IOException: + // An error occurred during the execution of this operation. + // + // T:System.Security.SecurityException: + // Your application does not have permission to perform this operation. + /// + /// + /// + public static Encoding OutputEncoding { get; set; } + // + // Summary: + // Gets or sets the background color of the console. + // + // Returns: + // A value that specifies the background color of the console; that is, the color + // that appears behind each character. The default is black. + // + // Exceptions: + // T:System.ArgumentException: + // The color specified in a set operation is not a valid member of System.ConsoleColor. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + + static ConsoleColor _defaultBackgroundColor = ConsoleColor.Black; - // - // Summary: - // Gets or sets the width of the console window. - // - // Returns: - // The width of the console window measured in columns. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight - // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight - // property plus the value of the System.Console.WindowTop property is greater than - // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth - // property or the value of the System.Console.WindowHeight property is greater - // than the largest possible window width or height for the current screen resolution - // and console font. - // - // T:System.IO.IOException: - // Error reading or writing information. -#pragma warning disable RCS1138 // Add summary to documentation comment. + /// + /// + /// + public static ConsoleColor BackgroundColor { get; set; } = _defaultBackgroundColor; + + // + // Summary: + // Gets or sets the foreground color of the console. + // + // Returns: + // A System.ConsoleColor that specifies the foreground color of the console; that + // is, the color of each character that is displayed. The default is gray. + // + // Exceptions: + // T:System.ArgumentException: + // The color specified in a set operation is not a valid member of System.ConsoleColor. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + + static ConsoleColor _defaultForegroundColor = ConsoleColor.Gray; - /// - /// Specifies the initial console width. - /// - public const int WIDTH = 80; - - /// - /// Specifies the initial console height. - /// - public const int HEIGHT = 25; - - /// - /// - /// - public static int WindowWidth { get; set; } = WIDTH; - // - // Summary: - // Gets a value that indicates whether output has been redirected from the standard - // output stream. - // - // Returns: - // true if output is redirected; otherwise, false. - /// - /// - /// - public static bool IsOutputRedirected { get; } - // - // Summary: - // Gets a value that indicates whether the error output stream has been redirected - // from the standard error stream. - // - // Returns: - // true if error output is redirected; otherwise, false. - /// - /// - /// - public static bool IsErrorRedirected { get; } - // - // Summary: - // Gets the standard input stream. - // - // Returns: - // A System.IO.TextReader that represents the standard input stream. - /// - /// - /// - public static TextReader In { get; } - // - // Summary: - // Gets the standard output stream. - // - // Returns: - // A System.IO.TextWriter that represents the standard output stream. - /// - /// - /// - public static TextWriter Out { get; } - // - // Summary: - // Gets the standard error output stream. - // - // Returns: - // A System.IO.TextWriter that represents the standard error output stream. - /// - /// - /// - public static TextWriter Error { get; } - // - // Summary: - // Gets or sets the encoding the console uses to read input. - // - // Returns: - // The encoding used to read console input. - // - // Exceptions: - // T:System.ArgumentNullException: - // The property value in a set operation is null. - // - // T:System.IO.IOException: - // An error occurred during the execution of this operation. - // - // T:System.Security.SecurityException: - // Your application does not have permission to perform this operation. - /// - /// - /// - public static Encoding InputEncoding { get; set; } - // - // Summary: - // Gets or sets the encoding the console uses to write output. - // - // Returns: - // The encoding used to write console output. - // - // Exceptions: - // T:System.ArgumentNullException: - // The property value in a set operation is null. - // - // T:System.IO.IOException: - // An error occurred during the execution of this operation. - // - // T:System.Security.SecurityException: - // Your application does not have permission to perform this operation. - /// - /// - /// - public static Encoding OutputEncoding { get; set; } - // - // Summary: - // Gets or sets the background color of the console. - // - // Returns: - // A value that specifies the background color of the console; that is, the color - // that appears behind each character. The default is black. - // - // Exceptions: - // T:System.ArgumentException: - // The color specified in a set operation is not a valid member of System.ConsoleColor. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - - static ConsoleColor _defaultBackgroundColor = ConsoleColor.Black; - - /// - /// - /// - public static ConsoleColor BackgroundColor { get; set; } = _defaultBackgroundColor; - - // - // Summary: - // Gets or sets the foreground color of the console. - // - // Returns: - // A System.ConsoleColor that specifies the foreground color of the console; that - // is, the color of each character that is displayed. The default is gray. - // - // Exceptions: - // T:System.ArgumentException: - // The color specified in a set operation is not a valid member of System.ConsoleColor. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - - static ConsoleColor _defaultForegroundColor = ConsoleColor.Gray; - - /// - /// - /// - public static ConsoleColor ForegroundColor { get; set; } = _defaultForegroundColor; - // - // Summary: - // Gets or sets the height of the buffer area. - // - // Returns: - // The current height, in rows, of the buffer area. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than or equal to zero.-or- The value in - // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value - // in a set operation is less than System.Console.WindowTop + System.Console.WindowHeight. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static int BufferHeight { get; set; } = HEIGHT; - // - // Summary: - // Gets or sets the width of the buffer area. - // - // Returns: - // The current width, in columns, of the buffer area. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than or equal to zero.-or- The value in - // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value - // in a set operation is less than System.Console.WindowLeft + System.Console.WindowWidth. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static int BufferWidth { get; set; } = WIDTH; - - // - // Summary: - // Gets or sets the height of the console window area. - // - // Returns: - // The height of the console window measured in rows. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight - // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight - // property plus the value of the System.Console.WindowTop property is greater than - // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth - // property or the value of the System.Console.WindowHeight property is greater - // than the largest possible window width or height for the current screen resolution - // and console font. - // - // T:System.IO.IOException: - // Error reading or writing information. - /// - /// - /// - public static int WindowHeight { get; set; } = HEIGHT; - // - // Summary: - // Gets or sets a value indicating whether the combination of the System.ConsoleModifiers.Control - // modifier key and System.ConsoleKey.C console key (Ctrl+C) is treated as ordinary - // input or as an interruption that is handled by the operating system. - // - // Returns: - // true if Ctrl+C is treated as ordinary input; otherwise, false. - // - // Exceptions: - // T:System.IO.IOException: - // Unable to get or set the input mode of the console input buffer. - /// - /// - /// - public static bool TreatControlCAsInput { get; set; } - // - // Summary: - // Gets the largest possible number of console window columns, based on the current - // font and screen resolution. - // - // Returns: - // The width of the largest possible console window measured in columns. - /// - /// - /// - public static int LargestWindowWidth { get; } - // - // Summary: - // Gets the largest possible number of console window rows, based on the current - // font and screen resolution. - // - // Returns: - // The height of the largest possible console window measured in rows. - /// - /// - /// - public static int LargestWindowHeight { get; } - // - // Summary: - // Gets or sets the leftmost position of the console window area relative to the - // screen buffer. - // - // Returns: - // The leftmost console window position measured in columns. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // In a set operation, the value to be assigned is less than zero.-or-As a result - // of the assignment, System.Console.WindowLeft plus System.Console.WindowWidth - // would exceed System.Console.BufferWidth. - // - // T:System.IO.IOException: - // Error reading or writing information. - /// - /// - /// - public static int WindowLeft { get; set; } - // - // Summary: - // Gets or sets the top position of the console window area relative to the screen - // buffer. - // - // Returns: - // The uppermost console window position measured in rows. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // In a set operation, the value to be assigned is less than zero.-or-As a result - // of the assignment, System.Console.WindowTop plus System.Console.WindowHeight - // would exceed System.Console.BufferHeight. - // - // T:System.IO.IOException: - // Error reading or writing information. - /// - /// - /// - public static int WindowTop { get; set; } - // - // Summary: - // Gets or sets the column position of the cursor within the buffer area. - // - // Returns: - // The current position, in columns, of the cursor. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than zero.-or- The value in a set operation - // is greater than or equal to System.Console.BufferWidth. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static int CursorLeft { get; set; } - // - // Summary: - // Gets or sets the row position of the cursor within the buffer area. - // - // Returns: - // The current position, in rows, of the cursor. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than zero.-or- The value in a set operation - // is greater than or equal to System.Console.BufferHeight. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static int CursorTop { get; set; } - // - // Summary: - // Gets or sets the height of the cursor within a character cell. - // - // Returns: - // The size of the cursor expressed as a percentage of the height of a character - // cell. The property value ranges from 1 to 100. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value specified in a set operation is less than 1 or greater than 100. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static int CursorSize { get; set; } - // - // Summary: - // Gets or sets a value indicating whether the cursor is visible. - // - // Returns: - // true if the cursor is visible; otherwise, false. - // - // Exceptions: - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static bool CursorVisible { get; set; } - // - // Summary: - // Gets or sets the title to display in the console title bar. - // - // Returns: - // The string to be displayed in the title bar of the console. The maximum length - // of the title string is 24500 characters. - // - // Exceptions: - // T:System.InvalidOperationException: - // In a get operation, the retrieved title is longer than 24500 characters. - // - // T:System.ArgumentOutOfRangeException: - // In a set operation, the specified title is longer than 24500 characters. - // - // T:System.ArgumentNullException: - // In a set operation, the specified title is null. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static string Title { get; set; } - // - // Summary: - // Gets a value indicating whether a key press is available in the input stream. - // - // Returns: - // true if a key press is available; otherwise, false. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.InvalidOperationException: - // Standard input is redirected to a file instead of the keyboard. - /// - /// - /// - public static bool KeyAvailable { get; } - // - // Summary: - // Gets a value indicating whether the NUM LOCK keyboard toggle is turned on or - // turned off. - // - // Returns: - // true if NUM LOCK is turned on; false if NUM LOCK is turned off. - /// - /// - /// - public static bool NumberLock { get; } - // - // Summary: - // Gets a value indicating whether the CAPS LOCK keyboard toggle is turned on or - // turned off. - // - // Returns: - // true if CAPS LOCK is turned on; false if CAPS LOCK is turned off. - /// - /// - /// - public static bool CapsLock { get; } - // - // Summary: - // Gets a value that indicates whether input has been redirected from the standard - // input stream. - // - // Returns: - // true if input is redirected; otherwise, false. - /// - /// - /// - public static bool IsInputRedirected { get; } - - // - // Summary: - // Plays the sound of a beep through the console speaker. - // - // Exceptions: - // T:System.Security.HostProtectionException: - // This method was executed on a server, such as SQL Server, that does not permit - // access to a user interface. - /// - /// - /// - public static void Beep () - { - throw new NotImplementedException (); - } - // - // Summary: - // Plays the sound of a beep of a specified frequency and duration through the console - // speaker. - // - // Parameters: - // frequency: - // The frequency of the beep, ranging from 37 to 32767 hertz. - // - // duration: - // The duration of the beep measured in milliseconds. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // frequency is less than 37 or more than 32767 hertz.-or- duration is less than - // or equal to zero. - // - // T:System.Security.HostProtectionException: - // This method was executed on a server, such as SQL Server, that does not permit - // access to the console. - /// - /// - /// - public static void Beep (int frequency, int duration) - { - throw new NotImplementedException (); - } - // - // Summary: - // Clears the console buffer and corresponding console window of display information. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - - static char [,] _buffer = new char [WindowWidth, WindowHeight]; - - /// - /// - /// - public static void Clear () - { - _buffer = new char [BufferWidth, BufferHeight]; - SetCursorPosition (0, 0); - } + /// + /// + /// + public static ConsoleColor ForegroundColor { get; set; } = _defaultForegroundColor; + // + // Summary: + // Gets or sets the height of the buffer area. + // + // Returns: + // The current height, in rows, of the buffer area. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than or equal to zero.-or- The value in + // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value + // in a set operation is less than System.Console.WindowTop + System.Console.WindowHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static int BufferHeight { get; set; } = HEIGHT; + // + // Summary: + // Gets or sets the width of the buffer area. + // + // Returns: + // The current width, in columns, of the buffer area. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than or equal to zero.-or- The value in + // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value + // in a set operation is less than System.Console.WindowLeft + System.Console.WindowWidth. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static int BufferWidth { get; set; } = WIDTH; + + // + // Summary: + // Gets or sets the height of the console window area. + // + // Returns: + // The height of the console window measured in rows. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight + // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight + // property plus the value of the System.Console.WindowTop property is greater than + // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth + // property or the value of the System.Console.WindowHeight property is greater + // than the largest possible window width or height for the current screen resolution + // and console font. + // + // T:System.IO.IOException: + // Error reading or writing information. + /// + /// + /// + public static int WindowHeight { get; set; } = HEIGHT; + // + // Summary: + // Gets or sets a value indicating whether the combination of the System.ConsoleModifiers.Control + // modifier key and System.ConsoleKey.C console key (Ctrl+C) is treated as ordinary + // input or as an interruption that is handled by the operating system. + // + // Returns: + // true if Ctrl+C is treated as ordinary input; otherwise, false. + // + // Exceptions: + // T:System.IO.IOException: + // Unable to get or set the input mode of the console input buffer. + /// + /// + /// + public static bool TreatControlCAsInput { get; set; } + // + // Summary: + // Gets the largest possible number of console window columns, based on the current + // font and screen resolution. + // + // Returns: + // The width of the largest possible console window measured in columns. + /// + /// + /// + public static int LargestWindowWidth { get; } + // + // Summary: + // Gets the largest possible number of console window rows, based on the current + // font and screen resolution. + // + // Returns: + // The height of the largest possible console window measured in rows. + /// + /// + /// + public static int LargestWindowHeight { get; } + // + // Summary: + // Gets or sets the leftmost position of the console window area relative to the + // screen buffer. + // + // Returns: + // The leftmost console window position measured in columns. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // In a set operation, the value to be assigned is less than zero.-or-As a result + // of the assignment, System.Console.WindowLeft plus System.Console.WindowWidth + // would exceed System.Console.BufferWidth. + // + // T:System.IO.IOException: + // Error reading or writing information. + /// + /// + /// + public static int WindowLeft { get; set; } + // + // Summary: + // Gets or sets the top position of the console window area relative to the screen + // buffer. + // + // Returns: + // The uppermost console window position measured in rows. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // In a set operation, the value to be assigned is less than zero.-or-As a result + // of the assignment, System.Console.WindowTop plus System.Console.WindowHeight + // would exceed System.Console.BufferHeight. + // + // T:System.IO.IOException: + // Error reading or writing information. + /// + /// + /// + public static int WindowTop { get; set; } + // + // Summary: + // Gets or sets the column position of the cursor within the buffer area. + // + // Returns: + // The current position, in columns, of the cursor. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than zero.-or- The value in a set operation + // is greater than or equal to System.Console.BufferWidth. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static int CursorLeft { get; set; } + // + // Summary: + // Gets or sets the row position of the cursor within the buffer area. + // + // Returns: + // The current position, in rows, of the cursor. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than zero.-or- The value in a set operation + // is greater than or equal to System.Console.BufferHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static int CursorTop { get; set; } + // + // Summary: + // Gets or sets the height of the cursor within a character cell. + // + // Returns: + // The size of the cursor expressed as a percentage of the height of a character + // cell. The property value ranges from 1 to 100. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The value specified in a set operation is less than 1 or greater than 100. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static int CursorSize { get; set; } + // + // Summary: + // Gets or sets a value indicating whether the cursor is visible. + // + // Returns: + // true if the cursor is visible; otherwise, false. + // + // Exceptions: + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static bool CursorVisible { get; set; } + // + // Summary: + // Gets or sets the title to display in the console title bar. + // + // Returns: + // The string to be displayed in the title bar of the console. The maximum length + // of the title string is 24500 characters. + // + // Exceptions: + // T:System.InvalidOperationException: + // In a get operation, the retrieved title is longer than 24500 characters. + // + // T:System.ArgumentOutOfRangeException: + // In a set operation, the specified title is longer than 24500 characters. + // + // T:System.ArgumentNullException: + // In a set operation, the specified title is null. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static string Title { get; set; } + // + // Summary: + // Gets a value indicating whether a key press is available in the input stream. + // + // Returns: + // true if a key press is available; otherwise, false. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.InvalidOperationException: + // Standard input is redirected to a file instead of the keyboard. + /// + /// + /// + public static bool KeyAvailable { get; } + // + // Summary: + // Gets a value indicating whether the NUM LOCK keyboard toggle is turned on or + // turned off. + // + // Returns: + // true if NUM LOCK is turned on; false if NUM LOCK is turned off. + /// + /// + /// + public static bool NumberLock { get; } + // + // Summary: + // Gets a value indicating whether the CAPS LOCK keyboard toggle is turned on or + // turned off. + // + // Returns: + // true if CAPS LOCK is turned on; false if CAPS LOCK is turned off. + /// + /// + /// + public static bool CapsLock { get; } + // + // Summary: + // Gets a value that indicates whether input has been redirected from the standard + // input stream. + // + // Returns: + // true if input is redirected; otherwise, false. + /// + /// + /// + public static bool IsInputRedirected { get; } + + // + // Summary: + // Plays the sound of a beep through the console speaker. + // + // Exceptions: + // T:System.Security.HostProtectionException: + // This method was executed on a server, such as SQL Server, that does not permit + // access to a user interface. + /// + /// + /// + public static void Beep () + { + throw new NotImplementedException (); + } + // + // Summary: + // Plays the sound of a beep of a specified frequency and duration through the console + // speaker. + // + // Parameters: + // frequency: + // The frequency of the beep, ranging from 37 to 32767 hertz. + // + // duration: + // The duration of the beep measured in milliseconds. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // frequency is less than 37 or more than 32767 hertz.-or- duration is less than + // or equal to zero. + // + // T:System.Security.HostProtectionException: + // This method was executed on a server, such as SQL Server, that does not permit + // access to the console. + /// + /// + /// + public static void Beep (int frequency, int duration) + { + throw new NotImplementedException (); + } + // + // Summary: + // Clears the console buffer and corresponding console window of display information. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. - // - // Summary: - // Copies a specified source area of the screen buffer to a specified destination - // area. - // - // Parameters: - // sourceLeft: - // The leftmost column of the source area. - // - // sourceTop: - // The topmost row of the source area. - // - // sourceWidth: - // The number of columns in the source area. - // - // sourceHeight: - // The number of rows in the source area. - // - // targetLeft: - // The leftmost column of the destination area. - // - // targetTop: - // The topmost row of the destination area. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft - // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop - // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight - // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth - // is greater than or equal to System.Console.BufferWidth. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop) - { - throw new NotImplementedException (); - } + static char [,] _buffer = new char [WindowWidth, WindowHeight]; - // - // Summary: - // Copies a specified source area of the screen buffer to a specified destination - // area. - // - // Parameters: - // sourceLeft: - // The leftmost column of the source area. - // - // sourceTop: - // The topmost row of the source area. - // - // sourceWidth: - // The number of columns in the source area. - // - // sourceHeight: - // The number of rows in the source area. - // - // targetLeft: - // The leftmost column of the destination area. - // - // targetTop: - // The topmost row of the destination area. - // - // sourceChar: - // The character used to fill the source area. - // - // sourceForeColor: - // The foreground color used to fill the source area. - // - // sourceBackColor: - // The background color used to fill the source area. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft - // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop - // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight - // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth - // is greater than or equal to System.Console.BufferWidth. - // - // T:System.ArgumentException: - // One or both of the color parameters is not a member of the System.ConsoleColor - // enumeration. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - //[SecuritySafeCritical] - /// - /// - /// - public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop, char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor) - { - throw new NotImplementedException (); - } + /// + /// + /// + public static void Clear () + { + _buffer = new char [BufferWidth, BufferHeight]; + SetCursorPosition (0, 0); + } - // - // Summary: - // Acquires the standard error stream. - // - // Returns: - // The standard error stream. - /// - /// - /// - public static Stream OpenStandardError () - { - throw new NotImplementedException (); - } + // + // Summary: + // Copies a specified source area of the screen buffer to a specified destination + // area. + // + // Parameters: + // sourceLeft: + // The leftmost column of the source area. + // + // sourceTop: + // The topmost row of the source area. + // + // sourceWidth: + // The number of columns in the source area. + // + // sourceHeight: + // The number of rows in the source area. + // + // targetLeft: + // The leftmost column of the destination area. + // + // targetTop: + // The topmost row of the destination area. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft + // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop + // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight + // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth + // is greater than or equal to System.Console.BufferWidth. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop) + { + throw new NotImplementedException (); + } - // - // Summary: - // Acquires the standard error stream, which is set to a specified buffer size. - // - // Parameters: - // bufferSize: - // The internal stream buffer size. - // - // Returns: - // The standard error stream. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // bufferSize is less than or equal to zero. - /// - /// - /// - public static Stream OpenStandardError (int bufferSize) - { - throw new NotImplementedException (); - } + // + // Summary: + // Copies a specified source area of the screen buffer to a specified destination + // area. + // + // Parameters: + // sourceLeft: + // The leftmost column of the source area. + // + // sourceTop: + // The topmost row of the source area. + // + // sourceWidth: + // The number of columns in the source area. + // + // sourceHeight: + // The number of rows in the source area. + // + // targetLeft: + // The leftmost column of the destination area. + // + // targetTop: + // The topmost row of the destination area. + // + // sourceChar: + // The character used to fill the source area. + // + // sourceForeColor: + // The foreground color used to fill the source area. + // + // sourceBackColor: + // The background color used to fill the source area. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft + // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop + // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight + // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth + // is greater than or equal to System.Console.BufferWidth. + // + // T:System.ArgumentException: + // One or both of the color parameters is not a member of the System.ConsoleColor + // enumeration. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + /// + /// + /// + public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop, char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor) + { + throw new NotImplementedException (); + } - // - // Summary: - // Acquires the standard input stream, which is set to a specified buffer size. - // - // Parameters: - // bufferSize: - // The internal stream buffer size. - // - // Returns: - // The standard input stream. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // bufferSize is less than or equal to zero. - /// - /// - /// - public static Stream OpenStandardInput (int bufferSize) - { - throw new NotImplementedException (); - } + // + // Summary: + // Acquires the standard error stream. + // + // Returns: + // The standard error stream. + /// + /// + /// + public static Stream OpenStandardError () + { + throw new NotImplementedException (); + } - // - // Summary: - // Acquires the standard input stream. - // - // Returns: - // The standard input stream. - /// - /// - /// - public static Stream OpenStandardInput () - { - throw new NotImplementedException (); - } + // + // Summary: + // Acquires the standard error stream, which is set to a specified buffer size. + // + // Parameters: + // bufferSize: + // The internal stream buffer size. + // + // Returns: + // The standard error stream. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. + /// + /// + /// + public static Stream OpenStandardError (int bufferSize) + { + throw new NotImplementedException (); + } - // - // Summary: - // Acquires the standard output stream, which is set to a specified buffer size. - // - // Parameters: - // bufferSize: - // The internal stream buffer size. - // - // Returns: - // The standard output stream. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // bufferSize is less than or equal to zero. - /// - /// - /// - public static Stream OpenStandardOutput (int bufferSize) - { - throw new NotImplementedException (); - } + // + // Summary: + // Acquires the standard input stream, which is set to a specified buffer size. + // + // Parameters: + // bufferSize: + // The internal stream buffer size. + // + // Returns: + // The standard input stream. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. + /// + /// + /// + public static Stream OpenStandardInput (int bufferSize) + { + throw new NotImplementedException (); + } - // - // Summary: - // Acquires the standard output stream. - // - // Returns: - // The standard output stream. - /// - /// - /// - public static Stream OpenStandardOutput () - { - throw new NotImplementedException (); - } + // + // Summary: + // Acquires the standard input stream. + // + // Returns: + // The standard input stream. + /// + /// + /// + public static Stream OpenStandardInput () + { + throw new NotImplementedException (); + } - // - // Summary: - // Reads the next character from the standard input stream. - // - // Returns: - // The next character from the input stream, or negative one (-1) if there are currently - // no more characters to be read. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static int Read () - { - throw new NotImplementedException (); - } + // + // Summary: + // Acquires the standard output stream, which is set to a specified buffer size. + // + // Parameters: + // bufferSize: + // The internal stream buffer size. + // + // Returns: + // The standard output stream. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. + /// + /// + /// + public static Stream OpenStandardOutput (int bufferSize) + { + throw new NotImplementedException (); + } - // - // Summary: - // Obtains the next character or function key pressed by the user. The pressed key - // is optionally displayed in the console window. - // - // Parameters: - // intercept: - // Determines whether to display the pressed key in the console window. true to - // not display the pressed key; otherwise, false. - // - // Returns: - // An object that describes the System.ConsoleKey constant and Unicode character, - // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo - // object also describes, in a bitwise combination of System.ConsoleModifiers values, - // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously - // with the console key. - // - // Exceptions: - // T:System.InvalidOperationException: - // The System.Console.In property is redirected from some stream other than the - // console. - //[SecuritySafeCritical] - /// - /// - /// - public static ConsoleKeyInfo ReadKey (bool intercept) - { - if (MockKeyPresses.Count > 0) { - return MockKeyPresses.Pop (); - } else { - return new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', false, false, false); - } - } + // + // Summary: + // Acquires the standard output stream. + // + // Returns: + // The standard output stream. + /// + /// + /// + public static Stream OpenStandardOutput () + { + throw new NotImplementedException (); + } - /// - /// A stack of keypresses to return when ReadKey is called. - /// - public static Stack MockKeyPresses = new Stack (); - - /// - /// Helper to push a onto . - /// - /// - public static void PushMockKeyPress (Key key) - { - MockKeyPresses.Push (new ConsoleKeyInfo ( - (char)(key & ~Key.CtrlMask & ~Key.ShiftMask & ~Key.AltMask), - ConsoleKeyMapping.GetConsoleKeyFromKey (key), - key.HasFlag (Key.ShiftMask), - key.HasFlag (Key.AltMask), - key.HasFlag (Key.CtrlMask))); - } + // + // Summary: + // Reads the next character from the standard input stream. + // + // Returns: + // The next character from the input stream, or negative one (-1) if there are currently + // no more characters to be read. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static int Read () + { + throw new NotImplementedException (); + } - // - // Summary: - // Obtains the next character or function key pressed by the user. The pressed key - // is displayed in the console window. - // - // Returns: - // An object that describes the System.ConsoleKey constant and Unicode character, - // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo - // object also describes, in a bitwise combination of System.ConsoleModifiers values, - // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously - // with the console key. - // - // Exceptions: - // T:System.InvalidOperationException: - // The System.Console.In property is redirected from some stream other than the - // console. - /// - /// - /// - public static ConsoleKeyInfo ReadKey () - { - throw new NotImplementedException (); + // + // Summary: + // Obtains the next character or function key pressed by the user. The pressed key + // is optionally displayed in the console window. + // + // Parameters: + // intercept: + // Determines whether to display the pressed key in the console window. true to + // not display the pressed key; otherwise, false. + // + // Returns: + // An object that describes the System.ConsoleKey constant and Unicode character, + // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo + // object also describes, in a bitwise combination of System.ConsoleModifiers values, + // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously + // with the console key. + // + // Exceptions: + // T:System.InvalidOperationException: + // The System.Console.In property is redirected from some stream other than the + // console. + //[SecuritySafeCritical] + /// + /// + /// + public static ConsoleKeyInfo ReadKey (bool intercept) + { + if (MockKeyPresses.Count > 0) { + return MockKeyPresses.Pop (); + } else { + return new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', false, false, false); } + } - // - // Summary: - // Reads the next line of characters from the standard input stream. - // - // Returns: - // The next line of characters from the input stream, or null if no more lines are - // available. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.OutOfMemoryException: - // There is insufficient memory to allocate a buffer for the returned string. - // - // T:System.ArgumentOutOfRangeException: - // The number of characters in the next line of characters is greater than System.Int32.MaxValue. - /// - /// - /// - public static string ReadLine () - { - throw new NotImplementedException (); - } + /// + /// A stack of keypresses to return when ReadKey is called. + /// + public static Stack MockKeyPresses = new Stack (); - // - // Summary: - // Sets the foreground and background console colors to their defaults. - // - // Exceptions: - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - //[SecuritySafeCritical] - /// - /// - /// - public static void ResetColor () - { - BackgroundColor = _defaultBackgroundColor; - ForegroundColor = _defaultForegroundColor; - } + /// + /// Helper to push a onto . + /// + /// + public static void PushMockKeyPress (Key key) + { + MockKeyPresses.Push (new ConsoleKeyInfo ( + (char)(key & ~Key.CtrlMask & ~Key.ShiftMask & ~Key.AltMask), + ConsoleKeyMapping.GetConsoleKeyFromKey (key), + key.HasFlag (Key.ShiftMask), + key.HasFlag (Key.AltMask), + key.HasFlag (Key.CtrlMask))); + } - // - // Summary: - // Sets the height and width of the screen buffer area to the specified values. - // - // Parameters: - // width: - // The width of the buffer area measured in columns. - // - // height: - // The height of the buffer area measured in rows. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // height or width is less than or equal to zero.-or- height or width is greater - // than or equal to System.Int16.MaxValue.-or- width is less than System.Console.WindowLeft - // + System.Console.WindowWidth.-or- height is less than System.Console.WindowTop - // + System.Console.WindowHeight. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - //[SecuritySafeCritical] - /// - /// - /// - public static void SetBufferSize (int width, int height) - { - BufferWidth = width; - BufferHeight = height; - _buffer = new char [BufferWidth, BufferHeight]; - } + // + // Summary: + // Obtains the next character or function key pressed by the user. The pressed key + // is displayed in the console window. + // + // Returns: + // An object that describes the System.ConsoleKey constant and Unicode character, + // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo + // object also describes, in a bitwise combination of System.ConsoleModifiers values, + // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously + // with the console key. + // + // Exceptions: + // T:System.InvalidOperationException: + // The System.Console.In property is redirected from some stream other than the + // console. + /// + /// + /// + public static ConsoleKeyInfo ReadKey () + { + throw new NotImplementedException (); + } - // - // Summary: - // Sets the position of the cursor. - // - // Parameters: - // left: - // The column position of the cursor. Columns are numbered from left to right starting - // at 0. - // - // top: - // The row position of the cursor. Rows are numbered from top to bottom starting - // at 0. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // left or top is less than zero.-or- left is greater than or equal to System.Console.BufferWidth.-or- - // top is greater than or equal to System.Console.BufferHeight. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - //[SecuritySafeCritical] - /// - /// - /// - public static void SetCursorPosition (int left, int top) - { - CursorLeft = left; - CursorTop = top; - WindowLeft = Math.Max (Math.Min (left, BufferWidth - WindowWidth), 0); - WindowTop = Math.Max (Math.Min (top, BufferHeight - WindowHeight), 0); - } + // + // Summary: + // Reads the next line of characters from the standard input stream. + // + // Returns: + // The next line of characters from the input stream, or null if no more lines are + // available. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.OutOfMemoryException: + // There is insufficient memory to allocate a buffer for the returned string. + // + // T:System.ArgumentOutOfRangeException: + // The number of characters in the next line of characters is greater than System.Int32.MaxValue. + /// + /// + /// + public static string ReadLine () + { + throw new NotImplementedException (); + } - // - // Summary: - // Sets the System.Console.Error property to the specified System.IO.TextWriter - // object. - // - // Parameters: - // newError: - // A stream that is the new standard error output. - // - // Exceptions: - // T:System.ArgumentNullException: - // newError is null. - // - // T:System.Security.SecurityException: - // The caller does not have the required permission. - //[SecuritySafeCritical] - /// - /// - /// - public static void SetError (TextWriter newError) - { - throw new NotImplementedException (); - } + // + // Summary: + // Sets the foreground and background console colors to their defaults. + // + // Exceptions: + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + /// + /// + /// + public static void ResetColor () + { + BackgroundColor = _defaultBackgroundColor; + ForegroundColor = _defaultForegroundColor; + } - // - // Summary: - // Sets the System.Console.In property to the specified System.IO.TextReader object. - // - // Parameters: - // newIn: - // A stream that is the new standard input. - // - // Exceptions: - // T:System.ArgumentNullException: - // newIn is null. - // - // T:System.Security.SecurityException: - // The caller does not have the required permission. - //[SecuritySafeCritical] - /// - /// - /// - public static void SetIn (TextReader newIn) - { - throw new NotImplementedException (); - } + // + // Summary: + // Sets the height and width of the screen buffer area to the specified values. + // + // Parameters: + // width: + // The width of the buffer area measured in columns. + // + // height: + // The height of the buffer area measured in rows. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // height or width is less than or equal to zero.-or- height or width is greater + // than or equal to System.Int16.MaxValue.-or- width is less than System.Console.WindowLeft + // + System.Console.WindowWidth.-or- height is less than System.Console.WindowTop + // + System.Console.WindowHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + /// + /// + /// + public static void SetBufferSize (int width, int height) + { + BufferWidth = width; + BufferHeight = height; + _buffer = new char [BufferWidth, BufferHeight]; + } - // - // Summary: - // Sets the System.Console.Out property to the specified System.IO.TextWriter object. - // - // Parameters: - // newOut: - // A stream that is the new standard output. - // - // Exceptions: - // T:System.ArgumentNullException: - // newOut is null. - // - // T:System.Security.SecurityException: - // The caller does not have the required permission. - //[SecuritySafeCritical] - /// - /// - /// - /// - public static void SetOut (TextWriter newOut) - { - throw new NotImplementedException (); - } + // + // Summary: + // Sets the position of the cursor. + // + // Parameters: + // left: + // The column position of the cursor. Columns are numbered from left to right starting + // at 0. + // + // top: + // The row position of the cursor. Rows are numbered from top to bottom starting + // at 0. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // left or top is less than zero.-or- left is greater than or equal to System.Console.BufferWidth.-or- + // top is greater than or equal to System.Console.BufferHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + /// + /// + /// + public static void SetCursorPosition (int left, int top) + { + CursorLeft = left; + CursorTop = top; + WindowLeft = Math.Max (Math.Min (left, BufferWidth - WindowWidth), 0); + WindowTop = Math.Max (Math.Min (top, BufferHeight - WindowHeight), 0); + } - // - // Summary: - // Sets the position of the console window relative to the screen buffer. - // - // Parameters: - // left: - // The column position of the upper left corner of the console window. - // - // top: - // The row position of the upper left corner of the console window. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // left or top is less than zero.-or- left + System.Console.WindowWidth is greater - // than System.Console.BufferWidth.-or- top + System.Console.WindowHeight is greater - // than System.Console.BufferHeight. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - //[SecuritySafeCritical] - /// - /// - /// - /// - /// - public static void SetWindowPosition (int left, int top) - { - WindowLeft = left; - WindowTop = top; - } + // + // Summary: + // Sets the System.Console.Error property to the specified System.IO.TextWriter + // object. + // + // Parameters: + // newError: + // A stream that is the new standard error output. + // + // Exceptions: + // T:System.ArgumentNullException: + // newError is null. + // + // T:System.Security.SecurityException: + // The caller does not have the required permission. + //[SecuritySafeCritical] + /// + /// + /// + public static void SetError (TextWriter newError) + { + throw new NotImplementedException (); + } - // - // Summary: - // Sets the height and width of the console window to the specified values. - // - // Parameters: - // width: - // The width of the console window measured in columns. - // - // height: - // The height of the console window measured in rows. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // width or height is less than or equal to zero.-or- width plus System.Console.WindowLeft - // or height plus System.Console.WindowTop is greater than or equal to System.Int16.MaxValue. - // -or- width or height is greater than the largest possible window width or height - // for the current screen resolution and console font. - // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. - // - // T:System.IO.IOException: - // An I/O error occurred. - //[SecuritySafeCritical] - /// - /// - /// - /// - /// - public static void SetWindowSize (int width, int height) - { - WindowWidth = width; - WindowHeight = height; - } + // + // Summary: + // Sets the System.Console.In property to the specified System.IO.TextReader object. + // + // Parameters: + // newIn: + // A stream that is the new standard input. + // + // Exceptions: + // T:System.ArgumentNullException: + // newIn is null. + // + // T:System.Security.SecurityException: + // The caller does not have the required permission. + //[SecuritySafeCritical] + /// + /// + /// + public static void SetIn (TextReader newIn) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the specified string value to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (string value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Sets the System.Console.Out property to the specified System.IO.TextWriter object. + // + // Parameters: + // newOut: + // A stream that is the new standard output. + // + // Exceptions: + // T:System.ArgumentNullException: + // newOut is null. + // + // T:System.Security.SecurityException: + // The caller does not have the required permission. + //[SecuritySafeCritical] + /// + /// + /// + /// + public static void SetOut (TextWriter newOut) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified object to the standard output - // stream. - // - // Parameters: - // value: - // The value to write, or null. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (object value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Sets the position of the console window relative to the screen buffer. + // + // Parameters: + // left: + // The column position of the upper left corner of the console window. + // + // top: + // The row position of the upper left corner of the console window. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // left or top is less than zero.-or- left + System.Console.WindowWidth is greater + // than System.Console.BufferWidth.-or- top + System.Console.WindowHeight is greater + // than System.Console.BufferHeight. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + /// + /// + /// + /// + /// + public static void SetWindowPosition (int left, int top) + { + WindowLeft = left; + WindowTop = top; + } - // - // Summary: - // Writes the text representation of the specified 64-bit unsigned integer value - // to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - //[CLSCompliant (false)] - /// - /// - /// - /// - public static void Write (ulong value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Sets the height and width of the console window to the specified values. + // + // Parameters: + // width: + // The width of the console window measured in columns. + // + // height: + // The height of the console window measured in rows. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // width or height is less than or equal to zero.-or- width plus System.Console.WindowLeft + // or height plus System.Console.WindowTop is greater than or equal to System.Int16.MaxValue. + // -or- width or height is greater than the largest possible window width or height + // for the current screen resolution and console font. + // + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. + // + // T:System.IO.IOException: + // An I/O error occurred. + //[SecuritySafeCritical] + /// + /// + /// + /// + /// + public static void SetWindowSize (int width, int height) + { + WindowWidth = width; + WindowHeight = height; + } - // - // Summary: - // Writes the text representation of the specified 64-bit signed integer value to - // the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (long value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the specified string value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (string value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified objects to the standard output - // stream using the specified format information. - // - // Parameters: - // format: - // A composite format string (see Remarks). - // - // arg0: - // The first object to write using format. - // - // arg1: - // The second object to write using format. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.ArgumentNullException: - // format is null. - // - // T:System.FormatException: - // The format specification in format is invalid. - /// - /// - /// - /// - /// - /// - public static void Write (string format, object arg0, object arg1) - { + // + // Summary: + // Writes the text representation of the specified object to the standard output + // stream. + // + // Parameters: + // value: + // The value to write, or null. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (object value) + { + if (value is Rune rune) { + Write ((char)rune.Value); + } else { throw new NotImplementedException (); } + } - // - // Summary: - // Writes the text representation of the specified 32-bit signed integer value to - // the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (int value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified 64-bit unsigned integer value + // to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + //[CLSCompliant (false)] + /// + /// + /// + /// + public static void Write (ulong value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified object to the standard output - // stream using the specified format information. - // - // Parameters: - // format: - // A composite format string (see Remarks). - // - // arg0: - // An object to write using format. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.ArgumentNullException: - // format is null. - // - // T:System.FormatException: - // The format specification in format is invalid. - /// - /// - /// - /// - /// - public static void Write (string format, object arg0) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified 64-bit signed integer value to + // the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (long value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified 32-bit unsigned integer value - // to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - //[CLSCompliant (false)] - /// - /// - /// - /// - public static void Write (uint value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified objects to the standard output + // stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // The first object to write using format. + // + // arg1: + // The second object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + /// + /// + /// + /// + /// + /// + public static void Write (string format, object arg0, object arg1) + { + throw new NotImplementedException (); + } - //[CLSCompliant (false)] - /// - /// - /// - /// - /// - /// - /// - /// - public static void Write (string format, object arg0, object arg1, object arg2, object arg3) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified 32-bit signed integer value to + // the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (int value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified array of objects to the standard - // output stream using the specified format information. - // - // Parameters: - // format: - // A composite format string (see Remarks). - // - // arg: - // An array of objects to write using format. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.ArgumentNullException: - // format or arg is null. - // - // T:System.FormatException: - // The format specification in format is invalid. - /// - /// - /// - /// - /// - public static void Write (string format, params object [] arg) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified object to the standard output + // stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // An object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + /// + /// + /// + /// + /// + public static void Write (string format, object arg0) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified Boolean value to the standard - // output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (bool value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified 32-bit unsigned integer value + // to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + //[CLSCompliant (false)] + /// + /// + /// + /// + public static void Write (uint value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the specified Unicode character value to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (char value) - { - _buffer [CursorLeft, CursorTop] = value; - } + //[CLSCompliant (false)] + /// + /// + /// + /// + /// + /// + /// + /// + public static void Write (string format, object arg0, object arg1, object arg2, object arg3) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the specified array of Unicode characters to the standard output stream. - // - // Parameters: - // buffer: - // A Unicode character array. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (char [] buffer) - { - _buffer [CursorLeft, CursorTop] = (char)0; - foreach (var ch in buffer) { - _buffer [CursorLeft, CursorTop] += ch; - } - } + // + // Summary: + // Writes the text representation of the specified array of objects to the standard + // output stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg: + // An array of objects to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format or arg is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + /// + /// + /// + /// + /// + public static void Write (string format, params object [] arg) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the specified subarray of Unicode characters to the standard output stream. - // - // Parameters: - // buffer: - // An array of Unicode characters. - // - // index: - // The starting position in buffer. - // - // count: - // The number of characters to write. - // - // Exceptions: - // T:System.ArgumentNullException: - // buffer is null. - // - // T:System.ArgumentOutOfRangeException: - // index or count is less than zero. - // - // T:System.ArgumentException: - // index plus count specify a position that is not within buffer. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - /// - /// - public static void Write (char [] buffer, int index, int count) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified Boolean value to the standard + // output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (bool value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified objects to the standard output - // stream using the specified format information. - // - // Parameters: - // format: - // A composite format string (see Remarks). - // - // arg0: - // The first object to write using format. - // - // arg1: - // The second object to write using format. - // - // arg2: - // The third object to write using format. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.ArgumentNullException: - // format is null. - // - // T:System.FormatException: - // The format specification in format is invalid. - /// - /// - /// - /// - /// - /// - /// - public static void Write (string format, object arg0, object arg1, object arg2) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the specified Unicode character value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (char value) + { + _buffer [CursorLeft, CursorTop] = value; + } - // - // Summary: - // Writes the text representation of the specified System.Decimal value to the standard - // output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (decimal value) - { - throw new NotImplementedException (); + // + // Summary: + // Writes the specified array of Unicode characters to the standard output stream. + // + // Parameters: + // buffer: + // A Unicode character array. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (char [] buffer) + { + _buffer [CursorLeft, CursorTop] = (char)0; + foreach (var ch in buffer) { + _buffer [CursorLeft, CursorTop] += ch; } + } - // - // Summary: - // Writes the text representation of the specified single-precision floating-point - // value to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (float value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the specified subarray of Unicode characters to the standard output stream. + // + // Parameters: + // buffer: + // An array of Unicode characters. + // + // index: + // The starting position in buffer. + // + // count: + // The number of characters to write. + // + // Exceptions: + // T:System.ArgumentNullException: + // buffer is null. + // + // T:System.ArgumentOutOfRangeException: + // index or count is less than zero. + // + // T:System.ArgumentException: + // index plus count specify a position that is not within buffer. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + /// + /// + public static void Write (char [] buffer, int index, int count) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified double-precision floating-point - // value to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void Write (double value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified objects to the standard output + // stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // The first object to write using format. + // + // arg1: + // The second object to write using format. + // + // arg2: + // The third object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + /// + /// + /// + /// + /// + /// + /// + public static void Write (string format, object arg0, object arg1, object arg2) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the current line terminator to the standard output stream. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - public static void WriteLine () - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified System.Decimal value to the standard + // output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (decimal value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified single-precision floating-point - // value, followed by the current line terminator, to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (float value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified single-precision floating-point + // value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (float value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified 32-bit signed integer value, - // followed by the current line terminator, to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (int value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified double-precision floating-point + // value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void Write (double value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified 32-bit unsigned integer value, - // followed by the current line terminator, to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - //[CLSCompliant (false)] - /// - /// - /// - /// - public static void WriteLine (uint value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the current line terminator to the standard output stream. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + public static void WriteLine () + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified 64-bit signed integer value, - // followed by the current line terminator, to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (long value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified single-precision floating-point + // value, followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (float value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified 64-bit unsigned integer value, - // followed by the current line terminator, to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - //[CLSCompliant (false)] - /// - /// - /// - /// - public static void WriteLine (ulong value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified 32-bit signed integer value, + // followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (int value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified object, followed by the current - // line terminator, to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (object value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified 32-bit unsigned integer value, + // followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + //[CLSCompliant (false)] + /// + /// + /// + /// + public static void WriteLine (uint value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the specified string value, followed by the current line terminator, to - // the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (string value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified 64-bit signed integer value, + // followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (long value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified object, followed by the current - // line terminator, to the standard output stream using the specified format information. - // - // Parameters: - // format: - // A composite format string (see Remarks). - // - // arg0: - // An object to write using format. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.ArgumentNullException: - // format is null. - // - // T:System.FormatException: - // The format specification in format is invalid. - /// - /// - /// - /// - /// - public static void WriteLine (string format, object arg0) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified 64-bit unsigned integer value, + // followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + //[CLSCompliant (false)] + /// + /// + /// + /// + public static void WriteLine (ulong value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified objects, followed by the current - // line terminator, to the standard output stream using the specified format information. - // - // Parameters: - // format: - // A composite format string (see Remarks). - // - // arg0: - // The first object to write using format. - // - // arg1: - // The second object to write using format. - // - // arg2: - // The third object to write using format. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.ArgumentNullException: - // format is null. - // - // T:System.FormatException: - // The format specification in format is invalid. - /// - /// - /// - /// - /// - /// - /// - public static void WriteLine (string format, object arg0, object arg1, object arg2) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified object, followed by the current + // line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (object value) + { + throw new NotImplementedException (); + } - //[CLSCompliant (false)] - /// - /// - /// - /// - /// - /// - /// - /// - public static void WriteLine (string format, object arg0, object arg1, object arg2, object arg3) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the specified string value, followed by the current line terminator, to + // the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (string value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified array of objects, followed by - // the current line terminator, to the standard output stream using the specified - // format information. - // - // Parameters: - // format: - // A composite format string (see Remarks). - // - // arg: - // An array of objects to write using format. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.ArgumentNullException: - // format or arg is null. - // - // T:System.FormatException: - // The format specification in format is invalid. - /// - /// - /// - /// - /// - public static void WriteLine (string format, params object [] arg) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified object, followed by the current + // line terminator, to the standard output stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // An object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + /// + /// + /// + /// + /// + public static void WriteLine (string format, object arg0) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the specified subarray of Unicode characters, followed by the current - // line terminator, to the standard output stream. - // - // Parameters: - // buffer: - // An array of Unicode characters. - // - // index: - // The starting position in buffer. - // - // count: - // The number of characters to write. - // - // Exceptions: - // T:System.ArgumentNullException: - // buffer is null. - // - // T:System.ArgumentOutOfRangeException: - // index or count is less than zero. - // - // T:System.ArgumentException: - // index plus count specify a position that is not within buffer. - // - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - /// - /// - public static void WriteLine (char [] buffer, int index, int count) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified objects, followed by the current + // line terminator, to the standard output stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // The first object to write using format. + // + // arg1: + // The second object to write using format. + // + // arg2: + // The third object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + /// + /// + /// + /// + /// + /// + /// + public static void WriteLine (string format, object arg0, object arg1, object arg2) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified System.Decimal value, followed - // by the current line terminator, to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (decimal value) - { - throw new NotImplementedException (); - } + //[CLSCompliant (false)] + /// + /// + /// + /// + /// + /// + /// + /// + public static void WriteLine (string format, object arg0, object arg1, object arg2, object arg3) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the specified array of Unicode characters, followed by the current line - // terminator, to the standard output stream. - // - // Parameters: - // buffer: - // A Unicode character array. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (char [] buffer) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified array of objects, followed by + // the current line terminator, to the standard output stream using the specified + // format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg: + // An array of objects to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format or arg is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + /// + /// + /// + /// + /// + public static void WriteLine (string format, params object [] arg) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the specified Unicode character, followed by the current line terminator, - // value to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (char value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the specified subarray of Unicode characters, followed by the current + // line terminator, to the standard output stream. + // + // Parameters: + // buffer: + // An array of Unicode characters. + // + // index: + // The starting position in buffer. + // + // count: + // The number of characters to write. + // + // Exceptions: + // T:System.ArgumentNullException: + // buffer is null. + // + // T:System.ArgumentOutOfRangeException: + // index or count is less than zero. + // + // T:System.ArgumentException: + // index plus count specify a position that is not within buffer. + // + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + /// + /// + public static void WriteLine (char [] buffer, int index, int count) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified Boolean value, followed by the - // current line terminator, to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (bool value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the text representation of the specified System.Decimal value, followed + // by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (decimal value) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified objects, followed by the current - // line terminator, to the standard output stream using the specified format information. - // - // Parameters: - // format: - // A composite format string (see Remarks). - // - // arg0: - // The first object to write using format. - // - // arg1: - // The second object to write using format. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - // - // T:System.ArgumentNullException: - // format is null. - // - // T:System.FormatException: - // The format specification in format is invalid. - /// - /// - /// - /// - /// - /// - public static void WriteLine (string format, object arg0, object arg1) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the specified array of Unicode characters, followed by the current line + // terminator, to the standard output stream. + // + // Parameters: + // buffer: + // A Unicode character array. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (char [] buffer) + { + throw new NotImplementedException (); + } - // - // Summary: - // Writes the text representation of the specified double-precision floating-point - // value, followed by the current line terminator, to the standard output stream. - // - // Parameters: - // value: - // The value to write. - // - // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. - /// - /// - /// - /// - public static void WriteLine (double value) - { - throw new NotImplementedException (); - } + // + // Summary: + // Writes the specified Unicode character, followed by the current line terminator, + // value to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (char value) + { + throw new NotImplementedException (); + } + // + // Summary: + // Writes the text representation of the specified Boolean value, followed by the + // current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (bool value) + { + throw new NotImplementedException (); + } + + // + // Summary: + // Writes the text representation of the specified objects, followed by the current + // line terminator, to the standard output stream using the specified format information. + // + // Parameters: + // format: + // A composite format string (see Remarks). + // + // arg0: + // The first object to write using format. + // + // arg1: + // The second object to write using format. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + // + // T:System.ArgumentNullException: + // format is null. + // + // T:System.FormatException: + // The format specification in format is invalid. + /// + /// + /// + /// + /// + /// + public static void WriteLine (string format, object arg0, object arg1) + { + throw new NotImplementedException (); } -} + + // + // Summary: + // Writes the text representation of the specified double-precision floating-point + // value, followed by the current line terminator, to the standard output stream. + // + // Parameters: + // value: + // The value to write. + // + // Exceptions: + // T:System.IO.IOException: + // An I/O error occurred. + /// + /// + /// + /// + public static void WriteLine (double value) + { + throw new NotImplementedException (); + } + +} \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 69ed10017b..bdc2a8f861 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -2,627 +2,638 @@ // FakeDriver.cs: A fake ConsoleDriver for unit tests. // using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; -using NStack; +using System.Text; +using Rune = System.Text.Rune; // Alias Console to MockConsole so we don't accidentally use Console using Console = Terminal.Gui.FakeConsole; +using Unix.Terminal; -namespace Terminal.Gui { - /// - /// Implements a mock ConsoleDriver for unit testing - /// - public class FakeDriver : ConsoleDriver { +namespace Terminal.Gui; +/// +/// Implements a mock ConsoleDriver for unit testing +/// +public class FakeDriver : ConsoleDriver { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - public class Behaviors { + public class Behaviors { - public bool UseFakeClipboard { get; internal set; } - public bool FakeClipboardAlwaysThrowsNotSupportedException { get; internal set; } - public bool FakeClipboardIsSupportedAlwaysFalse { get; internal set; } + public bool UseFakeClipboard { get; internal set; } + public bool FakeClipboardAlwaysThrowsNotSupportedException { get; internal set; } + public bool FakeClipboardIsSupportedAlwaysFalse { get; internal set; } - public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsNotSupportedException = false, bool fakeClipboardIsSupportedAlwaysTrue = false) - { - UseFakeClipboard = useFakeClipboard; - FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; - FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue; + public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsNotSupportedException = false, bool fakeClipboardIsSupportedAlwaysTrue = false) + { + UseFakeClipboard = useFakeClipboard; + FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; + FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue; - // double check usage is correct - Debug.Assert (useFakeClipboard == false && fakeClipboardAlwaysThrowsNotSupportedException == false); - Debug.Assert (useFakeClipboard == false && fakeClipboardIsSupportedAlwaysTrue == false); - } + // double check usage is correct + Debug.Assert (useFakeClipboard == false && fakeClipboardAlwaysThrowsNotSupportedException == false); + Debug.Assert (useFakeClipboard == false && fakeClipboardIsSupportedAlwaysTrue == false); } + } - public static FakeDriver.Behaviors FakeBehaviors = new Behaviors (); - - // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - bool [] _dirtyLine; - - //void UpdateOffscreen () - //{ - // int cols = Cols; - // int rows = Rows; - - // contents = new int [rows, cols, 3]; - // for (int r = 0; r < rows; r++) { - // for (int c = 0; c < cols; c++) { - // contents [r, c, 0] = ' '; - // contents [r, c, 1] = MakeColor (ConsoleColor.Gray, ConsoleColor.Black); - // contents [r, c, 2] = 0; - // } - // } - // dirtyLine = new bool [rows]; - // for (int row = 0; row < rows; row++) - // dirtyLine [row] = true; - //} - - public FakeDriver () - { - if (FakeBehaviors.UseFakeClipboard) { - Clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse); + public static FakeDriver.Behaviors FakeBehaviors = new Behaviors (); + + // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag + bool [] _dirtyLine; + + //void UpdateOffscreen () + //{ + // int cols = Cols; + // int rows = Rows; + + // contents = new int [rows, cols, 3]; + // for (int r = 0; r < rows; r++) { + // for (int c = 0; c < cols; c++) { + // contents [r, c, 0] = ' '; + // contents [r, c, 1] = MakeColor (ConsoleColor.Gray, ConsoleColor.Black); + // contents [r, c, 2] = 0; + // } + // } + // dirtyLine = new bool [rows]; + // for (int row = 0; row < rows; row++) + // dirtyLine [row] = true; + //} + + public FakeDriver () + { + if (FakeBehaviors.UseFakeClipboard) { + Clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse); + } else { + if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { + Clipboard = new WindowsClipboard (); + } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { + Clipboard = new MacOSXClipboard (); } else { - if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { - Clipboard = new WindowsClipboard (); - } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - Clipboard = new MacOSXClipboard (); + if (CursesDriver.Is_WSL_Platform ()) { + Clipboard = new WSLClipboard (); } else { - if (CursesDriver.Is_WSL_Platform ()) { - Clipboard = new WSLClipboard (); - } else { - Clipboard = new CursesClipboard (); - } + Clipboard = new CursesClipboard (); } } } + } - public override void AddRune (Rune rune) - { - rune = MakePrintable (rune); - var runeWidth = Rune.ColumnWidth (rune); - var validLocation = IsValidLocation (Col, Row); - - if (validLocation) { - if (runeWidth == 0 && Col > 0) { - var r = Contents [Row, Col - 1, 0]; - var s = new string (new [] { (char)r, (char)rune }); - var sn = !s.IsNormalized () ? s.Normalize () : s; - var c = sn [0]; - Contents [Row, Col - 1, 0] = c; - Contents [Row, Col - 1, 1] = CurrentAttribute; - Contents [Row, Col - 1, 2] = 1; - } else { - if (runeWidth < 2 && Col > 0 - && Rune.ColumnWidth (Contents [Row, Col - 1, 0]) > 1) { - - Contents [Row, Col - 1, 0] = System.Text.Rune.ReplacementChar.Value; - - } else if (runeWidth < 2 && Col <= Clip.Right - 1 - && Rune.ColumnWidth (Contents [Row, Col, 0]) > 1) { + public override void AddRune (System.Rune systemRune) + { + var rune = new Rune (systemRune).MakePrintable (); + var runeWidth = rune.GetColumnWidth (); + var validLocation = IsValidLocation (Col, Row); + + if (validLocation) { + if (rune.IsCombiningMark () && Col > 0) { + // Decode the previous rune + var previousRune = new Rune (Contents [Row, Col - 1, 0]); + var newCombo = new StringBuilder (); + ReadOnlySpan remainingInput = previousRune.ToString ().AsSpan (); + while (!remainingInput.IsEmpty) { + // Decode + OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); + + if (opStatus == OperationStatus.DestinationTooSmall || opStatus == OperationStatus.InvalidData) { + result = Rune.ReplacementChar; + } - Contents [Row, Col + 1, 0] = System.Text.Rune.ReplacementChar.Value; - Contents [Row, Col + 1, 2] = 1; + newCombo.Append (result); + // Slice and loop again + remainingInput = remainingInput.Slice (charsConsumed); + } + newCombo.Append (rune); + + var combined = newCombo.ToString (); + var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; + Contents [Row, Col - 1, 0] = normalized [0];// BUGBUG: This is wrong, we need to handle the case where the rune is more than one char + Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 2] = 1; + Col--; + } else { + Contents [Row, Col, 0] = rune.Value; + Contents [Row, Col, 1] = CurrentAttribute; + if (Col > 0) { + var left = new Rune (Contents [Row, Col - 1, 0]); + if (left.GetColumnWidth () > 1) { + Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; } - if (runeWidth > 1 && Col == Clip.Right - 1) { - Contents [Row, Col, 0] = System.Text.Rune.ReplacementChar.Value; - } else { - Contents [Row, Col, 0] = (int)(uint)rune; - } - Contents [Row, Col, 1] = CurrentAttribute; - Contents [Row, Col, 2] = 1; - _dirtyLine [Row] = true; } - } - - if (runeWidth < 0 || runeWidth > 0) { - Col++; - } - if (runeWidth > 1) { - if (validLocation && Col < Clip.Right) { + if (runeWidth > 1) { + Col++; + Contents [Row, Col, 0] = Rune.ReplacementChar.Value; Contents [Row, Col, 1] = CurrentAttribute; - Contents [Row, Col, 2] = 0; + Contents [Row, Col, 2] = 1; } - Col++; } } + Col++; + } - public override void End () - { - FakeConsole.ResetColor (); - FakeConsole.Clear (); - } + public override void End () + { + FakeConsole.ResetColor (); + FakeConsole.Clear (); + } - public override Attribute MakeColor (Color foreground, Color background) - { - return new Attribute ( - value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), - foreground: (Color)foreground, - background: (Color)background - ); - } + public override Attribute MakeColor (Color foreground, Color background) + { + return new Attribute ( + value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), + foreground: (Color)foreground, + background: (Color)background + ); + } - public override void Init (Action terminalResized) - { - FakeConsole.MockKeyPresses.Clear (); + public override void Init (Action terminalResized) + { + FakeConsole.MockKeyPresses.Clear (); - TerminalResized = terminalResized; + TerminalResized = terminalResized; - Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; - Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; - FakeConsole.Clear (); - ResizeScreen (); - // Call InitializeColorSchemes before UpdateOffScreen as it references Colors - CurrentAttribute = MakeColor (Color.White, Color.Black); - InitializeColorSchemes (); - UpdateOffScreen (); - } + Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; + Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; + FakeConsole.Clear (); + ResizeScreen (); + // Call InitializeColorSchemes before UpdateOffScreen as it references Colors + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitializeColorSchemes (); + UpdateOffScreen (); + } - int _redrawColor = -1; - void SetColor (int color) - { - _redrawColor = color; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (color & 0xffff)) { - FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff); - } - if (values.Contains ((color >> 16) & 0xffff)) { - FakeConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff); - } + int _redrawColor = -1; + void SetColor (int color) + { + _redrawColor = color; + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains (color & 0xffff)) { + FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff); } + if (values.Contains ((color >> 16) & 0xffff)) { + FakeConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff); + } + } - public override void UpdateScreen () - { - int top = Top; - int left = Left; - int rows = Math.Min (FakeConsole.WindowHeight + top, Rows); - int cols = Cols; - - var savedRow = FakeConsole.CursorTop; - var savedCol = FakeConsole.CursorLeft; - var savedCursorVisible = FakeConsole.CursorVisible; - for (int row = top; row < rows; row++) { - if (!_dirtyLine [row]) { - continue; - } - _dirtyLine [row] = false; - for (int col = left; col < cols; col++) { - FakeConsole.CursorTop = row; - FakeConsole.CursorLeft = col; - for (; col < cols; col++) { - if (Contents [row, col, 2] == 0) { - FakeConsole.CursorLeft++; - continue; - } - - var color = Contents [row, col, 1]; - if (color != _redrawColor) { - SetColor (color); - } - - Rune rune = Contents [row, col, 0]; - if (Rune.DecodeSurrogatePair (rune, out char [] spair)) { - FakeConsole.Write (spair); - } else { - FakeConsole.Write ((char)rune); - } - Contents [row, col, 2] = 0; + public override void UpdateScreen () + { + int top = Top; + int left = Left; + int rows = Math.Min (FakeConsole.WindowHeight + top, Rows); + int cols = Cols; + + var savedRow = FakeConsole.CursorTop; + var savedCol = FakeConsole.CursorLeft; + var savedCursorVisible = FakeConsole.CursorVisible; + for (int row = top; row < rows; row++) { + if (!_dirtyLine [row]) { + continue; + } + _dirtyLine [row] = false; + for (int col = left; col < cols; col++) { + FakeConsole.CursorTop = row; + FakeConsole.CursorLeft = col; + for (; col < cols; col++) { + if (Contents [row, col, 2] == 0) { + FakeConsole.CursorLeft++; + continue; + } + + var color = Contents [row, col, 1]; + if (color != _redrawColor) { + SetColor (color); + } + + var rune = (Rune)Contents [row, col, 0]; + if (rune.Utf16SequenceLength == 1) { + FakeConsole.Write (rune); + } else { + // TODO: Not sure we need to do this. I think we can just write the rune. + + FakeConsole.Write (rune.ToString ()); } + //if (Rune.DecodeSurrogatePair (rune, out char [] spair)) { + // FakeConsole.Write (spair); + //} else { + // FakeConsole.Write ((char)rune); + //} + Contents [row, col, 2] = 0; } } - FakeConsole.CursorTop = savedRow; - FakeConsole.CursorLeft = savedCol; - FakeConsole.CursorVisible = savedCursorVisible; } + FakeConsole.CursorTop = savedRow; + FakeConsole.CursorLeft = savedCol; + FakeConsole.CursorVisible = savedCursorVisible; + } - public override void Refresh () - { - UpdateScreen (); - UpdateCursor (); + public override void Refresh () + { + UpdateScreen (); + UpdateCursor (); + } + + public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) + { + if (consoleKeyInfo.Key != ConsoleKey.Packet) { + return consoleKeyInfo; } - public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) - { - if (consoleKeyInfo.Key != ConsoleKey.Packet) { - return consoleKeyInfo; - } + var mod = consoleKeyInfo.Modifiers; + var shift = (mod & ConsoleModifiers.Shift) != 0; + var alt = (mod & ConsoleModifiers.Alt) != 0; + var control = (mod & ConsoleModifiers.Control) != 0; - var mod = consoleKeyInfo.Modifiers; - var shift = (mod & ConsoleModifiers.Shift) != 0; - var alt = (mod & ConsoleModifiers.Alt) != 0; - var control = (mod & ConsoleModifiers.Control) != 0; + var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _); - var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _); + return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control); + } - return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control); + Key MapKey (ConsoleKeyInfo keyInfo) + { + switch (keyInfo.Key) { + case ConsoleKey.Escape: + return MapKeyModifiers (keyInfo, Key.Esc); + case ConsoleKey.Tab: + return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; + case ConsoleKey.Clear: + return MapKeyModifiers (keyInfo, Key.Clear); + case ConsoleKey.Home: + return MapKeyModifiers (keyInfo, Key.Home); + case ConsoleKey.End: + return MapKeyModifiers (keyInfo, Key.End); + case ConsoleKey.LeftArrow: + return MapKeyModifiers (keyInfo, Key.CursorLeft); + case ConsoleKey.RightArrow: + return MapKeyModifiers (keyInfo, Key.CursorRight); + case ConsoleKey.UpArrow: + return MapKeyModifiers (keyInfo, Key.CursorUp); + case ConsoleKey.DownArrow: + return MapKeyModifiers (keyInfo, Key.CursorDown); + case ConsoleKey.PageUp: + return MapKeyModifiers (keyInfo, Key.PageUp); + case ConsoleKey.PageDown: + return MapKeyModifiers (keyInfo, Key.PageDown); + case ConsoleKey.Enter: + return MapKeyModifiers (keyInfo, Key.Enter); + case ConsoleKey.Spacebar: + return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); + case ConsoleKey.Backspace: + return MapKeyModifiers (keyInfo, Key.Backspace); + case ConsoleKey.Delete: + return MapKeyModifiers (keyInfo, Key.DeleteChar); + case ConsoleKey.Insert: + return MapKeyModifiers (keyInfo, Key.InsertChar); + case ConsoleKey.PrintScreen: + return MapKeyModifiers (keyInfo, Key.PrintScreen); + + case ConsoleKey.Oem1: + case ConsoleKey.Oem2: + case ConsoleKey.Oem3: + case ConsoleKey.Oem4: + case ConsoleKey.Oem5: + case ConsoleKey.Oem6: + case ConsoleKey.Oem7: + case ConsoleKey.Oem8: + case ConsoleKey.Oem102: + case ConsoleKey.OemPeriod: + case ConsoleKey.OemComma: + case ConsoleKey.OemPlus: + case ConsoleKey.OemMinus: + if (keyInfo.KeyChar == 0) { + return Key.Unknown; + } + + return (Key)((uint)keyInfo.KeyChar); } - Key MapKey (ConsoleKeyInfo keyInfo) - { - switch (keyInfo.Key) { - case ConsoleKey.Escape: - return MapKeyModifiers (keyInfo, Key.Esc); - case ConsoleKey.Tab: - return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; - case ConsoleKey.Clear: - return MapKeyModifiers (keyInfo, Key.Clear); - case ConsoleKey.Home: - return MapKeyModifiers (keyInfo, Key.Home); - case ConsoleKey.End: - return MapKeyModifiers (keyInfo, Key.End); - case ConsoleKey.LeftArrow: - return MapKeyModifiers (keyInfo, Key.CursorLeft); - case ConsoleKey.RightArrow: - return MapKeyModifiers (keyInfo, Key.CursorRight); - case ConsoleKey.UpArrow: - return MapKeyModifiers (keyInfo, Key.CursorUp); - case ConsoleKey.DownArrow: - return MapKeyModifiers (keyInfo, Key.CursorDown); - case ConsoleKey.PageUp: - return MapKeyModifiers (keyInfo, Key.PageUp); - case ConsoleKey.PageDown: - return MapKeyModifiers (keyInfo, Key.PageDown); - case ConsoleKey.Enter: - return MapKeyModifiers (keyInfo, Key.Enter); - case ConsoleKey.Spacebar: - return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); - case ConsoleKey.Backspace: - return MapKeyModifiers (keyInfo, Key.Backspace); - case ConsoleKey.Delete: - return MapKeyModifiers (keyInfo, Key.DeleteChar); - case ConsoleKey.Insert: - return MapKeyModifiers (keyInfo, Key.InsertChar); - case ConsoleKey.PrintScreen: - return MapKeyModifiers (keyInfo, Key.PrintScreen); - - case ConsoleKey.Oem1: - case ConsoleKey.Oem2: - case ConsoleKey.Oem3: - case ConsoleKey.Oem4: - case ConsoleKey.Oem5: - case ConsoleKey.Oem6: - case ConsoleKey.Oem7: - case ConsoleKey.Oem8: - case ConsoleKey.Oem102: - case ConsoleKey.OemPeriod: - case ConsoleKey.OemComma: - case ConsoleKey.OemPlus: - case ConsoleKey.OemMinus: + var key = keyInfo.Key; + if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { + var delta = key - ConsoleKey.A; + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { if (keyInfo.KeyChar == 0) { - return Key.Unknown; + return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } else { + return (Key)((uint)keyInfo.KeyChar); } - - return (Key)((uint)keyInfo.KeyChar); } - - var key = keyInfo.Key; - if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { - var delta = key - ConsoleKey.A; - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0) { - return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.A + delta)); - } else { - return (Key)((uint)keyInfo.KeyChar); - } - } - return (Key)((uint)keyInfo.KeyChar); + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { + var delta = key - ConsoleKey.D0; + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); } - if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { - var delta = key - ConsoleKey.D0; - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); - } - } - return (Key)((uint)keyInfo.KeyChar); + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); } - if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { - var delta = key - ConsoleKey.F1; - if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); } - - return (Key)((uint)Key.F1 + delta); } - if (keyInfo.KeyChar != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { + var delta = key - ConsoleKey.F1; + if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); } - return (Key)(0xffffffff); + return (Key)((uint)Key.F1 + delta); + } + if (keyInfo.KeyChar != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); } - KeyModifiers keyModifiers; + return (Key)(0xffffffff); + } - private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) - { - Key keyMod = new Key (); - if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { - keyMod = Key.ShiftMask; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { - keyMod |= Key.CtrlMask; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { - keyMod |= Key.AltMask; - } + KeyModifiers keyModifiers; - return keyMod != Key.Null ? keyMod | key : key; + private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) + { + Key keyMod = new Key (); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { + keyMod = Key.ShiftMask; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { + keyMod |= Key.CtrlMask; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { + keyMod |= Key.AltMask; } - Action _keyDownHandler; - Action _keyHandler; - Action _keyUpHandler; - private CursorVisibility _savedCursorVisibility; + return keyMod != Key.Null ? keyMod | key : key; + } - public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) - { - _keyDownHandler = keyDownHandler; - _keyHandler = keyHandler; - _keyUpHandler = keyUpHandler; + Action _keyDownHandler; + Action _keyHandler; + Action _keyUpHandler; + private CursorVisibility _savedCursorVisibility; - // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called - (mainLoop.Driver as FakeMainLoop).KeyPressed += (consoleKey) => ProcessInput (consoleKey); - } + public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) + { + _keyDownHandler = keyDownHandler; + _keyHandler = keyHandler; + _keyUpHandler = keyUpHandler; - void ProcessInput (ConsoleKeyInfo consoleKey) - { - if (consoleKey.Key == ConsoleKey.Packet) { - consoleKey = FromVKPacketToKConsoleKeyInfo (consoleKey); - } - keyModifiers = new KeyModifiers (); - if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Shift)) { - keyModifiers.Shift = true; - } - if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Alt)) { - keyModifiers.Alt = true; - } - if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Control)) { - keyModifiers.Ctrl = true; - } - var map = MapKey (consoleKey); - if (map == (Key)0xffffffff) { - if ((consoleKey.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - _keyDownHandler (new KeyEvent (map, keyModifiers)); - _keyUpHandler (new KeyEvent (map, keyModifiers)); - } - return; - } + // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called + (mainLoop.Driver as FakeMainLoop).KeyPressed += (consoleKey) => ProcessInput (consoleKey); + } - _keyDownHandler (new KeyEvent (map, keyModifiers)); - _keyHandler (new KeyEvent (map, keyModifiers)); - _keyUpHandler (new KeyEvent (map, keyModifiers)); + void ProcessInput (ConsoleKeyInfo consoleKey) + { + if (consoleKey.Key == ConsoleKey.Packet) { + consoleKey = FromVKPacketToKConsoleKeyInfo (consoleKey); + } + keyModifiers = new KeyModifiers (); + if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Shift)) { + keyModifiers.Shift = true; + } + if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Alt)) { + keyModifiers.Alt = true; + } + if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Control)) { + keyModifiers.Ctrl = true; + } + var map = MapKey (consoleKey); + if (map == (Key)0xffffffff) { + if ((consoleKey.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + _keyDownHandler (new KeyEvent (map, keyModifiers)); + _keyUpHandler (new KeyEvent (map, keyModifiers)); + } + return; } - /// - public override bool GetCursorVisibility (out CursorVisibility visibility) - { - visibility = FakeConsole.CursorVisible - ? CursorVisibility.Default - : CursorVisibility.Invisible; + _keyDownHandler (new KeyEvent (map, keyModifiers)); + _keyHandler (new KeyEvent (map, keyModifiers)); + _keyUpHandler (new KeyEvent (map, keyModifiers)); + } - return FakeConsole.CursorVisible; - } + /// + public override bool GetCursorVisibility (out CursorVisibility visibility) + { + visibility = FakeConsole.CursorVisible + ? CursorVisibility.Default + : CursorVisibility.Invisible; - /// - public override bool SetCursorVisibility (CursorVisibility visibility) - { - _savedCursorVisibility = visibility; - return FakeConsole.CursorVisible = visibility == CursorVisibility.Default; - } + return FakeConsole.CursorVisible; + } - /// - public override bool EnsureCursorVisibility () - { - if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { - GetCursorVisibility (out CursorVisibility cursorVisibility); - _savedCursorVisibility = cursorVisibility; - SetCursorVisibility (CursorVisibility.Invisible); - return false; - } + /// + public override bool SetCursorVisibility (CursorVisibility visibility) + { + _savedCursorVisibility = visibility; + return FakeConsole.CursorVisible = visibility == CursorVisibility.Default; + } - SetCursorVisibility (_savedCursorVisibility); - return FakeConsole.CursorVisible; + /// + public override bool EnsureCursorVisibility () + { + if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { + GetCursorVisibility (out CursorVisibility cursorVisibility); + _savedCursorVisibility = cursorVisibility; + SetCursorVisibility (CursorVisibility.Invisible); + return false; } - public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) - { - ProcessInput (new ConsoleKeyInfo (keyChar, key, shift, alt, control)); - } + SetCursorVisibility (_savedCursorVisibility); + return FakeConsole.CursorVisible; + } - public void SetBufferSize (int width, int height) - { - FakeConsole.SetBufferSize (width, height); - Cols = width; - Rows = height; - if (!EnableConsoleScrolling) { - SetWindowSize (width, height); - } - ProcessResize (); - } + public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) + { + ProcessInput (new ConsoleKeyInfo (keyChar, key, shift, alt, control)); + } - public void SetWindowSize (int width, int height) - { - FakeConsole.SetWindowSize (width, height); - if (!EnableConsoleScrolling) { - if (width != Cols || height != Rows) { - SetBufferSize (width, height); - Cols = width; - Rows = height; - } - } - ProcessResize (); + public void SetBufferSize (int width, int height) + { + FakeConsole.SetBufferSize (width, height); + Cols = width; + Rows = height; + if (!EnableConsoleScrolling) { + SetWindowSize (width, height); } + ProcessResize (); + } - public void SetWindowPosition (int left, int top) - { - if (EnableConsoleScrolling) { - Left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); - Top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); - } else if (Left > 0 || Top > 0) { - Left = 0; - Top = 0; + public void SetWindowSize (int width, int height) + { + FakeConsole.SetWindowSize (width, height); + if (!EnableConsoleScrolling) { + if (width != Cols || height != Rows) { + SetBufferSize (width, height); + Cols = width; + Rows = height; } - FakeConsole.SetWindowPosition (Left, Top); } + ProcessResize (); + } - void ProcessResize () - { - ResizeScreen (); - UpdateOffScreen (); - TerminalResized?.Invoke (); + public void SetWindowPosition (int left, int top) + { + if (EnableConsoleScrolling) { + Left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); + Top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); + } else if (Left > 0 || Top > 0) { + Left = 0; + Top = 0; } + FakeConsole.SetWindowPosition (Left, Top); + } - public virtual void ResizeScreen () - { - if (!EnableConsoleScrolling) { - if (FakeConsole.WindowHeight > 0) { - // Can raise an exception while is still resizing. - try { -#pragma warning disable CA1416 - FakeConsole.CursorTop = 0; - FakeConsole.CursorLeft = 0; - FakeConsole.WindowTop = 0; - FakeConsole.WindowLeft = 0; -#pragma warning restore CA1416 - } catch (System.IO.IOException) { - return; - } catch (ArgumentOutOfRangeException) { - return; - } - } - } else { + void ProcessResize () + { + ResizeScreen (); + UpdateOffScreen (); + TerminalResized?.Invoke (); + } + + public virtual void ResizeScreen () + { + if (!EnableConsoleScrolling) { + if (FakeConsole.WindowHeight > 0) { + // Can raise an exception while is still resizing. try { #pragma warning disable CA1416 - FakeConsole.WindowLeft = Math.Max (Math.Min (Left, Cols - FakeConsole.WindowWidth), 0); - FakeConsole.WindowTop = Math.Max (Math.Min (Top, Rows - FakeConsole.WindowHeight), 0); + FakeConsole.CursorTop = 0; + FakeConsole.CursorLeft = 0; + FakeConsole.WindowTop = 0; + FakeConsole.WindowLeft = 0; #pragma warning restore CA1416 - } catch (Exception) { + } catch (System.IO.IOException) { + return; + } catch (ArgumentOutOfRangeException) { return; } } - - Clip = new Rect (0, 0, Cols, Rows); + } else { + try { +#pragma warning disable CA1416 + FakeConsole.WindowLeft = Math.Max (Math.Min (Left, Cols - FakeConsole.WindowWidth), 0); + FakeConsole.WindowTop = Math.Max (Math.Min (Top, Rows - FakeConsole.WindowHeight), 0); +#pragma warning restore CA1416 + } catch (Exception) { + return; + } } - public override void UpdateOffScreen () - { - Contents = new int [Rows, Cols, 3]; - _dirtyLine = new bool [Rows]; + Clip = new Rect (0, 0, Cols, Rows); + } - // Can raise an exception while is still resizing. - try { - for (int row = 0; row < Rows; row++) { - for (int c = 0; c < Cols; c++) { - Contents [row, c, 0] = ' '; - Contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; - Contents [row, c, 2] = 0; - _dirtyLine [row] = true; - } + public override void UpdateOffScreen () + { + Contents = new int [Rows, Cols, 3]; + _dirtyLine = new bool [Rows]; + + // Can raise an exception while is still resizing. + try { + for (int row = 0; row < Rows; row++) { + for (int c = 0; c < Cols; c++) { + Contents [row, c, 0] = ' '; + Contents [row, c, 1] = 0; + Contents [row, c, 2] = 0; + _dirtyLine [row] = true; } - } catch (IndexOutOfRangeException) { } - Clip = new Rect (0, 0, Cols, Rows); - } - - public override bool GetColors (int value, out Color foreground, out Color background) - { - bool hasColor = false; - foreground = default; - background = default; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (value & 0xffff)) { - hasColor = true; - background = (Color)(ConsoleColor)(value & 0xffff); - } - if (values.Contains ((value >> 16) & 0xffff)) { - hasColor = true; - foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff); } - return hasColor; + } catch (IndexOutOfRangeException) { } + Clip = new Rect (0, 0, Cols, Rows); + } + + public override bool GetColors (int value, out Color foreground, out Color background) + { + bool hasColor = false; + foreground = default; + background = default; + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains (value & 0xffff)) { + hasColor = true; + background = (Color)(ConsoleColor)(value & 0xffff); } + if (values.Contains ((value >> 16) & 0xffff)) { + hasColor = true; + foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff); + } + return hasColor; + } - public override void UpdateCursor () - { - if (!EnsureCursorVisibility ()) { - return; - } + public override void UpdateCursor () + { + if (!EnsureCursorVisibility ()) { + return; + } - // Prevents the exception of size changing during resizing. - try { - // BUGBUG: Why is this using BufferWidth/Height and now Cols/Rows? - if (Col >= 0 && Col < FakeConsole.BufferWidth && Row >= 0 && Row < FakeConsole.BufferHeight) { - FakeConsole.SetCursorPosition (Col, Row); - } - } catch (System.IO.IOException) { - } catch (ArgumentOutOfRangeException) { + // Prevents the exception of size changing during resizing. + try { + // BUGBUG: Why is this using BufferWidth/Height and now Cols/Rows? + if (Col >= 0 && Col < FakeConsole.BufferWidth && Row >= 0 && Row < FakeConsole.BufferHeight) { + FakeConsole.SetCursorPosition (Col, Row); } + } catch (System.IO.IOException) { + } catch (ArgumentOutOfRangeException) { } + } - #region Not Implemented - public override void Suspend () - { - throw new NotImplementedException (); - } - #endregion + #region Not Implemented + public override void Suspend () + { + throw new NotImplementedException (); + } + #endregion - public class FakeClipboard : ClipboardBase { - public Exception FakeException = null; + public class FakeClipboard : ClipboardBase { + public Exception FakeException = null; - string Contents = string.Empty; + string Contents = string.Empty; - bool _isSupportedAlwaysFalse = false; + bool _isSupportedAlwaysFalse = false; - public override bool IsSupported => !_isSupportedAlwaysFalse; + public override bool IsSupported => !_isSupportedAlwaysFalse; - public FakeClipboard (bool fakeClipboardThrowsNotSupportedException = false, bool isSupportedAlwaysFalse = false) - { - _isSupportedAlwaysFalse = isSupportedAlwaysFalse; - if (fakeClipboardThrowsNotSupportedException) { - FakeException = new NotSupportedException ("Fake clipboard exception"); - } + public FakeClipboard (bool fakeClipboardThrowsNotSupportedException = false, bool isSupportedAlwaysFalse = false) + { + _isSupportedAlwaysFalse = isSupportedAlwaysFalse; + if (fakeClipboardThrowsNotSupportedException) { + FakeException = new NotSupportedException ("Fake clipboard exception"); } + } - protected override string GetClipboardDataImpl () - { - if (FakeException != null) { - throw FakeException; - } - return Contents; + protected override string GetClipboardDataImpl () + { + if (FakeException != null) { + throw FakeException; } + return Contents; + } - protected override void SetClipboardDataImpl (string text) - { - if (FakeException != null) { - throw FakeException; - } - Contents = text; + protected override void SetClipboardDataImpl (string text) + { + if (FakeException != null) { + throw FakeException; } + Contents = text; } + } #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member - } } \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 4655918222..dd4220fa8b 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -11,735 +11,739 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using NStack; - -namespace Terminal.Gui { - internal class NetWinVTConsole { - IntPtr _inputHandle, _outputHandle, _errorHandle; - uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode; - - public NetWinVTConsole () - { - _inputHandle = GetStdHandle (STD_INPUT_HANDLE); - if (!GetConsoleMode (_inputHandle, out uint mode)) { - throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}."); - } - _originalInputConsoleMode = mode; - if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) { - mode |= ENABLE_VIRTUAL_TERMINAL_INPUT; - if (!SetConsoleMode (_inputHandle, mode)) { - throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}."); - } - } +using Rune = System.Text.Rune; - _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); - if (!GetConsoleMode (_outputHandle, out mode)) { - throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}."); - } - _originalOutputConsoleMode = mode; - if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; - if (!SetConsoleMode (_outputHandle, mode)) { - throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}."); - } - } +namespace Terminal.Gui; +internal class NetWinVTConsole { + IntPtr _inputHandle, _outputHandle, _errorHandle; + uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode; - _errorHandle = GetStdHandle (STD_ERROR_HANDLE); - if (!GetConsoleMode (_errorHandle, out mode)) { - throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}."); - } - _originalErrorConsoleMode = mode; - if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { - mode |= DISABLE_NEWLINE_AUTO_RETURN; - if (!SetConsoleMode (_errorHandle, mode)) { - throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}."); - } + public NetWinVTConsole () + { + _inputHandle = GetStdHandle (STD_INPUT_HANDLE); + if (!GetConsoleMode (_inputHandle, out uint mode)) { + throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}."); + } + _originalInputConsoleMode = mode; + if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) { + mode |= ENABLE_VIRTUAL_TERMINAL_INPUT; + if (!SetConsoleMode (_inputHandle, mode)) { + throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}."); } } - public void Cleanup () - { - if (!SetConsoleMode (_inputHandle, _originalInputConsoleMode)) { - throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}."); - } - if (!SetConsoleMode (_outputHandle, _originalOutputConsoleMode)) { - throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}."); + _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); + if (!GetConsoleMode (_outputHandle, out mode)) { + throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}."); + } + _originalOutputConsoleMode = mode; + if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; + if (!SetConsoleMode (_outputHandle, mode)) { + throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}."); } - if (!SetConsoleMode (_errorHandle, _originalErrorConsoleMode)) { - throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}."); + } + + _errorHandle = GetStdHandle (STD_ERROR_HANDLE); + if (!GetConsoleMode (_errorHandle, out mode)) { + throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}."); + } + _originalErrorConsoleMode = mode; + if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { + mode |= DISABLE_NEWLINE_AUTO_RETURN; + if (!SetConsoleMode (_errorHandle, mode)) { + throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}."); } } + } - const int STD_INPUT_HANDLE = -10; - const int STD_OUTPUT_HANDLE = -11; - const int STD_ERROR_HANDLE = -12; - - // Input modes. - const uint ENABLE_PROCESSED_INPUT = 1; - const uint ENABLE_LINE_INPUT = 2; - const uint ENABLE_ECHO_INPUT = 4; - const uint ENABLE_WINDOW_INPUT = 8; - const uint ENABLE_MOUSE_INPUT = 16; - const uint ENABLE_INSERT_MODE = 32; - const uint ENABLE_QUICK_EDIT_MODE = 64; - const uint ENABLE_EXTENDED_FLAGS = 128; - const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512; - - // Output modes. - const uint ENABLE_PROCESSED_OUTPUT = 1; - const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2; - const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4; - const uint DISABLE_NEWLINE_AUTO_RETURN = 8; - const uint ENABLE_LVB_GRID_WORLDWIDE = 10; - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern IntPtr GetStdHandle (int nStdHandle); - - [DllImport ("kernel32.dll")] - static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode); - - [DllImport ("kernel32.dll")] - static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode); - - [DllImport ("kernel32.dll")] - static extern uint GetLastError (); - } - - internal class NetEvents { - ManualResetEventSlim _inputReady = new ManualResetEventSlim (false); - ManualResetEventSlim _waitForStart = new ManualResetEventSlim (false); - ManualResetEventSlim _winChange = new ManualResetEventSlim (false); - Queue _inputResultQueue = new Queue (); - ConsoleDriver _consoleDriver; - volatile ConsoleKeyInfo [] _cki = null; - static volatile bool _isEscSeq; - int _lastWindowHeight; - bool _stopTasks; + public void Cleanup () + { + if (!SetConsoleMode (_inputHandle, _originalInputConsoleMode)) { + throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}."); + } + if (!SetConsoleMode (_outputHandle, _originalOutputConsoleMode)) { + throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}."); + } + if (!SetConsoleMode (_errorHandle, _originalErrorConsoleMode)) { + throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}."); + } + } + + const int STD_INPUT_HANDLE = -10; + const int STD_OUTPUT_HANDLE = -11; + const int STD_ERROR_HANDLE = -12; + + // Input modes. + const uint ENABLE_PROCESSED_INPUT = 1; + const uint ENABLE_LINE_INPUT = 2; + const uint ENABLE_ECHO_INPUT = 4; + const uint ENABLE_WINDOW_INPUT = 8; + const uint ENABLE_MOUSE_INPUT = 16; + const uint ENABLE_INSERT_MODE = 32; + const uint ENABLE_QUICK_EDIT_MODE = 64; + const uint ENABLE_EXTENDED_FLAGS = 128; + const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512; + + // Output modes. + const uint ENABLE_PROCESSED_OUTPUT = 1; + const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2; + const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4; + const uint DISABLE_NEWLINE_AUTO_RETURN = 8; + const uint ENABLE_LVB_GRID_WORLDWIDE = 10; + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern IntPtr GetStdHandle (int nStdHandle); + + [DllImport ("kernel32.dll")] + static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode); + + [DllImport ("kernel32.dll")] + static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode); + + [DllImport ("kernel32.dll")] + static extern uint GetLastError (); +} + +internal class NetEvents { + ManualResetEventSlim _inputReady = new ManualResetEventSlim (false); + ManualResetEventSlim _waitForStart = new ManualResetEventSlim (false); + ManualResetEventSlim _winChange = new ManualResetEventSlim (false); + Queue _inputResultQueue = new Queue (); + ConsoleDriver _consoleDriver; + volatile ConsoleKeyInfo [] _cki = null; + static volatile bool _isEscSeq; + int _lastWindowHeight; + bool _stopTasks; #if PROCESS_REQUEST bool _neededProcessRequest; #endif - public bool IsTerminalWithOptions { get; set; } - public EscSeqReqProc EscSeqReqProc { get; } = new EscSeqReqProc (); + public bool IsTerminalWithOptions { get; set; } + public EscSeqReqProc EscSeqReqProc { get; } = new EscSeqReqProc (); - public NetEvents (ConsoleDriver consoleDriver) - { - if (consoleDriver == null) { - throw new ArgumentNullException ("Console driver instance must be provided."); - } - _consoleDriver = consoleDriver; - Task.Run (ProcessInputResultQueue); - Task.Run (CheckWinChange); + public NetEvents (ConsoleDriver consoleDriver) + { + if (consoleDriver == null) { + throw new ArgumentNullException ("Console driver instance must be provided."); } + _consoleDriver = consoleDriver; + Task.Run (ProcessInputResultQueue); + Task.Run (CheckWinChange); + } - internal void StopTasks () - { - _stopTasks = true; - } + internal void StopTasks () + { + _stopTasks = true; + } - public InputResult? ReadConsoleInput () - { - while (true) { - if (_stopTasks) { - return null; - } - _waitForStart.Set (); - _winChange.Set (); + public InputResult? ReadConsoleInput () + { + while (true) { + if (_stopTasks) { + return null; + } + _waitForStart.Set (); + _winChange.Set (); - if (_inputResultQueue.Count == 0) { - _inputReady.Wait (); - _inputReady.Reset (); - } + if (_inputResultQueue.Count == 0) { + _inputReady.Wait (); + _inputReady.Reset (); + } #if PROCESS_REQUEST _neededProcessRequest = false; #endif - if (_inputResultQueue.Count > 0) { - return _inputResultQueue.Dequeue (); - } + if (_inputResultQueue.Count > 0) { + return _inputResultQueue.Dequeue (); } } + } - void ProcessInputResultQueue () - { - while (true) { - _waitForStart.Wait (); - _waitForStart.Reset (); - - if (_inputResultQueue.Count == 0) { - GetConsoleKey (); - } + void ProcessInputResultQueue () + { + while (true) { + _waitForStart.Wait (); + _waitForStart.Reset (); - _inputReady.Set (); + if (_inputResultQueue.Count == 0) { + GetConsoleKey (); } - } - void GetConsoleKey () - { - ConsoleKey key = 0; - ConsoleModifiers mod = 0; - ConsoleKeyInfo newConsoleKeyInfo = default; + _inputReady.Set (); + } + } - while (true) { - ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true); - if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq) - || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) { - if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) { - _cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, - false, false, false), _cki); - } - _isEscSeq = true; - newConsoleKeyInfo = consoleKeyInfo; - _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki); - if (!Console.KeyAvailable) { - DecodeEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); - _cki = null; - _isEscSeq = false; - break; - } - } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq) { + void GetConsoleKey () + { + ConsoleKey key = 0; + ConsoleModifiers mod = 0; + ConsoleKeyInfo newConsoleKeyInfo = default; + + while (true) { + ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true); + if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq) + || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) { + if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) { + _cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, + false, false, false), _cki); + } + _isEscSeq = true; + newConsoleKeyInfo = consoleKeyInfo; + _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki); + if (!Console.KeyAvailable) { DecodeEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); _cki = null; - break; - } else { - GetConsoleInputType (consoleKeyInfo); + _isEscSeq = false; break; } + } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq) { + DecodeEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; + break; + } else { + GetConsoleInputType (consoleKeyInfo); + break; } } + } - void CheckWinChange () - { - while (true) { - if (_stopTasks) { - return; - } - _winChange.Wait (); - _winChange.Reset (); - WaitWinChange (); - _inputReady.Set (); + void CheckWinChange () + { + while (true) { + if (_stopTasks) { + return; } + _winChange.Wait (); + _winChange.Reset (); + WaitWinChange (); + _inputReady.Set (); } + } - void WaitWinChange () - { - while (true) { - // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. - Thread.Sleep (10); - if (_stopTasks) { - return; + void WaitWinChange () + { + while (true) { + // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. + Thread.Sleep (10); + if (_stopTasks) { + return; + } + switch (IsTerminalWithOptions) { + case false: + int buffHeight, buffWidth; + if (((NetDriver)_consoleDriver).IsWinPlatform) { + buffHeight = Math.Max (Console.BufferHeight, 0); + buffWidth = Math.Max (Console.BufferWidth, 0); + } else { + buffHeight = _consoleDriver.Rows; + buffWidth = _consoleDriver.Cols; } - switch (IsTerminalWithOptions) { - case false: - int buffHeight, buffWidth; - if (((NetDriver)_consoleDriver).IsWinPlatform) { - buffHeight = Math.Max (Console.BufferHeight, 0); - buffWidth = Math.Max (Console.BufferWidth, 0); - } else { - buffHeight = _consoleDriver.Rows; - buffWidth = _consoleDriver.Cols; - } - if (IsWinChanged ( - Math.Max (Console.WindowHeight, 0), - Math.Max (Console.WindowWidth, 0), - buffHeight, - buffWidth)) { + if (IsWinChanged ( + Math.Max (Console.WindowHeight, 0), + Math.Max (Console.WindowWidth, 0), + buffHeight, + buffWidth)) { - return; - } - break; - case true: - //Request the size of the text area in characters. - EscSeqReqProc.Add ("t"); - Console.Out.Write ("\x1b[18t"); - break; + return; } + break; + case true: + //Request the size of the text area in characters. + EscSeqReqProc.Add ("t"); + Console.Out.Write ("\x1b[18t"); + break; } } + } - bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth) - { - if (!_consoleDriver.EnableConsoleScrolling) { - if (winWidth != _consoleDriver.Cols || winHeight != _consoleDriver.Rows) { - var w = Math.Max (winWidth, 0); - var h = Math.Max (winHeight, 0); - GetWindowSizeEvent (new Size (w, h)); - return true; - } - } else { - if (winWidth != _consoleDriver.Cols || winHeight != _lastWindowHeight - || buffWidth != _consoleDriver.Cols || buffHeight != _consoleDriver.Rows) { + bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth) + { + if (!_consoleDriver.EnableConsoleScrolling) { + if (winWidth != _consoleDriver.Cols || winHeight != _consoleDriver.Rows) { + var w = Math.Max (winWidth, 0); + var h = Math.Max (winHeight, 0); + GetWindowSizeEvent (new Size (w, h)); + return true; + } + } else { + if (winWidth != _consoleDriver.Cols || winHeight != _lastWindowHeight + || buffWidth != _consoleDriver.Cols || buffHeight != _consoleDriver.Rows) { - _lastWindowHeight = Math.Max (winHeight, 0); - GetWindowSizeEvent (new Size (winWidth, _lastWindowHeight)); - return true; - } + _lastWindowHeight = Math.Max (winHeight, 0); + GetWindowSizeEvent (new Size (winWidth, _lastWindowHeight)); + return true; } - return false; } + return false; + } - void GetWindowSizeEvent (Size size) - { - WindowSizeEvent windowSizeEvent = new WindowSizeEvent () { - Size = size - }; - - _inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.WindowSize, - WindowSizeEvent = windowSizeEvent - }); - } + void GetWindowSizeEvent (Size size) + { + WindowSizeEvent windowSizeEvent = new WindowSizeEvent () { + Size = size + }; - void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) - { - InputResult inputResult = new InputResult { - EventType = EventType.Key - }; - MouseEvent mouseEvent = new MouseEvent (); - ConsoleKeyInfo newConsoleKeyInfo = EscSeqUtils.GetConsoleInputKey (consoleKeyInfo); - if (inputResult.EventType == EventType.Key) { - inputResult.ConsoleKeyInfo = newConsoleKeyInfo; - } else { - inputResult.MouseEvent = mouseEvent; - } + _inputResultQueue.Enqueue (new InputResult () { + EventType = EventType.WindowSize, + WindowSizeEvent = windowSizeEvent + }); + } - _inputResultQueue.Enqueue (inputResult); + void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) + { + InputResult inputResult = new InputResult { + EventType = EventType.Key + }; + MouseEvent mouseEvent = new MouseEvent (); + ConsoleKeyInfo newConsoleKeyInfo = EscSeqUtils.GetConsoleInputKey (consoleKeyInfo); + if (inputResult.EventType == EventType.Key) { + inputResult.ConsoleKeyInfo = newConsoleKeyInfo; + } else { + inputResult.MouseEvent = mouseEvent; } - void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) - { - string c1Control, code, terminating; - string [] values; - // isKeyMouse is true if it's CSI<, false otherwise - bool isKeyMouse; - bool isReq; - List mouseFlags; - Point pos; - EscSeqUtils.DecodeEscSeq (EscSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + _inputResultQueue.Enqueue (inputResult); + } - if (isKeyMouse) { - foreach (var mf in mouseFlags) { - GetMouseEvent (MapMouseFlags (mf), pos); - } - return; - } else if (isReq) { - GetRequestEvent (c1Control, code, values, terminating); - return; - } - InputResult inputResult = new InputResult { - EventType = EventType.Key, - ConsoleKeyInfo = newConsoleKeyInfo - }; + void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) + { + string c1Control, code, terminating; + string [] values; + // isKeyMouse is true if it's CSI<, false otherwise + bool isKeyMouse; + bool isReq; + List mouseFlags; + Point pos; + EscSeqUtils.DecodeEscSeq (EscSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + + if (isKeyMouse) { + foreach (var mf in mouseFlags) { + GetMouseEvent (MapMouseFlags (mf), pos); + } + return; + } else if (isReq) { + GetRequestEvent (c1Control, code, values, terminating); + return; + } + InputResult inputResult = new InputResult { + EventType = EventType.Key, + ConsoleKeyInfo = newConsoleKeyInfo + }; + + _inputResultQueue.Enqueue (inputResult); + } - _inputResultQueue.Enqueue (inputResult); - } - - void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) - { - GetMouseEvent (MapMouseFlags (mouseFlag), pos); - } - - MouseButtonState MapMouseFlags (MouseFlags mouseFlags) - { - MouseButtonState mbs = default; - foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) { - if (mouseFlags.HasFlag ((MouseFlags)flag)) { - switch (flag) { - case MouseFlags.Button1Pressed: - mbs |= MouseButtonState.Button1Pressed; - break; - case MouseFlags.Button1Released: - mbs |= MouseButtonState.Button1Released; - break; - case MouseFlags.Button1Clicked: - mbs |= MouseButtonState.Button1Clicked; - break; - case MouseFlags.Button1DoubleClicked: - mbs |= MouseButtonState.Button1DoubleClicked; - break; - case MouseFlags.Button1TripleClicked: - mbs |= MouseButtonState.Button1TripleClicked; - break; - case MouseFlags.Button2Pressed: - mbs |= MouseButtonState.Button2Pressed; - break; - case MouseFlags.Button2Released: - mbs |= MouseButtonState.Button2Released; - break; - case MouseFlags.Button2Clicked: - mbs |= MouseButtonState.Button2Clicked; - break; - case MouseFlags.Button2DoubleClicked: - mbs |= MouseButtonState.Button2DoubleClicked; - break; - case MouseFlags.Button2TripleClicked: - mbs |= MouseButtonState.Button2TripleClicked; - break; - case MouseFlags.Button3Pressed: - mbs |= MouseButtonState.Button3Pressed; - break; - case MouseFlags.Button3Released: - mbs |= MouseButtonState.Button3Released; - break; - case MouseFlags.Button3Clicked: - mbs |= MouseButtonState.Button3Clicked; - break; - case MouseFlags.Button3DoubleClicked: - mbs |= MouseButtonState.Button3DoubleClicked; - break; - case MouseFlags.Button3TripleClicked: - mbs |= MouseButtonState.Button3TripleClicked; - break; - case MouseFlags.WheeledUp: - mbs |= MouseButtonState.ButtonWheeledUp; - break; - case MouseFlags.WheeledDown: - mbs |= MouseButtonState.ButtonWheeledDown; - break; - case MouseFlags.WheeledLeft: - mbs |= MouseButtonState.ButtonWheeledLeft; - break; - case MouseFlags.WheeledRight: - mbs |= MouseButtonState.ButtonWheeledRight; - break; - case MouseFlags.Button4Pressed: - mbs |= MouseButtonState.Button4Pressed; - break; - case MouseFlags.Button4Released: - mbs |= MouseButtonState.Button4Released; - break; - case MouseFlags.Button4Clicked: - mbs |= MouseButtonState.Button4Clicked; - break; - case MouseFlags.Button4DoubleClicked: - mbs |= MouseButtonState.Button4DoubleClicked; - break; - case MouseFlags.Button4TripleClicked: - mbs |= MouseButtonState.Button4TripleClicked; - break; - case MouseFlags.ButtonShift: - mbs |= MouseButtonState.ButtonShift; - break; - case MouseFlags.ButtonCtrl: - mbs |= MouseButtonState.ButtonCtrl; - break; - case MouseFlags.ButtonAlt: - mbs |= MouseButtonState.ButtonAlt; - break; - case MouseFlags.ReportMousePosition: - mbs |= MouseButtonState.ReportMousePosition; - break; - case MouseFlags.AllEvents: - mbs |= MouseButtonState.AllEvents; - break; - } + void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) + { + GetMouseEvent (MapMouseFlags (mouseFlag), pos); + } + + MouseButtonState MapMouseFlags (MouseFlags mouseFlags) + { + MouseButtonState mbs = default; + foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) { + if (mouseFlags.HasFlag ((MouseFlags)flag)) { + switch (flag) { + case MouseFlags.Button1Pressed: + mbs |= MouseButtonState.Button1Pressed; + break; + case MouseFlags.Button1Released: + mbs |= MouseButtonState.Button1Released; + break; + case MouseFlags.Button1Clicked: + mbs |= MouseButtonState.Button1Clicked; + break; + case MouseFlags.Button1DoubleClicked: + mbs |= MouseButtonState.Button1DoubleClicked; + break; + case MouseFlags.Button1TripleClicked: + mbs |= MouseButtonState.Button1TripleClicked; + break; + case MouseFlags.Button2Pressed: + mbs |= MouseButtonState.Button2Pressed; + break; + case MouseFlags.Button2Released: + mbs |= MouseButtonState.Button2Released; + break; + case MouseFlags.Button2Clicked: + mbs |= MouseButtonState.Button2Clicked; + break; + case MouseFlags.Button2DoubleClicked: + mbs |= MouseButtonState.Button2DoubleClicked; + break; + case MouseFlags.Button2TripleClicked: + mbs |= MouseButtonState.Button2TripleClicked; + break; + case MouseFlags.Button3Pressed: + mbs |= MouseButtonState.Button3Pressed; + break; + case MouseFlags.Button3Released: + mbs |= MouseButtonState.Button3Released; + break; + case MouseFlags.Button3Clicked: + mbs |= MouseButtonState.Button3Clicked; + break; + case MouseFlags.Button3DoubleClicked: + mbs |= MouseButtonState.Button3DoubleClicked; + break; + case MouseFlags.Button3TripleClicked: + mbs |= MouseButtonState.Button3TripleClicked; + break; + case MouseFlags.WheeledUp: + mbs |= MouseButtonState.ButtonWheeledUp; + break; + case MouseFlags.WheeledDown: + mbs |= MouseButtonState.ButtonWheeledDown; + break; + case MouseFlags.WheeledLeft: + mbs |= MouseButtonState.ButtonWheeledLeft; + break; + case MouseFlags.WheeledRight: + mbs |= MouseButtonState.ButtonWheeledRight; + break; + case MouseFlags.Button4Pressed: + mbs |= MouseButtonState.Button4Pressed; + break; + case MouseFlags.Button4Released: + mbs |= MouseButtonState.Button4Released; + break; + case MouseFlags.Button4Clicked: + mbs |= MouseButtonState.Button4Clicked; + break; + case MouseFlags.Button4DoubleClicked: + mbs |= MouseButtonState.Button4DoubleClicked; + break; + case MouseFlags.Button4TripleClicked: + mbs |= MouseButtonState.Button4TripleClicked; + break; + case MouseFlags.ButtonShift: + mbs |= MouseButtonState.ButtonShift; + break; + case MouseFlags.ButtonCtrl: + mbs |= MouseButtonState.ButtonCtrl; + break; + case MouseFlags.ButtonAlt: + mbs |= MouseButtonState.ButtonAlt; + break; + case MouseFlags.ReportMousePosition: + mbs |= MouseButtonState.ReportMousePosition; + break; + case MouseFlags.AllEvents: + mbs |= MouseButtonState.AllEvents; + break; } } - return mbs; } + return mbs; + } - Point _lastCursorPosition; + Point _lastCursorPosition; - void GetRequestEvent (string c1Control, string code, string [] values, string terminating) - { - EventType eventType = new EventType (); - switch (terminating) { - case "R": // Reports cursor position as CSI r ; c R - Point point = new Point { - X = int.Parse (values [1]) - 1, - Y = int.Parse (values [0]) - 1 + void GetRequestEvent (string c1Control, string code, string [] values, string terminating) + { + EventType eventType = new EventType (); + switch (terminating) { + case "R": // Reports cursor position as CSI r ; c R + Point point = new Point { + X = int.Parse (values [1]) - 1, + Y = int.Parse (values [0]) - 1 + }; + if (_lastCursorPosition.Y != point.Y) { + _lastCursorPosition = point; + eventType = EventType.WindowPosition; + var winPositionEv = new WindowPositionEvent () { + CursorPosition = point }; - if (_lastCursorPosition.Y != point.Y) { - _lastCursorPosition = point; - eventType = EventType.WindowPosition; - var winPositionEv = new WindowPositionEvent () { - CursorPosition = point - }; - _inputResultQueue.Enqueue (new InputResult () { - EventType = eventType, - WindowPositionEvent = winPositionEv - }); - } else { - return; - } - break; - case "c": - try { - var parent = EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ()); - if (parent == null) { Debug.WriteLine ("Not supported!"); } - } catch (Exception ex) { - Debug.WriteLine (ex.Message); - } + _inputResultQueue.Enqueue (new InputResult () { + EventType = eventType, + WindowPositionEvent = winPositionEv + }); + } else { + return; + } + break; + case "c": + try { + var parent = EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ()); + if (parent == null) { Debug.WriteLine ("Not supported!"); } + } catch (Exception ex) { + Debug.WriteLine (ex.Message); + } - if (c1Control == "CSI" && values.Length == 2 - && values [0] == "1" && values [1] == "0") { - // Reports CSI?1;0c ("VT101 with No Options") - IsTerminalWithOptions = false; - } else { - IsTerminalWithOptions = true; - } - break; - case "t": - switch (values [0]) { - case "8": - IsWinChanged ( - Math.Max (int.Parse (values [1]), 0), - Math.Max (int.Parse (values [2]), 0), - Math.Max (int.Parse (values [1]), 0), - Math.Max (int.Parse (values [2]), 0)); - break; - default: - SetRequestedEvent (c1Control, code, values, terminating); - break; - } + if (c1Control == "CSI" && values.Length == 2 + && values [0] == "1" && values [1] == "0") { + // Reports CSI?1;0c ("VT101 with No Options") + IsTerminalWithOptions = false; + } else { + IsTerminalWithOptions = true; + } + break; + case "t": + switch (values [0]) { + case "8": + IsWinChanged ( + Math.Max (int.Parse (values [1]), 0), + Math.Max (int.Parse (values [2]), 0), + Math.Max (int.Parse (values [1]), 0), + Math.Max (int.Parse (values [2]), 0)); break; default: SetRequestedEvent (c1Control, code, values, terminating); break; } - - _inputReady.Set (); + break; + default: + SetRequestedEvent (c1Control, code, values, terminating); + break; } - void SetRequestedEvent (string c1Control, string code, string [] values, string terminating) - { - EventType eventType = EventType.RequestResponse; - var requestRespEv = new RequestResponseEvent () { - ResultTuple = (c1Control, code, values, terminating) - }; - _inputResultQueue.Enqueue (new InputResult () { - EventType = eventType, - RequestResponseEvent = requestRespEv - }); - } + _inputReady.Set (); + } - void GetMouseEvent (MouseButtonState buttonState, Point pos) - { - MouseEvent mouseEvent = new MouseEvent () { - Position = pos, - ButtonState = buttonState, - }; + void SetRequestedEvent (string c1Control, string code, string [] values, string terminating) + { + EventType eventType = EventType.RequestResponse; + var requestRespEv = new RequestResponseEvent () { + ResultTuple = (c1Control, code, values, terminating) + }; + _inputResultQueue.Enqueue (new InputResult () { + EventType = eventType, + RequestResponseEvent = requestRespEv + }); + } - _inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = mouseEvent - }); + void GetMouseEvent (MouseButtonState buttonState, Point pos) + { + MouseEvent mouseEvent = new MouseEvent () { + Position = pos, + ButtonState = buttonState, + }; - _inputReady.Set (); - } + _inputResultQueue.Enqueue (new InputResult () { + EventType = EventType.Mouse, + MouseEvent = mouseEvent + }); - public enum EventType { - Key = 1, - Mouse = 2, - WindowSize = 3, - WindowPosition = 4, - RequestResponse = 5 - } - - [Flags] - public enum MouseButtonState { - Button1Pressed = 0x1, - Button1Released = 0x2, - Button1Clicked = 0x4, - Button1DoubleClicked = 0x8, - Button1TripleClicked = 0x10, - Button2Pressed = 0x20, - Button2Released = 0x40, - Button2Clicked = 0x80, - Button2DoubleClicked = 0x100, - Button2TripleClicked = 0x200, - Button3Pressed = 0x400, - Button3Released = 0x800, - Button3Clicked = 0x1000, - Button3DoubleClicked = 0x2000, - Button3TripleClicked = 0x4000, - ButtonWheeledUp = 0x8000, - ButtonWheeledDown = 0x10000, - ButtonWheeledLeft = 0x20000, - ButtonWheeledRight = 0x40000, - Button4Pressed = 0x80000, - Button4Released = 0x100000, - Button4Clicked = 0x200000, - Button4DoubleClicked = 0x400000, - Button4TripleClicked = 0x800000, - ButtonShift = 0x1000000, - ButtonCtrl = 0x2000000, - ButtonAlt = 0x4000000, - ReportMousePosition = 0x8000000, - AllEvents = -1 - } - - public struct MouseEvent { - public Point Position; - public MouseButtonState ButtonState; - } - - public struct WindowSizeEvent { - public Size Size; - } - - public struct WindowPositionEvent { - public int Top; - public int Left; - public Point CursorPosition; - } - - public struct RequestResponseEvent { - public (string c1Control, string code, string [] values, string terminating) ResultTuple; - } - - public struct InputResult { - public EventType EventType; - public ConsoleKeyInfo ConsoleKeyInfo; - public MouseEvent MouseEvent; - public WindowSizeEvent WindowSizeEvent; - public WindowPositionEvent WindowPositionEvent; - public RequestResponseEvent RequestResponseEvent; - } - } - - internal class NetDriver : ConsoleDriver { - const int COLOR_BLACK = 30; - const int COLOR_RED = 31; - const int COLOR_GREEN = 32; - const int COLOR_YELLOW = 33; - const int COLOR_BLUE = 34; - const int COLOR_MAGENTA = 35; - const int COLOR_CYAN = 36; - const int COLOR_WHITE = 37; - const int COLOR_BRIGHT_BLACK = 90; - const int COLOR_BRIGHT_RED = 91; - const int COLOR_BRIGHT_GREEN = 92; - const int COLOR_BRIGHT_YELLOW = 93; - const int COLOR_BRIGHT_BLUE = 94; - const int COLOR_BRIGHT_MAGENTA = 95; - const int COLOR_BRIGHT_CYAN = 96; - const int COLOR_BRIGHT_WHITE = 97; - - public NetWinVTConsole NetWinConsole { get; } - public bool IsWinPlatform { get; } - - int _largestBufferHeight; - - public NetDriver () - { - var p = Environment.OSVersion.Platform; - if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) { - IsWinPlatform = true; - NetWinConsole = new NetWinVTConsole (); - } - if (IsWinPlatform) { - Clipboard = new WindowsClipboard (); - } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - Clipboard = new MacOSXClipboard (); - } else { - if (CursesDriver.Is_WSL_Platform ()) { - Clipboard = new WSLClipboard (); - } else { - Clipboard = new CursesClipboard (); - } - } - } + _inputReady.Set (); + } + + public enum EventType { + Key = 1, + Mouse = 2, + WindowSize = 3, + WindowPosition = 4, + RequestResponse = 5 + } - bool [] _dirtyLine; + [Flags] + public enum MouseButtonState { + Button1Pressed = 0x1, + Button1Released = 0x2, + Button1Clicked = 0x4, + Button1DoubleClicked = 0x8, + Button1TripleClicked = 0x10, + Button2Pressed = 0x20, + Button2Released = 0x40, + Button2Clicked = 0x80, + Button2DoubleClicked = 0x100, + Button2TripleClicked = 0x200, + Button3Pressed = 0x400, + Button3Released = 0x800, + Button3Clicked = 0x1000, + Button3DoubleClicked = 0x2000, + Button3TripleClicked = 0x4000, + ButtonWheeledUp = 0x8000, + ButtonWheeledDown = 0x10000, + ButtonWheeledLeft = 0x20000, + ButtonWheeledRight = 0x40000, + Button4Pressed = 0x80000, + Button4Released = 0x100000, + Button4Clicked = 0x200000, + Button4DoubleClicked = 0x400000, + Button4TripleClicked = 0x800000, + ButtonShift = 0x1000000, + ButtonCtrl = 0x2000000, + ButtonAlt = 0x4000000, + ReportMousePosition = 0x8000000, + AllEvents = -1 + } - public override void AddRune (Rune rune) - { - if (Contents.Length != Rows * Cols * 3) { - // BUGBUG: Shouldn't this throw an exception? Doing so to see what happens - throw new InvalidOperationException ("Driver contents are wrong size"); - return; - } + public struct MouseEvent { + public Point Position; + public MouseButtonState ButtonState; + } - rune = MakePrintable (rune); - var runeWidth = Rune.ColumnWidth (rune); - var validLocation = IsValidLocation (Col, Row); - - if (validLocation) { - if (runeWidth == 0 && Col > 0) { - // Single column glyph beyond first col - var r = Contents [Row, Col - 1, 0]; - var s = new string (new char [] { (char)r, (char)rune }); - var sn = !s.IsNormalized () ? s.Normalize () : s; - var c = sn [0]; - Contents [Row, Col - 1, 0] = c; - Contents [Row, Col - 1, 1] = CurrentAttribute; - Contents [Row, Col - 1, 2] = 1; - } else { - if (runeWidth < 2 && Col > 0 - && Rune.ColumnWidth ((char)Contents [Row, Col - 1, 0]) > 1) { + public struct WindowSizeEvent { + public Size Size; + } - Contents [Row, Col - 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; + public struct WindowPositionEvent { + public int Top; + public int Left; + public Point CursorPosition; + } - } else if (runeWidth < 2 && Col <= Clip.Right - 1 - && Rune.ColumnWidth ((char)Contents [Row, Col, 0]) > 1) { + public struct RequestResponseEvent { + public (string c1Control, string code, string [] values, string terminating) ResultTuple; + } - Contents [Row, Col + 1, 0] = (char)System.Text.Rune.ReplacementChar.Value; - Contents [Row, Col + 1, 2] = 1; + public struct InputResult { + public EventType EventType; + public ConsoleKeyInfo ConsoleKeyInfo; + public MouseEvent MouseEvent; + public WindowSizeEvent WindowSizeEvent; + public WindowPositionEvent WindowPositionEvent; + public RequestResponseEvent RequestResponseEvent; + } +} + +internal class NetDriver : ConsoleDriver { + const int COLOR_BLACK = 30; + const int COLOR_RED = 31; + const int COLOR_GREEN = 32; + const int COLOR_YELLOW = 33; + const int COLOR_BLUE = 34; + const int COLOR_MAGENTA = 35; + const int COLOR_CYAN = 36; + const int COLOR_WHITE = 37; + const int COLOR_BRIGHT_BLACK = 90; + const int COLOR_BRIGHT_RED = 91; + const int COLOR_BRIGHT_GREEN = 92; + const int COLOR_BRIGHT_YELLOW = 93; + const int COLOR_BRIGHT_BLUE = 94; + const int COLOR_BRIGHT_MAGENTA = 95; + const int COLOR_BRIGHT_CYAN = 96; + const int COLOR_BRIGHT_WHITE = 97; + + public NetWinVTConsole NetWinConsole { get; private set; } + public bool IsWinPlatform { get; private set; } + + int _largestBufferHeight; + + public NetDriver () + { + } - } - if (runeWidth > 1 && Col == Clip.Right - 1) { - Contents [Row, Col, 0] = (char)System.Text.Rune.ReplacementChar.Value; - } else { - Contents [Row, Col, 0] = (int)(uint)rune; - } - Contents [Row, Col, 1] = CurrentAttribute; - Contents [Row, Col, 2] = 1; + bool [] _dirtyLine; + public override void AddRune (System.Rune systemRune) + { + if (Contents.Length != Rows * Cols * 3) { + // BUGBUG: Shouldn't this throw an exception? Doing so to see what happens + throw new InvalidOperationException ("Driver contents are wrong size"); + return; + } + + var rune = new Rune (systemRune).MakePrintable (); + var runeWidth = rune.GetColumnWidth (); + var validLocation = IsValidLocation (Col, Row); + + if (validLocation) { + if (runeWidth == 0 && Col > 0) { + // This is a combining character, and we are not at the beginning of the line. + var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); + var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; + Contents [Row, Col - 1, 0] = normalized [0]; + Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 2] = 1; + } else { + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 1; + + if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumnWidth () > 1) { + // This is a single-width character, and we are not at the beginning of the line. + Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; + } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumnWidth () > 1) { + // This is a single-width character, and we are not at the end of the line. + Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; + Contents [Row, Col + 1, 2] = 1; + } + if (runeWidth > 1 && Col == Clip.Right - 1) { + // This is a double-width character, and we are at the end of the line. + Contents [Row, Col, 0] = Rune.ReplacementChar.Value; + } else { + // This is a single-width character, or we are not at the end of the line. + Contents [Row, Col, 0] = rune.Value; } _dirtyLine [Row] = true; } + } - if (runeWidth < 0 || runeWidth > 0) { - Col++; - } + if (runeWidth is < 0 or > 0) { + Col++; + } - if (runeWidth > 1) { - if (validLocation && Col < Clip.Right) { - Contents [Row, Col, 1] = CurrentAttribute; - Contents [Row, Col, 2] = 0; - } - Col++; + if (runeWidth > 1) { + // This is a double-width character, and we are not at the end of the line. + if (validLocation && Col < Clip.Right) { + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 0; } + Col++; } - public override void End () - { - _mainLoop._netEvents.StopTasks (); + } - if (IsWinPlatform) { - NetWinConsole.Cleanup (); - } + public override void End () + { + _mainLoop._netEvents.StopTasks (); + + if (IsWinPlatform) { + NetWinConsole.Cleanup (); + } - StopReportingMouseMoves (); - Console.ResetColor (); + StopReportingMouseMoves (); + Console.ResetColor (); - //Disable alternative screen buffer. - Console.Out.Write ("\x1b[?1049l"); + //Disable alternative screen buffer. + Console.Out.Write ("\x1b[?1049l"); - //Set cursor key to cursor. - Console.Out.Write ("\x1b[?25h"); + //Set cursor key to cursor. + Console.Out.Write ("\x1b[?25h"); - Console.Out.Close (); - } + Console.Out.Close (); + } - public override Attribute MakeColor (Color foreground, Color background) - { - return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); - } + public override Attribute MakeColor (Color foreground, Color background) + { + return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); + } + + static Attribute MakeColor (ConsoleColor f, ConsoleColor b) + { + // Encode the colors into the int value. + return new Attribute ( + value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff), + foreground: (Color)f, + background: (Color)b + ); + } - static Attribute MakeColor (ConsoleColor f, ConsoleColor b) - { - // Encode the colors into the int value. - return new Attribute ( - value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff), - foreground: (Color)f, - background: (Color)b - ); + public override void Init (Action terminalResized) + { + var p = Environment.OSVersion.Platform; + if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) { + IsWinPlatform = true; + try { + NetWinConsole = new NetWinVTConsole (); + } catch (ApplicationException e) { + // Likely running as a unit test, or in a non-interactive session. + } + } + if (IsWinPlatform) { + Clipboard = new WindowsClipboard (); + } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { + Clipboard = new MacOSXClipboard (); + } else { + if (CursesDriver.Is_WSL_Platform ()) { + Clipboard = new WSLClipboard (); + } else { + Clipboard = new CursesClipboard (); + } } - public override void Init (Action terminalResized) - { - TerminalResized = terminalResized; + TerminalResized = terminalResized; + if (NetWinConsole != null) { //Enable alternative screen buffer. Console.Out.Write ("\x1b[?1049h"); @@ -756,31 +760,58 @@ public override void Init (Action terminalResized) Cols = Console.WindowWidth; Rows = _largestBufferHeight; + } else { + // Simluate + Cols = 80; + Rows = 25; + _largestBufferHeight = Rows; + } - CurrentAttribute = MakeColor (Color.White, Color.Black); - InitializeColorSchemes (); - - CurrentAttribute = MakeColor (Color.White, Color.Black); - InitializeColorSchemes (); + ResizeScreen (); + UpdateOffScreen (); + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitializeColorSchemes (); - ResizeScreen (); - UpdateOffScreen (); + StartReportingMouseMoves (); + } - StartReportingMouseMoves (); + public virtual void ResizeScreen () + { + if (NetWinConsole == null) { + return; } - - public virtual void ResizeScreen () - { - if (!EnableConsoleScrolling && Console.WindowHeight > 0) { - // Not supported on Unix. - if (IsWinPlatform) { + + if (!EnableConsoleScrolling && Console.WindowHeight > 0) { + // Not supported on Unix. + if (IsWinPlatform) { + // Can raise an exception while is still resizing. + try { +#pragma warning disable CA1416 + Console.CursorTop = 0; + Console.CursorLeft = 0; + Console.WindowTop = 0; + Console.WindowLeft = 0; + if (Console.WindowHeight > Rows) { + Console.SetWindowSize (Cols, Rows); + } + Console.SetBufferSize (Cols, Rows); +#pragma warning restore CA1416 + } catch (System.IO.IOException) { + Clip = new Rect (0, 0, Cols, Rows); + } catch (ArgumentOutOfRangeException) { + Clip = new Rect (0, 0, Cols, Rows); + } + } else { + Console.Out.Write ($"\x1b[8;{Rows};{Cols}t"); + } + } else { + if (IsWinPlatform) { + if (Console.WindowHeight > 0) { // Can raise an exception while is still resizing. try { #pragma warning disable CA1416 Console.CursorTop = 0; Console.CursorLeft = 0; - Console.WindowTop = 0; - Console.WindowLeft = 0; if (Console.WindowHeight > Rows) { Console.SetWindowSize (Cols, Rows); } @@ -791,776 +822,754 @@ public virtual void ResizeScreen () } catch (ArgumentOutOfRangeException) { Clip = new Rect (0, 0, Cols, Rows); } - } else { - Console.Out.Write ($"\x1b[8;{Rows};{Cols}t"); } } else { - if (IsWinPlatform) { - if (Console.WindowHeight > 0) { - // Can raise an exception while is still resizing. - try { -#pragma warning disable CA1416 - Console.CursorTop = 0; - Console.CursorLeft = 0; - if (Console.WindowHeight > Rows) { - Console.SetWindowSize (Cols, Rows); - } - Console.SetBufferSize (Cols, Rows); -#pragma warning restore CA1416 - } catch (System.IO.IOException) { - Clip = new Rect (0, 0, Cols, Rows); - } catch (ArgumentOutOfRangeException) { - Clip = new Rect (0, 0, Cols, Rows); - } - } - } else { - Console.Out.Write ($"\x1b[30;{Rows};{Cols}t"); - } + Console.Out.Write ($"\x1b[30;{Rows};{Cols}t"); } - Clip = new Rect (0, 0, Cols, Rows); } + Clip = new Rect (0, 0, Cols, Rows); + } - public override void UpdateOffScreen () - { - Contents = new int [Rows, Cols, 3]; - _dirtyLine = new bool [Rows]; + public override void UpdateOffScreen () + { + Contents = new int [Rows, Cols, 3]; + _dirtyLine = new bool [Rows]; - lock (Contents) { - // Can raise an exception while is still resizing. - try { - for (int row = 0; row < Rows; row++) { - for (int c = 0; c < Cols; c++) { - Contents [row, c, 0] = ' '; - Contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; - Contents [row, c, 2] = 0; - _dirtyLine [row] = true; - } + lock (Contents) { + // Can raise an exception while is still resizing. + try { + for (int row = 0; row < Rows; row++) { + for (int c = 0; c < Cols; c++) { + Contents [row, c, 0] = ' '; + Contents [row, c, 1] = new Attribute (Color.White, Color.Black).Value; + Contents [row, c, 2] = 0; + _dirtyLine [row] = true; } - } catch (IndexOutOfRangeException) { } - } + } + } catch (IndexOutOfRangeException) { } } + } - public override void Refresh () - { - UpdateScreen (); - UpdateCursor (); - } + public override void Refresh () + { + UpdateScreen (); + UpdateCursor (); + } - public override void UpdateScreen () - { - if (winChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols * 3 - || (!EnableConsoleScrolling && Rows != Console.WindowHeight) - || (EnableConsoleScrolling && Rows != _largestBufferHeight)) { - return; - } + public override void UpdateScreen () + { + if (winChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols * 3 + || (!EnableConsoleScrolling && Rows != Console.WindowHeight) + || (EnableConsoleScrolling && Rows != _largestBufferHeight)) { + return; + } - int top = 0; - int left = 0; - int rows = Rows; - int cols = Cols; - System.Text.StringBuilder output = new System.Text.StringBuilder (); - int redrawAttr = -1; - var lastCol = -1; + int top = 0; + int left = 0; + int rows = Rows; + int cols = Cols; + System.Text.StringBuilder output = new System.Text.StringBuilder (); + int redrawAttr = -1; + var lastCol = -1; - Console.CursorVisible = false; + Console.CursorVisible = false; - for (int row = top; row < rows; row++) { - if (Console.WindowHeight < 1) { - return; - } - if (!_dirtyLine [row]) { - continue; - } - if (!SetCursorPosition (0, row)) { - return; - } - _dirtyLine [row] = false; - output.Clear (); - for (int col = left; col < cols; col++) { - lastCol = -1; - var outputWidth = 0; - for (; col < cols; col++) { - if (Contents [row, col, 2] == 0) { - if (output.Length > 0) { - SetCursorPosition (lastCol, row); - Console.Write (output); - output.Clear (); - lastCol += outputWidth; - outputWidth = 0; - } else if (lastCol == -1) { - lastCol = col; - } - if (lastCol + 1 < cols) - lastCol++; - continue; + for (int row = top; row < rows; row++) { + if (Console.WindowHeight < 1) { + return; + } + if (!_dirtyLine [row]) { + continue; + } + if (!SetCursorPosition (0, row)) { + return; + } + _dirtyLine [row] = false; + output.Clear (); + for (int col = left; col < cols; col++) { + lastCol = -1; + var outputWidth = 0; + for (; col < cols; col++) { + if (Contents [row, col, 2] == 0) { + if (output.Length > 0) { + SetCursorPosition (lastCol, row); + Console.Write (output); + output.Clear (); + lastCol += outputWidth; + outputWidth = 0; + } else if (lastCol == -1) { + lastCol = col; } + if (lastCol + 1 < cols) + lastCol++; + continue; + } - if (lastCol == -1) - lastCol = col; + if (lastCol == -1) + lastCol = col; - var attr = Contents [row, col, 1]; - if (attr != redrawAttr) { - redrawAttr = attr; - output.Append (WriteAttributes (attr)); - } - outputWidth++; - var rune = Contents [row, col, 0]; - char [] spair; - if (Rune.DecodeSurrogatePair ((uint)rune, out spair)) { - output.Append (spair); - } else { - output.Append ((char)rune); - } - Contents [row, col, 2] = 0; + var attr = Contents [row, col, 1]; + if (attr != redrawAttr) { + redrawAttr = attr; + output.Append (WriteAttributes (attr)); } - } - if (output.Length > 0) { - SetCursorPosition (lastCol, row); - Console.Write (output); + outputWidth++; + var rune = (Rune)Contents [row, col, 0]; + if (rune.Utf16SequenceLength == 1) { + output.Append (rune); + } else { + // TODO: Not sure we need to do this. I think we can just append the rune. + output.Append (rune.ToString ()); + } + Contents [row, col, 2] = 0; } } - SetCursorPosition (0, 0); + if (output.Length > 0) { + SetCursorPosition (lastCol, row); + Console.Write (output); + } } + SetCursorPosition (0, 0); + } - void SetVirtualCursorPosition (int col, int row) - { - Console.Out.Write ($"\x1b[{row + 1};{col + 1}H"); - } + void SetVirtualCursorPosition (int col, int row) + { + Console.Out.Write ($"\x1b[{row + 1};{col + 1}H"); + } - System.Text.StringBuilder WriteAttributes (int attr) - { - const string CSI = "\x1b["; - int bg = 0; - int fg = 0; - System.Text.StringBuilder sb = new System.Text.StringBuilder (); + System.Text.StringBuilder WriteAttributes (int attr) + { + const string CSI = "\x1b["; + int bg = 0; + int fg = 0; + System.Text.StringBuilder sb = new System.Text.StringBuilder (); - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (attr & 0xffff)) { - bg = MapColors ((ConsoleColor)(attr & 0xffff), false); - } - if (values.Contains ((attr >> 16) & 0xffff)) { - fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff)); - } - sb.Append ($"{CSI}{bg};{fg}m"); - - return sb; - } - - int MapColors (ConsoleColor color, bool isForeground = true) - { - switch (color) { - case ConsoleColor.Black: - return isForeground ? COLOR_BLACK : COLOR_BLACK + 10; - case ConsoleColor.DarkBlue: - return isForeground ? COLOR_BLUE : COLOR_BLUE + 10; - case ConsoleColor.DarkGreen: - return isForeground ? COLOR_GREEN : COLOR_GREEN + 10; - case ConsoleColor.DarkCyan: - return isForeground ? COLOR_CYAN : COLOR_CYAN + 10; - case ConsoleColor.DarkRed: - return isForeground ? COLOR_RED : COLOR_RED + 10; - case ConsoleColor.DarkMagenta: - return isForeground ? COLOR_MAGENTA : COLOR_MAGENTA + 10; - case ConsoleColor.DarkYellow: - return isForeground ? COLOR_YELLOW : COLOR_YELLOW + 10; - case ConsoleColor.Gray: - return isForeground ? COLOR_WHITE : COLOR_WHITE + 10; - case ConsoleColor.DarkGray: - return isForeground ? COLOR_BRIGHT_BLACK : COLOR_BRIGHT_BLACK + 10; - case ConsoleColor.Blue: - return isForeground ? COLOR_BRIGHT_BLUE : COLOR_BRIGHT_BLUE + 10; - case ConsoleColor.Green: - return isForeground ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_GREEN + 10; - case ConsoleColor.Cyan: - return isForeground ? COLOR_BRIGHT_CYAN : COLOR_BRIGHT_CYAN + 10; - case ConsoleColor.Red: - return isForeground ? COLOR_BRIGHT_RED : COLOR_BRIGHT_RED + 10; - case ConsoleColor.Magenta: - return isForeground ? COLOR_BRIGHT_MAGENTA : COLOR_BRIGHT_MAGENTA + 10; - case ConsoleColor.Yellow: - return isForeground ? COLOR_BRIGHT_YELLOW : COLOR_BRIGHT_YELLOW + 10; - case ConsoleColor.White: - return isForeground ? COLOR_BRIGHT_WHITE : COLOR_BRIGHT_WHITE + 10; - } - return 0; + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains (attr & 0xffff)) { + bg = MapColors ((ConsoleColor)(attr & 0xffff), false); } + if (values.Contains ((attr >> 16) & 0xffff)) { + fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff)); + } + sb.Append ($"{CSI}{bg};{fg}m"); - bool SetCursorPosition (int col, int row) - { - if (IsWinPlatform) { - // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. - try { - Console.SetCursorPosition (col, row); - return true; - } catch (Exception) { - return false; - } - } else { - SetVirtualCursorPosition (col, row); + return sb; + } + + int MapColors (ConsoleColor color, bool isForeground = true) + { + switch (color) { + case ConsoleColor.Black: + return isForeground ? COLOR_BLACK : COLOR_BLACK + 10; + case ConsoleColor.DarkBlue: + return isForeground ? COLOR_BLUE : COLOR_BLUE + 10; + case ConsoleColor.DarkGreen: + return isForeground ? COLOR_GREEN : COLOR_GREEN + 10; + case ConsoleColor.DarkCyan: + return isForeground ? COLOR_CYAN : COLOR_CYAN + 10; + case ConsoleColor.DarkRed: + return isForeground ? COLOR_RED : COLOR_RED + 10; + case ConsoleColor.DarkMagenta: + return isForeground ? COLOR_MAGENTA : COLOR_MAGENTA + 10; + case ConsoleColor.DarkYellow: + return isForeground ? COLOR_YELLOW : COLOR_YELLOW + 10; + case ConsoleColor.Gray: + return isForeground ? COLOR_WHITE : COLOR_WHITE + 10; + case ConsoleColor.DarkGray: + return isForeground ? COLOR_BRIGHT_BLACK : COLOR_BRIGHT_BLACK + 10; + case ConsoleColor.Blue: + return isForeground ? COLOR_BRIGHT_BLUE : COLOR_BRIGHT_BLUE + 10; + case ConsoleColor.Green: + return isForeground ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_GREEN + 10; + case ConsoleColor.Cyan: + return isForeground ? COLOR_BRIGHT_CYAN : COLOR_BRIGHT_CYAN + 10; + case ConsoleColor.Red: + return isForeground ? COLOR_BRIGHT_RED : COLOR_BRIGHT_RED + 10; + case ConsoleColor.Magenta: + return isForeground ? COLOR_BRIGHT_MAGENTA : COLOR_BRIGHT_MAGENTA + 10; + case ConsoleColor.Yellow: + return isForeground ? COLOR_BRIGHT_YELLOW : COLOR_BRIGHT_YELLOW + 10; + case ConsoleColor.White: + return isForeground ? COLOR_BRIGHT_WHITE : COLOR_BRIGHT_WHITE + 10; + } + return 0; + } + + bool SetCursorPosition (int col, int row) + { + if (IsWinPlatform) { + // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. + try { + Console.SetCursorPosition (col, row); return true; + } catch (Exception) { + return false; } + } else { + SetVirtualCursorPosition (col, row); + return true; } + } - private void SetWindowPosition (int col, int row) - { - if (IsWinPlatform && EnableConsoleScrolling) { - var winTop = Math.Max (Rows - Console.WindowHeight - row, 0); - winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1); - winTop = Math.Max (winTop, 0); - if (winTop != Console.WindowTop) { - try { - if (!EnsureBufferSize ()) { - return; - } + private void SetWindowPosition (int col, int row) + { + if (IsWinPlatform && EnableConsoleScrolling) { + var winTop = Math.Max (Rows - Console.WindowHeight - row, 0); + winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1); + winTop = Math.Max (winTop, 0); + if (winTop != Console.WindowTop) { + try { + if (!EnsureBufferSize ()) { + return; + } #pragma warning disable CA1416 - Console.SetWindowPosition (col, winTop); + Console.SetWindowPosition (col, winTop); #pragma warning restore CA1416 - } catch (System.IO.IOException) { + } catch (System.IO.IOException) { - } catch (System.ArgumentOutOfRangeException) { } - } + } catch (System.ArgumentOutOfRangeException) { } } - Top = Console.WindowTop; - Left = Console.WindowLeft; } + Top = Console.WindowTop; + Left = Console.WindowLeft; + } - private bool EnsureBufferSize () - { + private bool EnsureBufferSize () + { #pragma warning disable CA1416 - if (IsWinPlatform && Console.BufferHeight < Rows) { - try { - Console.SetBufferSize (Console.WindowWidth, Rows); - } catch (Exception) { - return false; - } + if (IsWinPlatform && Console.BufferHeight < Rows) { + try { + Console.SetBufferSize (Console.WindowWidth, Rows); + } catch (Exception) { + return false; } -#pragma warning restore CA1416 - return true; } +#pragma warning restore CA1416 + return true; + } - private CursorVisibility? savedCursorVisibility; + private CursorVisibility? savedCursorVisibility; - public override void UpdateCursor () - { - EnsureCursorVisibility (); + public override void UpdateCursor () + { + EnsureCursorVisibility (); - if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) { - SetCursorPosition (Col, Row); - SetWindowPosition (0, Row); - } + if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) { + SetCursorPosition (Col, Row); + SetWindowPosition (0, Row); } + } - public override bool GetCursorVisibility (out CursorVisibility visibility) - { - visibility = savedCursorVisibility ?? CursorVisibility.Default; - return visibility == CursorVisibility.Default; - } + public override bool GetCursorVisibility (out CursorVisibility visibility) + { + visibility = savedCursorVisibility ?? CursorVisibility.Default; + return visibility == CursorVisibility.Default; + } - public override bool SetCursorVisibility (CursorVisibility visibility) - { - savedCursorVisibility = visibility; - return Console.CursorVisible = visibility == CursorVisibility.Default; + public override bool SetCursorVisibility (CursorVisibility visibility) + { + savedCursorVisibility = visibility; + return Console.CursorVisible = visibility == CursorVisibility.Default; + } + + public override bool EnsureCursorVisibility () + { + if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { + GetCursorVisibility (out CursorVisibility cursorVisibility); + savedCursorVisibility = cursorVisibility; + SetCursorVisibility (CursorVisibility.Invisible); + return false; } - public override bool EnsureCursorVisibility () - { - if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { - GetCursorVisibility (out CursorVisibility cursorVisibility); - savedCursorVisibility = cursorVisibility; - SetCursorVisibility (CursorVisibility.Invisible); - return false; - } + SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default); + return savedCursorVisibility == CursorVisibility.Default; + } - SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default); - return savedCursorVisibility == CursorVisibility.Default; - } - - public void StartReportingMouseMoves () - { - Console.Out.Write (EscSeqUtils.EnableMouseEvents); - } + public void StartReportingMouseMoves () + { + Console.Out.Write (EscSeqUtils.EnableMouseEvents); + } - public void StopReportingMouseMoves () - { - Console.Out.Write (EscSeqUtils.DisableMouseEvents); - } + public void StopReportingMouseMoves () + { + Console.Out.Write (EscSeqUtils.DisableMouseEvents); + } - #region Not Implemented - public override void Suspend () - { - throw new NotImplementedException (); + #region Not Implemented + public override void Suspend () + { + throw new NotImplementedException (); + } + #endregion + + public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) + { + if (consoleKeyInfo.Key != ConsoleKey.Packet) { + return consoleKeyInfo; } - #endregion - public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) - { - if (consoleKeyInfo.Key != ConsoleKey.Packet) { - return consoleKeyInfo; - } + var mod = consoleKeyInfo.Modifiers; + var shift = (mod & ConsoleModifiers.Shift) != 0; + var alt = (mod & ConsoleModifiers.Alt) != 0; + var control = (mod & ConsoleModifiers.Control) != 0; - var mod = consoleKeyInfo.Modifiers; - var shift = (mod & ConsoleModifiers.Shift) != 0; - var alt = (mod & ConsoleModifiers.Alt) != 0; - var control = (mod & ConsoleModifiers.Control) != 0; - - var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _); - - return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control); - } - - Key MapKey (ConsoleKeyInfo keyInfo) - { - MapKeyModifiers (keyInfo, (Key)keyInfo.Key); - switch (keyInfo.Key) { - case ConsoleKey.Escape: - return MapKeyModifiers (keyInfo, Key.Esc); - case ConsoleKey.Tab: - return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; - case ConsoleKey.Home: - return MapKeyModifiers (keyInfo, Key.Home); - case ConsoleKey.End: - return MapKeyModifiers (keyInfo, Key.End); - case ConsoleKey.LeftArrow: - return MapKeyModifiers (keyInfo, Key.CursorLeft); - case ConsoleKey.RightArrow: - return MapKeyModifiers (keyInfo, Key.CursorRight); - case ConsoleKey.UpArrow: - return MapKeyModifiers (keyInfo, Key.CursorUp); - case ConsoleKey.DownArrow: - return MapKeyModifiers (keyInfo, Key.CursorDown); - case ConsoleKey.PageUp: - return MapKeyModifiers (keyInfo, Key.PageUp); - case ConsoleKey.PageDown: - return MapKeyModifiers (keyInfo, Key.PageDown); - case ConsoleKey.Enter: - return MapKeyModifiers (keyInfo, Key.Enter); - case ConsoleKey.Spacebar: - return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); - case ConsoleKey.Backspace: - return MapKeyModifiers (keyInfo, Key.Backspace); - case ConsoleKey.Delete: - return MapKeyModifiers (keyInfo, Key.DeleteChar); - case ConsoleKey.Insert: - return MapKeyModifiers (keyInfo, Key.InsertChar); - - case ConsoleKey.Oem1: - case ConsoleKey.Oem2: - case ConsoleKey.Oem3: - case ConsoleKey.Oem4: - case ConsoleKey.Oem5: - case ConsoleKey.Oem6: - case ConsoleKey.Oem7: - case ConsoleKey.Oem8: - case ConsoleKey.Oem102: - case ConsoleKey.OemPeriod: - case ConsoleKey.OemComma: - case ConsoleKey.OemPlus: - case ConsoleKey.OemMinus: - return (Key)((uint)keyInfo.KeyChar); - } + var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _); - var key = keyInfo.Key; - if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { - var delta = key - ConsoleKey.A; - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); - } + return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control); + } + + Key MapKey (ConsoleKeyInfo keyInfo) + { + MapKeyModifiers (keyInfo, (Key)keyInfo.Key); + switch (keyInfo.Key) { + case ConsoleKey.Escape: + return MapKeyModifiers (keyInfo, Key.Esc); + case ConsoleKey.Tab: + return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; + case ConsoleKey.Home: + return MapKeyModifiers (keyInfo, Key.Home); + case ConsoleKey.End: + return MapKeyModifiers (keyInfo, Key.End); + case ConsoleKey.LeftArrow: + return MapKeyModifiers (keyInfo, Key.CursorLeft); + case ConsoleKey.RightArrow: + return MapKeyModifiers (keyInfo, Key.CursorRight); + case ConsoleKey.UpArrow: + return MapKeyModifiers (keyInfo, Key.CursorUp); + case ConsoleKey.DownArrow: + return MapKeyModifiers (keyInfo, Key.CursorDown); + case ConsoleKey.PageUp: + return MapKeyModifiers (keyInfo, Key.PageUp); + case ConsoleKey.PageDown: + return MapKeyModifiers (keyInfo, Key.PageDown); + case ConsoleKey.Enter: + return MapKeyModifiers (keyInfo, Key.Enter); + case ConsoleKey.Spacebar: + return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); + case ConsoleKey.Backspace: + return MapKeyModifiers (keyInfo, Key.Backspace); + case ConsoleKey.Delete: + return MapKeyModifiers (keyInfo, Key.DeleteChar); + case ConsoleKey.Insert: + return MapKeyModifiers (keyInfo, Key.InsertChar); + + case ConsoleKey.Oem1: + case ConsoleKey.Oem2: + case ConsoleKey.Oem3: + case ConsoleKey.Oem4: + case ConsoleKey.Oem5: + case ConsoleKey.Oem6: + case ConsoleKey.Oem7: + case ConsoleKey.Oem8: + case ConsoleKey.Oem102: + case ConsoleKey.OemPeriod: + case ConsoleKey.OemComma: + case ConsoleKey.OemPlus: + case ConsoleKey.OemMinus: + return (Key)((uint)keyInfo.KeyChar); + } + + var key = keyInfo.Key; + if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { + var delta = key - ConsoleKey.A; + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); } - return (Key)((uint)keyInfo.KeyChar); } - if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { - var delta = key - ConsoleKey.D0; - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); - } - } - return (Key)((uint)keyInfo.KeyChar); + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { + var delta = key - ConsoleKey.D0; + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); } - if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { - var delta = key - ConsoleKey.F1; - if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); } - - return (Key)((uint)Key.F1 + delta); } - if (keyInfo.KeyChar != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { + var delta = key - ConsoleKey.F1; + if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); } - return (Key)(0xffffffff); + return (Key)((uint)Key.F1 + delta); + } + if (keyInfo.KeyChar != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); } - KeyModifiers _keyModifiers; + return (Key)(0xffffffff); + } - Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) - { - if (_keyModifiers == null) { - _keyModifiers = new KeyModifiers (); - } - Key keyMod = new Key (); - if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { - keyMod = Key.ShiftMask; - _keyModifiers.Shift = true; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { - keyMod |= Key.CtrlMask; - _keyModifiers.Ctrl = true; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { - keyMod |= Key.AltMask; - _keyModifiers.Alt = true; - } + KeyModifiers _keyModifiers; - return keyMod != Key.Null ? keyMod | key : key; + Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) + { + if (_keyModifiers == null) { + _keyModifiers = new KeyModifiers (); + } + Key keyMod = new Key (); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { + keyMod = Key.ShiftMask; + _keyModifiers.Shift = true; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { + keyMod |= Key.CtrlMask; + _keyModifiers.Ctrl = true; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { + keyMod |= Key.AltMask; + _keyModifiers.Alt = true; } - Action _keyHandler; - Action _keyDownHandler; - Action _keyUpHandler; - Action _mouseHandler; - NetMainLoop _mainLoop; + return keyMod != Key.Null ? keyMod | key : key; + } - public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) - { - _keyHandler = keyHandler; - _keyDownHandler = keyDownHandler; - _keyUpHandler = keyUpHandler; - _mouseHandler = mouseHandler; + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; + NetMainLoop _mainLoop; - var mLoop = _mainLoop = mainLoop.Driver as NetMainLoop; + public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) + { + _keyHandler = keyHandler; + _keyDownHandler = keyDownHandler; + _keyUpHandler = keyUpHandler; + _mouseHandler = mouseHandler; - // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. - mLoop.ProcessInput = (e) => ProcessInput (e); + var mLoop = _mainLoop = mainLoop.Driver as NetMainLoop; - // Check if terminal supports requests - _mainLoop._netEvents.EscSeqReqProc.Add ("c"); - Console.Out.Write ("\x1b[0c"); - } + // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. + mLoop.ProcessInput = (e) => ProcessInput (e); - void ProcessInput (NetEvents.InputResult inputEvent) - { - switch (inputEvent.EventType) { - case NetEvents.EventType.Key: - ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo; - if (consoleKeyInfo.Key == ConsoleKey.Packet) { - consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo); - } - _keyModifiers = new KeyModifiers (); - var map = MapKey (consoleKeyInfo); - if (map == (Key)0xffffffff) { - return; - } - if (map == Key.Null) { - _keyDownHandler (new KeyEvent (map, _keyModifiers)); - _keyUpHandler (new KeyEvent (map, _keyModifiers)); - } else { - _keyDownHandler (new KeyEvent (map, _keyModifiers)); - _keyHandler (new KeyEvent (map, _keyModifiers)); - _keyUpHandler (new KeyEvent (map, _keyModifiers)); - } - break; - case NetEvents.EventType.Mouse: - _mouseHandler (ToDriverMouse (inputEvent.MouseEvent)); - break; - case NetEvents.EventType.WindowSize: - ChangeWin (inputEvent.WindowSizeEvent.Size); - break; - case NetEvents.EventType.RequestResponse: - Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; - break; - } - } - - volatile bool winChanging; + // Check if terminal supports requests + _mainLoop._netEvents.EscSeqReqProc.Add ("c"); + Console.Out.Write ("\x1b[0c"); + } - void ChangeWin (Size size) - { - winChanging = true; - if (!EnableConsoleScrolling) { - _largestBufferHeight = Math.Max (size.Height, 0); - } else { - _largestBufferHeight = Math.Max (size.Height, _largestBufferHeight); + void ProcessInput (NetEvents.InputResult inputEvent) + { + switch (inputEvent.EventType) { + case NetEvents.EventType.Key: + ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo; + if (consoleKeyInfo.Key == ConsoleKey.Packet) { + consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo); + } + _keyModifiers = new KeyModifiers (); + var map = MapKey (consoleKeyInfo); + if (map == (Key)0xffffffff) { + return; } - Top = 0; - Left = 0; - Cols = size.Width; - Rows = _largestBufferHeight; - ResizeScreen (); - UpdateOffScreen (); - winChanging = false; - TerminalResized?.Invoke (); + if (map == Key.Null) { + _keyDownHandler (new KeyEvent (map, _keyModifiers)); + _keyUpHandler (new KeyEvent (map, _keyModifiers)); + } else { + _keyDownHandler (new KeyEvent (map, _keyModifiers)); + _keyHandler (new KeyEvent (map, _keyModifiers)); + _keyUpHandler (new KeyEvent (map, _keyModifiers)); + } + break; + case NetEvents.EventType.Mouse: + _mouseHandler (ToDriverMouse (inputEvent.MouseEvent)); + break; + case NetEvents.EventType.WindowSize: + ChangeWin (inputEvent.WindowSizeEvent.Size); + break; + case NetEvents.EventType.RequestResponse: + Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; + break; } + } - MouseEvent ToDriverMouse (NetEvents.MouseEvent me) - { - //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}"); + volatile bool winChanging; + + void ChangeWin (Size size) + { + winChanging = true; + if (!EnableConsoleScrolling) { + _largestBufferHeight = Math.Max (size.Height, 0); + } else { + _largestBufferHeight = Math.Max (size.Height, _largestBufferHeight); + } + Top = 0; + Left = 0; + Cols = size.Width; + Rows = _largestBufferHeight; + ResizeScreen (); + UpdateOffScreen (); + winChanging = false; + TerminalResized?.Invoke (); + } - MouseFlags mouseFlag = 0; + MouseEvent ToDriverMouse (NetEvents.MouseEvent me) + { + //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}"); - if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) { - mouseFlag |= MouseFlags.Button1Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) { - mouseFlag |= MouseFlags.Button1Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) { - mouseFlag |= MouseFlags.Button1Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) { - mouseFlag |= MouseFlags.Button1DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) { - mouseFlag |= MouseFlags.Button1TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) { - mouseFlag |= MouseFlags.Button2Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) { - mouseFlag |= MouseFlags.Button2Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) { - mouseFlag |= MouseFlags.Button2Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) { - mouseFlag |= MouseFlags.Button2DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) { - mouseFlag |= MouseFlags.Button2TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) { - mouseFlag |= MouseFlags.Button3Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) { - mouseFlag |= MouseFlags.Button3Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) { - mouseFlag |= MouseFlags.Button3Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) { - mouseFlag |= MouseFlags.Button3DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) { - mouseFlag |= MouseFlags.Button3TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) { - mouseFlag |= MouseFlags.WheeledUp; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) { - mouseFlag |= MouseFlags.WheeledDown; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) { - mouseFlag |= MouseFlags.WheeledLeft; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) { - mouseFlag |= MouseFlags.WheeledRight; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) { - mouseFlag |= MouseFlags.Button4Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) { - mouseFlag |= MouseFlags.Button4Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) { - mouseFlag |= MouseFlags.Button4Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) { - mouseFlag |= MouseFlags.Button4DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) { - mouseFlag |= MouseFlags.Button4TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) { - mouseFlag |= MouseFlags.ReportMousePosition; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) { - mouseFlag |= MouseFlags.ButtonShift; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) { - mouseFlag |= MouseFlags.ButtonCtrl; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) { - mouseFlag |= MouseFlags.ButtonAlt; - } + MouseFlags mouseFlag = 0; - return new MouseEvent () { - X = me.Position.X, - Y = me.Position.Y, - Flags = mouseFlag - }; + if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) { + mouseFlag |= MouseFlags.Button1Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) { + mouseFlag |= MouseFlags.Button1Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) { + mouseFlag |= MouseFlags.Button1Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) { + mouseFlag |= MouseFlags.Button1DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) { + mouseFlag |= MouseFlags.Button1TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) { + mouseFlag |= MouseFlags.Button2Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) { + mouseFlag |= MouseFlags.Button2Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) { + mouseFlag |= MouseFlags.Button2Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) { + mouseFlag |= MouseFlags.Button2DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) { + mouseFlag |= MouseFlags.Button2TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) { + mouseFlag |= MouseFlags.Button3Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) { + mouseFlag |= MouseFlags.Button3Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) { + mouseFlag |= MouseFlags.Button3Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) { + mouseFlag |= MouseFlags.Button3DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) { + mouseFlag |= MouseFlags.Button3TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) { + mouseFlag |= MouseFlags.WheeledUp; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) { + mouseFlag |= MouseFlags.WheeledDown; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) { + mouseFlag |= MouseFlags.WheeledLeft; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) { + mouseFlag |= MouseFlags.WheeledRight; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) { + mouseFlag |= MouseFlags.Button4Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) { + mouseFlag |= MouseFlags.Button4Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) { + mouseFlag |= MouseFlags.Button4Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) { + mouseFlag |= MouseFlags.Button4DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) { + mouseFlag |= MouseFlags.Button4TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) { + mouseFlag |= MouseFlags.ReportMousePosition; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) { + mouseFlag |= MouseFlags.ButtonShift; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) { + mouseFlag |= MouseFlags.ButtonCtrl; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) { + mouseFlag |= MouseFlags.ButtonAlt; } + return new MouseEvent () { + X = me.Position.X, + Y = me.Position.Y, + Flags = mouseFlag + }; + } - public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) - { - NetEvents.InputResult input = new NetEvents.InputResult (); - input.EventType = NetEvents.EventType.Key; - input.ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control); - try { - ProcessInput (input); - } catch (OverflowException) { } - } - - public override bool GetColors (int value, out Color foreground, out Color background) - { - bool hasColor = false; - foreground = default; - background = default; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (value & 0xffff)) { - hasColor = true; - background = (Color)(ConsoleColor)(value & 0xffff); - } - if (values.Contains ((value >> 16) & 0xffff)) { - hasColor = true; - foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff); - } - return hasColor; - } + public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) + { + NetEvents.InputResult input = new NetEvents.InputResult (); + input.EventType = NetEvents.EventType.Key; + input.ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control); + + try { + ProcessInput (input); + } catch (OverflowException) { } } + public override bool GetColors (int value, out Color foreground, out Color background) + { + bool hasColor = false; + foreground = default; + background = default; + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains (value & 0xffff)) { + hasColor = true; + background = (Color)(ConsoleColor)(value & 0xffff); + } + if (values.Contains ((value >> 16) & 0xffff)) { + hasColor = true; + foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff); + } + return hasColor; + } +} + +/// +/// Mainloop intended to be used with the .NET System.Console API, and can +/// be used on Windows and Unix, it is cross platform but lacks things like +/// file descriptor monitoring. +/// +/// +/// This implementation is used for NetDriver. +/// +internal class NetMainLoop : IMainLoopDriver { + ManualResetEventSlim _keyReady = new ManualResetEventSlim (false); + ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); + Queue _inputResult = new Queue (); + MainLoop _mainLoop; + CancellationTokenSource _tokenSource = new CancellationTokenSource (); + internal NetEvents _netEvents; + /// - /// Mainloop intended to be used with the .NET System.Console API, and can - /// be used on Windows and Unix, it is cross platform but lacks things like - /// file descriptor monitoring. + /// Invoked when a Key is pressed. + /// + internal Action ProcessInput; + + /// + /// Initializes the class with the console driver. /// /// - /// This implementation is used for NetDriver. + /// Passing a consoleDriver is provided to capture windows resizing. /// - internal class NetMainLoop : IMainLoopDriver { - ManualResetEventSlim _keyReady = new ManualResetEventSlim (false); - ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); - Queue _inputResult = new Queue (); - MainLoop _mainLoop; - CancellationTokenSource _tokenSource = new CancellationTokenSource (); - internal NetEvents _netEvents; - - /// - /// Invoked when a Key is pressed. - /// - internal Action ProcessInput; - - /// - /// Initializes the class with the console driver. - /// - /// - /// Passing a consoleDriver is provided to capture windows resizing. - /// - /// The console driver used by this Net main loop. - public NetMainLoop (ConsoleDriver consoleDriver = null) - { - if (consoleDriver == null) { - throw new ArgumentNullException ("Console driver instance must be provided."); - } - _netEvents = new NetEvents (consoleDriver); + /// The console driver used by this Net main loop. + public NetMainLoop (ConsoleDriver consoleDriver = null) + { + if (consoleDriver == null) { + throw new ArgumentNullException ("Console driver instance must be provided."); } + _netEvents = new NetEvents (consoleDriver); + } - void NetInputHandler () - { - while (true) { - _waitForProbe.Wait (); - _waitForProbe.Reset (); - if (_inputResult.Count == 0) { - _inputResult.Enqueue (_netEvents.ReadConsoleInput ()); - } - try { - while (_inputResult.Peek () == null) { - _inputResult.Dequeue (); - } - if (_inputResult.Count > 0) { - _keyReady.Set (); - } - } catch (InvalidOperationException) { } + void NetInputHandler () + { + while (true) { + _waitForProbe.Wait (); + _waitForProbe.Reset (); + if (_inputResult.Count == 0) { + _inputResult.Enqueue (_netEvents.ReadConsoleInput ()); } + try { + while (_inputResult.Peek () == null) { + _inputResult.Dequeue (); + } + if (_inputResult.Count > 0) { + _keyReady.Set (); + } + } catch (InvalidOperationException) { } } + } - void IMainLoopDriver.Setup (MainLoop mainLoop) - { - _mainLoop = mainLoop; - Task.Run (NetInputHandler); - } - - void IMainLoopDriver.Wakeup () - { - _keyReady.Set (); - } + void IMainLoopDriver.Setup (MainLoop mainLoop) + { + _mainLoop = mainLoop; + Task.Run (NetInputHandler); + } - bool IMainLoopDriver.EventsPending (bool wait) - { - _waitForProbe.Set (); + void IMainLoopDriver.Wakeup () + { + _keyReady.Set (); + } - if (CheckTimers (wait, out var waitTimeout)) { - return true; - } + bool IMainLoopDriver.EventsPending (bool wait) + { + _waitForProbe.Set (); - try { - if (!_tokenSource.IsCancellationRequested) { - _keyReady.Wait (waitTimeout, _tokenSource.Token); - } - } catch (OperationCanceledException) { - return true; - } finally { - _keyReady.Reset (); - } + if (CheckTimers (wait, out var waitTimeout)) { + return true; + } + try { if (!_tokenSource.IsCancellationRequested) { - return _inputResult.Count > 0 || CheckTimers (wait, out _); + _keyReady.Wait (waitTimeout, _tokenSource.Token); } - - _tokenSource.Dispose (); - _tokenSource = new CancellationTokenSource (); + } catch (OperationCanceledException) { return true; + } finally { + _keyReady.Reset (); } - bool CheckTimers (bool wait, out int waitTimeout) - { - long now = DateTime.UtcNow.Ticks; + if (!_tokenSource.IsCancellationRequested) { + return _inputResult.Count > 0 || CheckTimers (wait, out _); + } - if (_mainLoop.timeouts.Count > 0) { - waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); - if (waitTimeout < 0) { - return true; - } - } else { - waitTimeout = -1; - } + _tokenSource.Dispose (); + _tokenSource = new CancellationTokenSource (); + return true; + } - if (!wait) { - waitTimeout = 0; - } + bool CheckTimers (bool wait, out int waitTimeout) + { + long now = DateTime.UtcNow.Ticks; - int ic; - lock (_mainLoop.idleHandlers) { - ic = _mainLoop.idleHandlers.Count; + if (_mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) { + return true; } + } else { + waitTimeout = -1; + } - return ic > 0; + if (!wait) { + waitTimeout = 0; } - void IMainLoopDriver.Iteration () - { - while (_inputResult.Count > 0) { - ProcessInput?.Invoke (_inputResult.Dequeue ().Value); - } + int ic; + lock (_mainLoop.idleHandlers) { + ic = _mainLoop.idleHandlers.Count; + } + + return ic > 0; + } + + void IMainLoopDriver.Iteration () + { + while (_inputResult.Count > 0) { + ProcessInput?.Invoke (_inputResult.Dequeue ().Value); } } } \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 82d13f5d02..f25229d2f5 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1,7 +1,7 @@ // // WindowsDriver.cs: Windows specific driver // -using NStack; +using Rune = System.Text.Rune; using System; using System.Collections.Generic; using System.ComponentModel; @@ -9,614 +9,617 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using System.Buffers; +using System.ComponentModel.Design.Serialization; +using Unix.Terminal; + +namespace Terminal.Gui; + +internal class WindowsConsole { + public const int STD_OUTPUT_HANDLE = -11; + public const int STD_INPUT_HANDLE = -10; + public const int STD_ERROR_HANDLE = -12; + + IntPtr _inputHandle, _outputHandle; + IntPtr _screenBuffer; + readonly uint _originalConsoleMode; + CursorVisibility? _initialCursorVisibility = null; + CursorVisibility? _currentCursorVisibility = null; + CursorVisibility? _pendingCursorVisibility = null; + + public WindowsConsole () + { + _inputHandle = GetStdHandle (STD_INPUT_HANDLE); + _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); + _originalConsoleMode = _consoleMode; + var newConsoleMode = _originalConsoleMode; + newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags); + newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode; + newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput; + _consoleMode = newConsoleMode; + } -namespace Terminal.Gui { - - internal class WindowsConsole { - public const int STD_OUTPUT_HANDLE = -11; - public const int STD_INPUT_HANDLE = -10; - public const int STD_ERROR_HANDLE = -12; - - IntPtr _inputHandle, _outputHandle; - IntPtr _screenBuffer; - readonly uint _originalConsoleMode; - CursorVisibility? _initialCursorVisibility = null; - CursorVisibility? _currentCursorVisibility = null; - CursorVisibility? _pendingCursorVisibility = null; + CharInfo [] _originalStdOutChars; - public WindowsConsole () - { - _inputHandle = GetStdHandle (STD_INPUT_HANDLE); - _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); - _originalConsoleMode = _consoleMode; - var newConsoleMode = _originalConsoleMode; - newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags); - newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode; - newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput; - _consoleMode = newConsoleMode; + public bool WriteToConsole (Size size, CharInfo [] charInfoBuffer, Coord coords, SmallRect window) + { + if (_screenBuffer == IntPtr.Zero) { + ReadFromConsoleOutput (size, coords, ref window); } - CharInfo [] _originalStdOutChars; + return WriteConsoleOutput (_screenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window); + } - public bool WriteToConsole (Size size, CharInfo [] charInfoBuffer, Coord coords, SmallRect window) - { - if (_screenBuffer == IntPtr.Zero) { - ReadFromConsoleOutput (size, coords, ref window); + public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window) + { + _screenBuffer = CreateConsoleScreenBuffer ( + DesiredAccess.GenericRead | DesiredAccess.GenericWrite, + ShareMode.FileShareRead | ShareMode.FileShareWrite, + IntPtr.Zero, + 1, + IntPtr.Zero + ); + if (_screenBuffer == INVALID_HANDLE_VALUE) { + var err = Marshal.GetLastWin32Error (); + + if (err != 0) { + throw new System.ComponentModel.Win32Exception (err); } + } - return WriteConsoleOutput (_screenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window); + if (!_initialCursorVisibility.HasValue && GetCursorVisibility (out CursorVisibility visibility)) { + _initialCursorVisibility = visibility; } - public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window) - { - _screenBuffer = CreateConsoleScreenBuffer ( - DesiredAccess.GenericRead | DesiredAccess.GenericWrite, - ShareMode.FileShareRead | ShareMode.FileShareWrite, - IntPtr.Zero, - 1, - IntPtr.Zero - ); - if (_screenBuffer == INVALID_HANDLE_VALUE) { - var err = Marshal.GetLastWin32Error (); - - if (err != 0) { - throw new System.ComponentModel.Win32Exception (err); - } - } + if (!SetConsoleActiveScreenBuffer (_screenBuffer)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } - if (!_initialCursorVisibility.HasValue && GetCursorVisibility (out CursorVisibility visibility)) { - _initialCursorVisibility = visibility; - } + _originalStdOutChars = new CharInfo [size.Height * size.Width]; - if (!SetConsoleActiveScreenBuffer (_screenBuffer)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } + if (!ReadConsoleOutput (_screenBuffer, _originalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + } - _originalStdOutChars = new CharInfo [size.Height * size.Width]; + public bool SetCursorPosition (Coord position) + { + return SetConsoleCursorPosition (_screenBuffer, position); + } - if (!ReadConsoleOutput (_screenBuffer, _originalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } + public void SetInitialCursorVisibility () + { + if (_initialCursorVisibility.HasValue == false && GetCursorVisibility (out CursorVisibility visibility)) { + _initialCursorVisibility = visibility; } + } - public bool SetCursorPosition (Coord position) - { - return SetConsoleCursorPosition (_screenBuffer, position); + public bool GetCursorVisibility (out CursorVisibility visibility) + { + if (_screenBuffer == IntPtr.Zero) { + visibility = CursorVisibility.Invisible; + return false; } - - public void SetInitialCursorVisibility () - { - if (_initialCursorVisibility.HasValue == false && GetCursorVisibility (out CursorVisibility visibility)) { - _initialCursorVisibility = visibility; + if (!GetConsoleCursorInfo (_screenBuffer, out ConsoleCursorInfo info)) { + var err = Marshal.GetLastWin32Error (); + if (err != 0) { + throw new System.ComponentModel.Win32Exception (err); } + visibility = Gui.CursorVisibility.Default; + + return false; } - public bool GetCursorVisibility (out CursorVisibility visibility) - { - if (_screenBuffer == IntPtr.Zero) { - visibility = CursorVisibility.Invisible; - return false; - } - if (!GetConsoleCursorInfo (_screenBuffer, out ConsoleCursorInfo info)) { - var err = Marshal.GetLastWin32Error (); - if (err != 0) { - throw new System.ComponentModel.Win32Exception (err); - } - visibility = Gui.CursorVisibility.Default; + if (!info.bVisible) { + visibility = CursorVisibility.Invisible; + } else if (info.dwSize > 50) { + visibility = CursorVisibility.Box; + } else { + visibility = CursorVisibility.Underline; + } - return false; - } + return true; + } - if (!info.bVisible) { - visibility = CursorVisibility.Invisible; - } else if (info.dwSize > 50) { - visibility = CursorVisibility.Box; - } else { - visibility = CursorVisibility.Underline; - } + public bool EnsureCursorVisibility () + { + if (_initialCursorVisibility.HasValue && _pendingCursorVisibility.HasValue && SetCursorVisibility (_pendingCursorVisibility.Value)) { + _pendingCursorVisibility = null; return true; } - public bool EnsureCursorVisibility () - { - if (_initialCursorVisibility.HasValue && _pendingCursorVisibility.HasValue && SetCursorVisibility (_pendingCursorVisibility.Value)) { - _pendingCursorVisibility = null; - - return true; - } + return false; + } - return false; + public void ForceRefreshCursorVisibility () + { + if (_currentCursorVisibility.HasValue) { + _pendingCursorVisibility = _currentCursorVisibility; + _currentCursorVisibility = null; } + } - public void ForceRefreshCursorVisibility () - { - if (_currentCursorVisibility.HasValue) { - _pendingCursorVisibility = _currentCursorVisibility; - _currentCursorVisibility = null; - } + public bool SetCursorVisibility (CursorVisibility visibility) + { + if (_initialCursorVisibility.HasValue == false) { + _pendingCursorVisibility = visibility; + + return false; } - public bool SetCursorVisibility (CursorVisibility visibility) - { - if (_initialCursorVisibility.HasValue == false) { - _pendingCursorVisibility = visibility; + if (_currentCursorVisibility.HasValue == false || _currentCursorVisibility.Value != visibility) { + ConsoleCursorInfo info = new ConsoleCursorInfo { + dwSize = (uint)visibility & 0x00FF, + bVisible = ((uint)visibility & 0xFF00) != 0 + }; + if (!SetConsoleCursorInfo (_screenBuffer, ref info)) { return false; } - if (_currentCursorVisibility.HasValue == false || _currentCursorVisibility.Value != visibility) { - ConsoleCursorInfo info = new ConsoleCursorInfo { - dwSize = (uint)visibility & 0x00FF, - bVisible = ((uint)visibility & 0xFF00) != 0 - }; - - if (!SetConsoleCursorInfo (_screenBuffer, ref info)) { - return false; - } + _currentCursorVisibility = visibility; + } - _currentCursorVisibility = visibility; - } + return true; + } - return true; + public void Cleanup () + { + if (_initialCursorVisibility.HasValue) { + SetCursorVisibility (_initialCursorVisibility.Value); } - public void Cleanup () - { - if (_initialCursorVisibility.HasValue) { - SetCursorVisibility (_initialCursorVisibility.Value); - } + SetConsoleOutputWindow (out _); - SetConsoleOutputWindow (out _); + _consoleMode = _originalConsoleMode; + //ContinueListeningForConsoleEvents = false; + if (!SetConsoleActiveScreenBuffer (_outputHandle)) { + var err = Marshal.GetLastWin32Error (); + Console.WriteLine ("Error: {0}", err); + } - _consoleMode = _originalConsoleMode; - //ContinueListeningForConsoleEvents = false; - if (!SetConsoleActiveScreenBuffer (_outputHandle)) { - var err = Marshal.GetLastWin32Error (); - Console.WriteLine ("Error: {0}", err); - } + if (_screenBuffer != IntPtr.Zero) { + CloseHandle (_screenBuffer); + } - if (_screenBuffer != IntPtr.Zero) { - CloseHandle (_screenBuffer); - } + _screenBuffer = IntPtr.Zero; + } - _screenBuffer = IntPtr.Zero; + internal Size GetConsoleBufferWindow (out Point position) + { + if (_screenBuffer == IntPtr.Zero) { + position = Point.Empty; + return Size.Empty; } - internal Size GetConsoleBufferWindow (out Point position) - { - if (_screenBuffer == IntPtr.Zero) { - position = Point.Empty; - return Size.Empty; - } + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + position = Point.Empty; + return Size.Empty; + } + var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, + csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - position = Point.Empty; - return Size.Empty; - } - var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, - csbi.srWindow.Bottom - csbi.srWindow.Top + 1); - position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); + return sz; + } - return sz; + internal Size GetConsoleOutputWindow (out Point position) + { + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } + var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, + csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); - internal Size GetConsoleOutputWindow (out Point position) - { - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, - csbi.srWindow.Bottom - csbi.srWindow.Top + 1); - position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); + return sz; + } + + internal Size SetConsoleWindow (short cols, short rows) + { + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + var maxWinSize = GetLargestConsoleWindowSize (_screenBuffer); + var newCols = Math.Min (cols, maxWinSize.X); + var newRows = Math.Min (rows, maxWinSize.Y); + csbi.dwSize = new Coord (newCols, Math.Max (newRows, (short)1)); + csbi.srWindow = new SmallRect (0, 0, newCols, newRows); + csbi.dwMaximumWindowSize = new Coord (newCols, newRows); + if (!SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0)); + if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { + //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + return new Size (cols, rows); + } + SetConsoleOutputWindow (csbi); + return new Size (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1); + } - return sz; + void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi) + { + if (_screenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } + } - internal Size SetConsoleWindow (short cols, short rows) - { - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - var maxWinSize = GetLargestConsoleWindowSize (_screenBuffer); - var newCols = Math.Min (cols, maxWinSize.X); - var newRows = Math.Min (rows, maxWinSize.Y); - csbi.dwSize = new Coord (newCols, Math.Max (newRows, (short)1)); - csbi.srWindow = new SmallRect (0, 0, newCols, newRows); - csbi.dwMaximumWindowSize = new Coord (newCols, newRows); - if (!SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0)); - if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { - //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - return new Size (cols, rows); - } - SetConsoleOutputWindow (csbi); - return new Size (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1); + internal Size SetConsoleOutputWindow (out Point position) + { + if (_screenBuffer == IntPtr.Zero) { + position = Point.Empty; + return Size.Empty; } - void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi) - { - if (_screenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, + Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0)); + position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); + SetConsoleOutputWindow (csbi); + var winRect = new SmallRect (0, 0, (short)(sz.Width - 1), (short)Math.Max (sz.Height - 1, 0)); + if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } - internal Size SetConsoleOutputWindow (out Point position) - { - if (_screenBuffer == IntPtr.Zero) { - position = Point.Empty; - return Size.Empty; - } + return sz; + } - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, - Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0)); - position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); - SetConsoleOutputWindow (csbi); - var winRect = new SmallRect (0, 0, (short)(sz.Width - 1), (short)Math.Max (sz.Height - 1, 0)); - if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } + //bool ContinueListeningForConsoleEvents = true; - return sz; + uint _consoleMode { + get { + GetConsoleMode (_inputHandle, out uint v); + return v; } - - //bool ContinueListeningForConsoleEvents = true; - - uint _consoleMode { - get { - GetConsoleMode (_inputHandle, out uint v); - return v; - } - set { - SetConsoleMode (_inputHandle, value); - } + set { + SetConsoleMode (_inputHandle, value); } + } - [Flags] - public enum ConsoleModes : uint { - EnableProcessedInput = 1, - EnableMouseInput = 16, - EnableQuickEditMode = 64, - EnableExtendedFlags = 128, - } - - [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] - public struct KeyEventRecord { - [FieldOffset (0), MarshalAs (UnmanagedType.Bool)] - public bool bKeyDown; - [FieldOffset (4), MarshalAs (UnmanagedType.U2)] - public ushort wRepeatCount; - [FieldOffset (6), MarshalAs (UnmanagedType.U2)] - public ushort wVirtualKeyCode; - [FieldOffset (8), MarshalAs (UnmanagedType.U2)] - public ushort wVirtualScanCode; - [FieldOffset (10)] - public char UnicodeChar; - [FieldOffset (12), MarshalAs (UnmanagedType.U4)] - public ControlKeyState dwControlKeyState; - } - - [Flags] - public enum ButtonState { - Button1Pressed = 1, - Button2Pressed = 4, - Button3Pressed = 8, - Button4Pressed = 16, - RightmostButtonPressed = 2 - } - - [Flags] - public enum ControlKeyState { - RightAltPressed = 1, - LeftAltPressed = 2, - RightControlPressed = 4, - LeftControlPressed = 8, - ShiftPressed = 16, - NumlockOn = 32, - ScrolllockOn = 64, - CapslockOn = 128, - EnhancedKey = 256 - } - - [Flags] - public enum EventFlags { - MouseMoved = 1, - DoubleClick = 2, - MouseWheeled = 4, - MouseHorizontalWheeled = 8 - } - - [StructLayout (LayoutKind.Explicit)] - public struct MouseEventRecord { - [FieldOffset (0)] - public Coord MousePosition; - [FieldOffset (4)] - public ButtonState ButtonState; - [FieldOffset (8)] - public ControlKeyState ControlKeyState; - [FieldOffset (12)] - public EventFlags EventFlags; - - public override string ToString () - { - return $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}"; - } - } + [Flags] + public enum ConsoleModes : uint { + EnableProcessedInput = 1, + EnableMouseInput = 16, + EnableQuickEditMode = 64, + EnableExtendedFlags = 128, + } - public struct WindowBufferSizeRecord { - public Coord _size; + [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] + public struct KeyEventRecord { + [FieldOffset (0), MarshalAs (UnmanagedType.Bool)] + public bool bKeyDown; + [FieldOffset (4), MarshalAs (UnmanagedType.U2)] + public ushort wRepeatCount; + [FieldOffset (6), MarshalAs (UnmanagedType.U2)] + public ushort wVirtualKeyCode; + [FieldOffset (8), MarshalAs (UnmanagedType.U2)] + public ushort wVirtualScanCode; + [FieldOffset (10)] + public char UnicodeChar; + [FieldOffset (12), MarshalAs (UnmanagedType.U4)] + public ControlKeyState dwControlKeyState; + } - public WindowBufferSizeRecord (short x, short y) - { - _size = new Coord (x, y); - } + [Flags] + public enum ButtonState { + Button1Pressed = 1, + Button2Pressed = 4, + Button3Pressed = 8, + Button4Pressed = 16, + RightmostButtonPressed = 2 + } - public override string ToString () => $"[WindowBufferSize{_size}"; - } - - [StructLayout (LayoutKind.Sequential)] - public struct MenuEventRecord { - public uint dwCommandId; - } - - [StructLayout (LayoutKind.Sequential)] - public struct FocusEventRecord { - public uint bSetFocus; - } - - public enum EventType : ushort { - Focus = 0x10, - Key = 0x1, - Menu = 0x8, - Mouse = 2, - WindowBufferSize = 4 - } - - [StructLayout (LayoutKind.Explicit)] - public struct InputRecord { - [FieldOffset (0)] - public EventType EventType; - [FieldOffset (4)] - public KeyEventRecord KeyEvent; - [FieldOffset (4)] - public MouseEventRecord MouseEvent; - [FieldOffset (4)] - public WindowBufferSizeRecord WindowBufferSizeEvent; - [FieldOffset (4)] - public MenuEventRecord MenuEvent; - [FieldOffset (4)] - public FocusEventRecord FocusEvent; - - public override string ToString () - { - switch (EventType) { - case EventType.Focus: - return FocusEvent.ToString (); - case EventType.Key: - return KeyEvent.ToString (); - case EventType.Menu: - return MenuEvent.ToString (); - case EventType.Mouse: - return MouseEvent.ToString (); - case EventType.WindowBufferSize: - return WindowBufferSizeEvent.ToString (); - default: - return "Unknown event type: " + EventType; - } - } - }; + [Flags] + public enum ControlKeyState { + RightAltPressed = 1, + LeftAltPressed = 2, + RightControlPressed = 4, + LeftControlPressed = 8, + ShiftPressed = 16, + NumlockOn = 32, + ScrolllockOn = 64, + CapslockOn = 128, + EnhancedKey = 256 + } - [Flags] - enum ShareMode : uint { - FileShareRead = 1, - FileShareWrite = 2, - } + [Flags] + public enum EventFlags { + MouseMoved = 1, + DoubleClick = 2, + MouseWheeled = 4, + MouseHorizontalWheeled = 8 + } - [Flags] - enum DesiredAccess : uint { - GenericRead = 2147483648, - GenericWrite = 1073741824, + [StructLayout (LayoutKind.Explicit)] + public struct MouseEventRecord { + [FieldOffset (0)] + public Coord MousePosition; + [FieldOffset (4)] + public ButtonState ButtonState; + [FieldOffset (8)] + public ControlKeyState ControlKeyState; + [FieldOffset (12)] + public EventFlags EventFlags; + + public override string ToString () + { + return $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}"; } + } - [StructLayout (LayoutKind.Sequential)] - public struct ConsoleScreenBufferInfo { - public Coord dwSize; - public Coord dwCursorPosition; - public ushort wAttributes; - public SmallRect srWindow; - public Coord dwMaximumWindowSize; + public struct WindowBufferSizeRecord { + public Coord _size; + + public WindowBufferSizeRecord (short x, short y) + { + _size = new Coord (x, y); } - [StructLayout (LayoutKind.Sequential)] - public struct Coord { - public short X; - public short Y; + public override string ToString () => $"[WindowBufferSize{_size}"; + } - public Coord (short x, short y) - { - X = x; - Y = y; - } - public override string ToString () => $"({X},{Y})"; - }; + [StructLayout (LayoutKind.Sequential)] + public struct MenuEventRecord { + public uint dwCommandId; + } - [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] - public struct CharUnion { - [FieldOffset (0)] public char UnicodeChar; - [FieldOffset (0)] public byte AsciiChar; - } + [StructLayout (LayoutKind.Sequential)] + public struct FocusEventRecord { + public uint bSetFocus; + } - [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] - public struct CharInfo { - [FieldOffset (0)] public CharUnion Char; - [FieldOffset (2)] public ushort Attributes; - } + public enum EventType : ushort { + Focus = 0x10, + Key = 0x1, + Menu = 0x8, + Mouse = 2, + WindowBufferSize = 4 + } - [StructLayout (LayoutKind.Sequential)] - public struct SmallRect { - public short Left; - public short Top; - public short Right; - public short Bottom; + [StructLayout (LayoutKind.Explicit)] + public struct InputRecord { + [FieldOffset (0)] + public EventType EventType; + [FieldOffset (4)] + public KeyEventRecord KeyEvent; + [FieldOffset (4)] + public MouseEventRecord MouseEvent; + [FieldOffset (4)] + public WindowBufferSizeRecord WindowBufferSizeEvent; + [FieldOffset (4)] + public MenuEventRecord MenuEvent; + [FieldOffset (4)] + public FocusEventRecord FocusEvent; + + public override string ToString () + { + switch (EventType) { + case EventType.Focus: + return FocusEvent.ToString (); + case EventType.Key: + return KeyEvent.ToString (); + case EventType.Menu: + return MenuEvent.ToString (); + case EventType.Mouse: + return MouseEvent.ToString (); + case EventType.WindowBufferSize: + return WindowBufferSizeEvent.ToString (); + default: + return "Unknown event type: " + EventType; + } + } + }; + + [Flags] + enum ShareMode : uint { + FileShareRead = 1, + FileShareWrite = 2, + } - public SmallRect (short left, short top, short right, short bottom) - { - Left = left; - Top = top; - Right = right; - Bottom = bottom; - } + [Flags] + enum DesiredAccess : uint { + GenericRead = 2147483648, + GenericWrite = 1073741824, + } - public static void MakeEmpty (ref SmallRect rect) - { - rect.Left = -1; - } + [StructLayout (LayoutKind.Sequential)] + public struct ConsoleScreenBufferInfo { + public Coord dwSize; + public Coord dwCursorPosition; + public ushort wAttributes; + public SmallRect srWindow; + public Coord dwMaximumWindowSize; + } - public static void Update (ref SmallRect rect, short col, short row) - { - if (rect.Left == -1) { - rect.Left = rect.Right = col; - rect.Bottom = rect.Top = row; - return; - } - if (col >= rect.Left && col <= rect.Right && row >= rect.Top && row <= rect.Bottom) - return; - if (col < rect.Left) - rect.Left = col; - if (col > rect.Right) - rect.Right = col; - if (row < rect.Top) - rect.Top = row; - if (row > rect.Bottom) - rect.Bottom = row; - } + [StructLayout (LayoutKind.Sequential)] + public struct Coord { + public short X; + public short Y; - public override string ToString () - { - return $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}"; - } + public Coord (short x, short y) + { + X = x; + Y = y; } + public override string ToString () => $"({X},{Y})"; + }; - [StructLayout (LayoutKind.Sequential)] - public struct ConsoleKeyInfoEx { - public ConsoleKeyInfo ConsoleKeyInfo; - public bool CapsLock; - public bool NumLock; - public bool Scrolllock; + [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] + public struct CharUnion { + [FieldOffset (0)] public char UnicodeChar; + [FieldOffset (0)] public byte AsciiChar; + } - public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock) - { - ConsoleKeyInfo = consoleKeyInfo; - CapsLock = capslock; - NumLock = numlock; - Scrolllock = scrolllock; - } - } + [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] + public struct CharInfo { + [FieldOffset (0)] public CharUnion Char; + [FieldOffset (2)] public ushort Attributes; + } - [DllImport ("kernel32.dll", SetLastError = true)] - static extern IntPtr GetStdHandle (int nStdHandle); + [StructLayout (LayoutKind.Sequential)] + public struct SmallRect { + public short Left; + public short Top; + public short Right; + public short Bottom; - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool CloseHandle (IntPtr handle); + public SmallRect (short left, short top, short right, short bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } - [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)] - public static extern bool ReadConsoleInput ( - IntPtr hConsoleInput, - IntPtr lpBuffer, - uint nLength, - out uint lpNumberOfEventsRead); + public static void MakeEmpty (ref SmallRect rect) + { + rect.Left = -1; + } - [DllImport ("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool ReadConsoleOutput ( - IntPtr hConsoleOutput, - [Out] CharInfo [] lpBuffer, - Coord dwBufferSize, - Coord dwBufferCoord, - ref SmallRect lpReadRegion - ); + public static void Update (ref SmallRect rect, short col, short row) + { + if (rect.Left == -1) { + rect.Left = rect.Right = col; + rect.Bottom = rect.Top = row; + return; + } + if (col >= rect.Left && col <= rect.Right && row >= rect.Top && row <= rect.Bottom) + return; + if (col < rect.Left) + rect.Left = col; + if (col > rect.Right) + rect.Right = col; + if (row < rect.Top) + rect.Top = row; + if (row > rect.Bottom) + rect.Bottom = row; + } - // TODO: This API is obsolete. See https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput - [DllImport ("kernel32.dll", EntryPoint = "WriteConsoleOutput", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool WriteConsoleOutput ( - IntPtr hConsoleOutput, - CharInfo [] lpBuffer, - Coord dwBufferSize, - Coord dwBufferCoord, - ref SmallRect lpWriteRegion - ); + public override string ToString () + { + return $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}"; + } + } - [DllImport ("kernel32.dll")] - static extern bool SetConsoleCursorPosition (IntPtr hConsoleOutput, Coord dwCursorPosition); + [StructLayout (LayoutKind.Sequential)] + public struct ConsoleKeyInfoEx { + public ConsoleKeyInfo ConsoleKeyInfo; + public bool CapsLock; + public bool NumLock; + public bool Scrolllock; - [StructLayout (LayoutKind.Sequential)] - public struct ConsoleCursorInfo { - public uint dwSize; - public bool bVisible; + public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock) + { + ConsoleKeyInfo = consoleKeyInfo; + CapsLock = capslock; + NumLock = numlock; + Scrolllock = scrolllock; } + } - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleCursorInfo (IntPtr hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo); + [DllImport ("kernel32.dll", SetLastError = true)] + static extern IntPtr GetStdHandle (int nStdHandle); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool CloseHandle (IntPtr handle); + + [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)] + public static extern bool ReadConsoleInput ( + IntPtr hConsoleInput, + IntPtr lpBuffer, + uint nLength, + out uint lpNumberOfEventsRead); + + [DllImport ("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool ReadConsoleOutput ( + IntPtr hConsoleOutput, + [Out] CharInfo [] lpBuffer, + Coord dwBufferSize, + Coord dwBufferCoord, + ref SmallRect lpReadRegion + ); + + // TODO: This API is obsolete. See https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput + [DllImport ("kernel32.dll", EntryPoint = "WriteConsoleOutputW", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool WriteConsoleOutput ( + IntPtr hConsoleOutput, + CharInfo [] lpBuffer, + Coord dwBufferSize, + Coord dwBufferCoord, + ref SmallRect lpWriteRegion + ); + + [DllImport ("kernel32.dll")] + static extern bool SetConsoleCursorPosition (IntPtr hConsoleOutput, Coord dwCursorPosition); + + [StructLayout (LayoutKind.Sequential)] + public struct ConsoleCursorInfo { + public uint dwSize; + public bool bVisible; + } - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool GetConsoleCursorInfo (IntPtr hConsoleOutput, out ConsoleCursorInfo lpConsoleCursorInfo); + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleCursorInfo (IntPtr hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo); - [DllImport ("kernel32.dll")] - static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode); + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool GetConsoleCursorInfo (IntPtr hConsoleOutput, out ConsoleCursorInfo lpConsoleCursorInfo); - [DllImport ("kernel32.dll")] - static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode); + [DllImport ("kernel32.dll")] + static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode); - [DllImport ("kernel32.dll", SetLastError = true)] - static extern IntPtr CreateConsoleScreenBuffer ( - DesiredAccess dwDesiredAccess, - ShareMode dwShareMode, - IntPtr secutiryAttributes, - uint flags, - IntPtr screenBufferData - ); + [DllImport ("kernel32.dll")] + static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode); - internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr (-1); + [DllImport ("kernel32.dll", SetLastError = true)] + static extern IntPtr CreateConsoleScreenBuffer ( + DesiredAccess dwDesiredAccess, + ShareMode dwShareMode, + IntPtr secutiryAttributes, + uint flags, + IntPtr screenBufferData + ); - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleActiveScreenBuffer (IntPtr Handle); + internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr (-1); - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents); + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleActiveScreenBuffer (IntPtr Handle); - public InputRecord [] ReadConsoleInput () - { - const int bufferSize = 1; - var pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * bufferSize); - try { - ReadConsoleInput (_inputHandle, pRecord, bufferSize, - out var numberEventsRead); + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents); - return numberEventsRead == 0 - ? null - : new [] { Marshal.PtrToStructure (pRecord) }; - } catch (Exception) { - return null; - } finally { - Marshal.FreeHGlobal (pRecord); - } + public InputRecord [] ReadConsoleInput () + { + const int bufferSize = 1; + var pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * bufferSize); + try { + ReadConsoleInput (_inputHandle, pRecord, bufferSize, + out var numberEventsRead); + + return numberEventsRead == 0 + ? null + : new [] { Marshal.PtrToStructure (pRecord) }; + } catch (Exception) { + return null; + } finally { + Marshal.FreeHGlobal (pRecord); } + } #if false // Not needed on the constructor. Perhaps could be used on resizing. To study. [DllImport ("kernel32.dll", ExactSpelling = true)] @@ -636,1377 +639,1457 @@ internal void ShowWindow (int state) ShowWindow (thisConsole, state); } #endif - // See: https://github.com/gui-cs/Terminal.Gui/issues/357 - - [StructLayout (LayoutKind.Sequential)] - public struct CONSOLE_SCREEN_BUFFER_INFOEX { - public uint cbSize; - public Coord dwSize; - public Coord dwCursorPosition; - public ushort wAttributes; - public SmallRect srWindow; - public Coord dwMaximumWindowSize; - public ushort wPopupAttributes; - public bool bFullscreenSupported; - - [MarshalAs (UnmanagedType.ByValArray, SizeConst = 16)] - public COLORREF [] ColorTable; - } - - [StructLayout (LayoutKind.Explicit, Size = 4)] - public struct COLORREF { - public COLORREF (byte r, byte g, byte b) - { - Value = 0; - R = r; - G = g; - B = b; - } - - public COLORREF (uint value) - { - R = 0; - G = 0; - B = 0; - Value = value & 0x00FFFFFF; - } + // See: https://github.com/gui-cs/Terminal.Gui/issues/357 + + [StructLayout (LayoutKind.Sequential)] + public struct CONSOLE_SCREEN_BUFFER_INFOEX { + public uint cbSize; + public Coord dwSize; + public Coord dwCursorPosition; + public ushort wAttributes; + public SmallRect srWindow; + public Coord dwMaximumWindowSize; + public ushort wPopupAttributes; + public bool bFullscreenSupported; + + [MarshalAs (UnmanagedType.ByValArray, SizeConst = 16)] + public COLORREF [] ColorTable; + } - [FieldOffset (0)] - public byte R; - [FieldOffset (1)] - public byte G; - [FieldOffset (2)] - public byte B; + [StructLayout (LayoutKind.Explicit, Size = 4)] + public struct COLORREF { + public COLORREF (byte r, byte g, byte b) + { + Value = 0; + R = r; + G = g; + B = b; + } - [FieldOffset (0)] - public uint Value; + public COLORREF (uint value) + { + R = 0; + G = 0; + B = 0; + Value = value & 0x00FFFFFF; } - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool GetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi); + [FieldOffset (0)] + public byte R; + [FieldOffset (1)] + public byte G; + [FieldOffset (2)] + public byte B; - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX ConsoleScreenBufferInfo); + [FieldOffset (0)] + public uint Value; + } - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleWindowInfo ( - IntPtr hConsoleOutput, - bool bAbsolute, - [In] ref SmallRect lpConsoleWindow); + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool GetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi); - [DllImport ("kernel32.dll", SetLastError = true)] - static extern Coord GetLargestConsoleWindowSize ( - IntPtr hConsoleOutput); - } + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX ConsoleScreenBufferInfo); - internal class WindowsDriver : ConsoleDriver { - WindowsConsole.CharInfo [] _outputBuffer; - WindowsConsole.SmallRect _damageRegion; - Action _keyHandler; - Action _keyDownHandler; - Action _keyUpHandler; - Action _mouseHandler; + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleWindowInfo ( + IntPtr hConsoleOutput, + bool bAbsolute, + [In] ref SmallRect lpConsoleWindow); - public WindowsConsole WinConsole { get; private set; } + [DllImport ("kernel32.dll", SetLastError = true)] + static extern Coord GetLargestConsoleWindowSize ( + IntPtr hConsoleOutput); +} - public WindowsDriver () - { - WinConsole = new WindowsConsole (); - Clipboard = new WindowsClipboard (); - } +internal class WindowsDriver : ConsoleDriver { + WindowsConsole.CharInfo [] _outputBuffer; + WindowsConsole.SmallRect _damageRegion; + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; - public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) - { - _keyHandler = keyHandler; - _keyDownHandler = keyDownHandler; - _keyUpHandler = keyUpHandler; - _mouseHandler = mouseHandler; + public WindowsConsole WinConsole { get; private set; } - var mLoop = mainLoop.Driver as WindowsMainLoop; + public WindowsDriver () + { + WinConsole = new WindowsConsole (); + Clipboard = new WindowsClipboard (); + } - mLoop.ProcessInput = (e) => ProcessInput (e); + public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) + { + _keyHandler = keyHandler; + _keyDownHandler = keyDownHandler; + _keyUpHandler = keyUpHandler; + _mouseHandler = mouseHandler; - mLoop.WinChanged = (s, e) => { - ChangeWin (e.Size); - }; - } + var mLoop = mainLoop.Driver as WindowsMainLoop; - private void ChangeWin (Size e) - { - if (!EnableConsoleScrolling) { - var w = e.Width; - if (w == Cols - 3 && e.Height < Rows) { - w += 3; - } - var newSize = WinConsole.SetConsoleWindow ( - (short)Math.Max (w, 16), (short)Math.Max (e.Height, 0)); - Left = 0; - Top = 0; - Cols = newSize.Width; - Rows = newSize.Height; - ResizeScreen (); - UpdateOffScreen (); - TerminalResized.Invoke (); - } + mLoop.ProcessInput = (e) => ProcessInput (e); + + mLoop.WinChanged = (s, e) => { + ChangeWin (e.Size); + }; + } + + private void ChangeWin (Size e) + { + if (!EnableConsoleScrolling) { + var w = e.Width; + if (w == Cols - 3 && e.Height < Rows) { + w += 3; + } + var newSize = WinConsole.SetConsoleWindow ( + (short)Math.Max (w, 16), (short)Math.Max (e.Height, 0)); + Left = 0; + Top = 0; + Cols = newSize.Width; + Rows = newSize.Height; + ResizeScreen (); + UpdateOffScreen (); + TerminalResized.Invoke (); } + } - void ProcessInput (WindowsConsole.InputRecord inputEvent) - { - switch (inputEvent.EventType) { - case WindowsConsole.EventType.Key: - var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet; - if (fromPacketKey) { - inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent); + void ProcessInput (WindowsConsole.InputRecord inputEvent) + { + switch (inputEvent.EventType) { + case WindowsConsole.EventType.Key: + var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet; + if (fromPacketKey) { + inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent); + } + var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent)); + //var ke = inputEvent.KeyEvent; + //System.Diagnostics.Debug.WriteLine ($"fromPacketKey: {fromPacketKey}"); + //if (ke.UnicodeChar == '\0') { + // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 0'\\0'"); + //} else if (ke.UnicodeChar == 13) { + // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 13'\\n'"); + //} else { + // System.Diagnostics.Debug.WriteLine ($"UnicodeChar: {(uint)ke.UnicodeChar}'{ke.UnicodeChar}'"); + //} + //System.Diagnostics.Debug.WriteLine ($"bKeyDown: {ke.bKeyDown}"); + //System.Diagnostics.Debug.WriteLine ($"dwControlKeyState: {ke.dwControlKeyState}"); + //System.Diagnostics.Debug.WriteLine ($"wRepeatCount: {ke.wRepeatCount}"); + //System.Diagnostics.Debug.WriteLine ($"wVirtualKeyCode: {ke.wVirtualKeyCode}"); + //System.Diagnostics.Debug.WriteLine ($"wVirtualScanCode: {ke.wVirtualScanCode}"); + + if (map == (Key)0xffffffff) { + KeyEvent key = new KeyEvent (); + + // Shift = VK_SHIFT = 0x10 + // Ctrl = VK_CONTROL = 0x11 + // Alt = VK_MENU = 0x12 + + if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.CapslockOn)) { + inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn; } - var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent)); - //var ke = inputEvent.KeyEvent; - //System.Diagnostics.Debug.WriteLine ($"fromPacketKey: {fromPacketKey}"); - //if (ke.UnicodeChar == '\0') { - // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 0'\\0'"); - //} else if (ke.UnicodeChar == 13) { - // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 13'\\n'"); - //} else { - // System.Diagnostics.Debug.WriteLine ($"UnicodeChar: {(uint)ke.UnicodeChar}'{ke.UnicodeChar}'"); - //} - //System.Diagnostics.Debug.WriteLine ($"bKeyDown: {ke.bKeyDown}"); - //System.Diagnostics.Debug.WriteLine ($"dwControlKeyState: {ke.dwControlKeyState}"); - //System.Diagnostics.Debug.WriteLine ($"wRepeatCount: {ke.wRepeatCount}"); - //System.Diagnostics.Debug.WriteLine ($"wVirtualKeyCode: {ke.wVirtualKeyCode}"); - //System.Diagnostics.Debug.WriteLine ($"wVirtualScanCode: {ke.wVirtualScanCode}"); - - if (map == (Key)0xffffffff) { - KeyEvent key = new KeyEvent (); - - // Shift = VK_SHIFT = 0x10 - // Ctrl = VK_CONTROL = 0x11 - // Alt = VK_MENU = 0x12 - - if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.CapslockOn)) { - inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn; - } - if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ScrolllockOn)) { - inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn; - } + if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ScrolllockOn)) { + inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn; + } - if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.NumlockOn)) { - inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn; - } + if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.NumlockOn)) { + inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn; + } - switch (inputEvent.KeyEvent.dwControlKeyState) { - case WindowsConsole.ControlKeyState.RightAltPressed: - case WindowsConsole.ControlKeyState.RightAltPressed | - WindowsConsole.ControlKeyState.LeftControlPressed | - WindowsConsole.ControlKeyState.EnhancedKey: - case WindowsConsole.ControlKeyState.EnhancedKey: - key = new KeyEvent (Key.CtrlMask | Key.AltMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.LeftAltPressed: - key = new KeyEvent (Key.AltMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.RightControlPressed: - case WindowsConsole.ControlKeyState.LeftControlPressed: - key = new KeyEvent (Key.CtrlMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.ShiftPressed: + switch (inputEvent.KeyEvent.dwControlKeyState) { + case WindowsConsole.ControlKeyState.RightAltPressed: + case WindowsConsole.ControlKeyState.RightAltPressed | + WindowsConsole.ControlKeyState.LeftControlPressed | + WindowsConsole.ControlKeyState.EnhancedKey: + case WindowsConsole.ControlKeyState.EnhancedKey: + key = new KeyEvent (Key.CtrlMask | Key.AltMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.LeftAltPressed: + key = new KeyEvent (Key.AltMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.RightControlPressed: + case WindowsConsole.ControlKeyState.LeftControlPressed: + key = new KeyEvent (Key.CtrlMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.ShiftPressed: + key = new KeyEvent (Key.ShiftMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.NumlockOn: + break; + case WindowsConsole.ControlKeyState.ScrolllockOn: + break; + case WindowsConsole.ControlKeyState.CapslockOn: + break; + default: + switch (inputEvent.KeyEvent.wVirtualKeyCode) { + case 0x10: key = new KeyEvent (Key.ShiftMask, _keyModifiers); break; - case WindowsConsole.ControlKeyState.NumlockOn: - break; - case WindowsConsole.ControlKeyState.ScrolllockOn: + case 0x11: + key = new KeyEvent (Key.CtrlMask, _keyModifiers); break; - case WindowsConsole.ControlKeyState.CapslockOn: + case 0x12: + key = new KeyEvent (Key.AltMask, _keyModifiers); break; default: - switch (inputEvent.KeyEvent.wVirtualKeyCode) { - case 0x10: - key = new KeyEvent (Key.ShiftMask, _keyModifiers); - break; - case 0x11: - key = new KeyEvent (Key.CtrlMask, _keyModifiers); - break; - case 0x12: - key = new KeyEvent (Key.AltMask, _keyModifiers); - break; - default: - key = new KeyEvent (Key.Unknown, _keyModifiers); - break; - } + key = new KeyEvent (Key.Unknown, _keyModifiers); break; } + break; + } - if (inputEvent.KeyEvent.bKeyDown) { - _keyDownHandler (key); - } else { - _keyUpHandler (key); - } + if (inputEvent.KeyEvent.bKeyDown) { + _keyDownHandler (key); } else { - if (inputEvent.KeyEvent.bKeyDown) { - // May occurs using SendKeys - if (_keyModifiers == null) { - _keyModifiers = new KeyModifiers (); - } - // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event - _keyDownHandler (new KeyEvent (map, _keyModifiers)); - _keyHandler (new KeyEvent (map, _keyModifiers)); - } else { - _keyUpHandler (new KeyEvent (map, _keyModifiers)); - } + _keyUpHandler (key); } - if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) { - _keyModifiers = null; + } else { + if (inputEvent.KeyEvent.bKeyDown) { + // May occurs using SendKeys + if (_keyModifiers == null) { + _keyModifiers = new KeyModifiers (); + } + // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event + _keyDownHandler (new KeyEvent (map, _keyModifiers)); + _keyHandler (new KeyEvent (map, _keyModifiers)); + } else { + _keyUpHandler (new KeyEvent (map, _keyModifiers)); } - break; + } + if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) { + _keyModifiers = null; + } + break; - case WindowsConsole.EventType.Mouse: - var me = ToDriverMouse (inputEvent.MouseEvent); - _mouseHandler (me); - if (_processButtonClick) { - _mouseHandler ( - new MouseEvent () { - X = me.X, - Y = me.Y, - Flags = ProcessButtonClick (inputEvent.MouseEvent) - }); - } + case WindowsConsole.EventType.Mouse: + var me = ToDriverMouse (inputEvent.MouseEvent); + _mouseHandler (me); + if (_processButtonClick) { + _mouseHandler ( + new MouseEvent () { + X = me.X, + Y = me.Y, + Flags = ProcessButtonClick (inputEvent.MouseEvent) + }); + } + break; + + case WindowsConsole.EventType.WindowBufferSize: + var winSize = WinConsole.GetConsoleBufferWindow (out Point pos); + Left = pos.X; + Top = pos.Y; + Cols = inputEvent.WindowBufferSizeEvent._size.X; + if (EnableConsoleScrolling) { + Rows = Math.Max (inputEvent.WindowBufferSizeEvent._size.Y, Rows); + } else { + Rows = inputEvent.WindowBufferSizeEvent._size.Y; + } + //System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}"); + ResizeScreen (); + UpdateOffScreen (); + TerminalResized?.Invoke (); + break; + + case WindowsConsole.EventType.Focus: + break; + } + } + + WindowsConsole.ButtonState? _lastMouseButtonPressed = null; + bool _isButtonPressed = false; + bool _isButtonReleased = false; + bool _isButtonDoubleClicked = false; + Point? _point; + Point _pointMove; + //int _buttonPressedCount; + bool _isOneFingerDoubleClicked = false; + bool _processButtonClick; + + MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) + { + MouseFlags mouseFlag = MouseFlags.AllEvents; + + //System.Diagnostics.Debug.WriteLine ( + // $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}"); + + if (_isButtonDoubleClicked || _isOneFingerDoubleClicked) { + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); + return false; + }); + } + + // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button. + // This will tell when a mouse button is pressed. When the button is released this event will + // be fired with it's bit set to 0. So when the button is up ButtonState will be 0. + // To map to the correct driver events we save the last pressed mouse button so we can + // map to the correct clicked event. + if ((_lastMouseButtonPressed != null || _isButtonReleased) && mouseEvent.ButtonState != 0) { + _lastMouseButtonPressed = null; + //isButtonPressed = false; + _isButtonReleased = false; + } + + var p = new Point () { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y + }; + + if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && _lastMouseButtonPressed == null && !_isButtonDoubleClicked) || + (_lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.MouseMoved) && + mouseEvent.ButtonState != 0 && !_isButtonReleased && !_isButtonDoubleClicked)) { + switch (mouseEvent.ButtonState) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Pressed; break; - case WindowsConsole.EventType.WindowBufferSize: - var winSize = WinConsole.GetConsoleBufferWindow (out Point pos); - Left = pos.X; - Top = pos.Y; - Cols = inputEvent.WindowBufferSizeEvent._size.X; - if (EnableConsoleScrolling) { - Rows = Math.Max (inputEvent.WindowBufferSizeEvent._size.Y, Rows); - } else { - Rows = inputEvent.WindowBufferSizeEvent._size.Y; - } - //System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}"); - ResizeScreen (); - UpdateOffScreen (); - TerminalResized?.Invoke (); + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Pressed; break; - case WindowsConsole.EventType.Focus: + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Pressed; break; } - } - WindowsConsole.ButtonState? _lastMouseButtonPressed = null; - bool _isButtonPressed = false; - bool _isButtonReleased = false; - bool _isButtonDoubleClicked = false; - Point? _point; - Point _pointMove; - //int _buttonPressedCount; - bool _isOneFingerDoubleClicked = false; - bool _processButtonClick; - - MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) - { - MouseFlags mouseFlag = MouseFlags.AllEvents; + if (_point == null) { + _point = p; + } - //System.Diagnostics.Debug.WriteLine ( - // $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}"); + if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { + mouseFlag |= MouseFlags.ReportMousePosition; + _isButtonReleased = false; + _processButtonClick = false; + } + _lastMouseButtonPressed = mouseEvent.ButtonState; + _isButtonPressed = true; - if (_isButtonDoubleClicked || _isOneFingerDoubleClicked) { + if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) { Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); + Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag)); return false; }); } - // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button. - // This will tell when a mouse button is pressed. When the button is released this event will - // be fired with it's bit set to 0. So when the button is up ButtonState will be 0. - // To map to the correct driver events we save the last pressed mouse button so we can - // map to the correct clicked event. - if ((_lastMouseButtonPressed != null || _isButtonReleased) && mouseEvent.ButtonState != 0) { - _lastMouseButtonPressed = null; - //isButtonPressed = false; - _isButtonReleased = false; - } - - var p = new Point () { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y - }; - - if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && _lastMouseButtonPressed == null && !_isButtonDoubleClicked) || - (_lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.MouseMoved) && - mouseEvent.ButtonState != 0 && !_isButtonReleased && !_isButtonDoubleClicked)) { - switch (mouseEvent.ButtonState) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Pressed; - break; + } else if (_lastMouseButtonPressed != null && mouseEvent.EventFlags == 0 + && !_isButtonReleased && !_isButtonDoubleClicked && !_isOneFingerDoubleClicked) { + switch (_lastMouseButtonPressed) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Released; + break; - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Pressed; - break; + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Released; + break; - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Pressed; - break; - } - - if (_point == null) { - _point = p; - } - - if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { - mouseFlag |= MouseFlags.ReportMousePosition; - _isButtonReleased = false; - _processButtonClick = false; - } - _lastMouseButtonPressed = mouseEvent.ButtonState; - _isButtonPressed = true; - - if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag)); - return false; - }); - } + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Released; + break; + } + _isButtonPressed = false; + _isButtonReleased = true; + if (_point != null && (((Point)_point).X == mouseEvent.MousePosition.X && ((Point)_point).Y == mouseEvent.MousePosition.Y)) { + _processButtonClick = true; + } else { + _point = null; + } + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved + && !_isOneFingerDoubleClicked && _isButtonReleased && p == _point) { - } else if (_lastMouseButtonPressed != null && mouseEvent.EventFlags == 0 - && !_isButtonReleased && !_isButtonDoubleClicked && !_isOneFingerDoubleClicked) { - switch (_lastMouseButtonPressed) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Released; - break; + mouseFlag = ProcessButtonClick (mouseEvent); - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Released; - break; + } else if (mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.DoubleClick)) { + switch (mouseEvent.ButtonState) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1DoubleClicked; + break; - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Released; - break; - } - _isButtonPressed = false; - _isButtonReleased = true; - if (_point != null && (((Point)_point).X == mouseEvent.MousePosition.X && ((Point)_point).Y == mouseEvent.MousePosition.Y)) { - _processButtonClick = true; - } else { - _point = null; - } - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved - && !_isOneFingerDoubleClicked && _isButtonReleased && p == _point) { + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2DoubleClicked; + break; - mouseFlag = ProcessButtonClick (mouseEvent); + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3DoubleClicked; + break; + } + _isButtonDoubleClicked = true; + } else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && _isButtonDoubleClicked) { + switch (mouseEvent.ButtonState) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1TripleClicked; + break; - } else if (mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.DoubleClick)) { - switch (mouseEvent.ButtonState) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1DoubleClicked; - break; + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2TripleClicked; + break; - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2DoubleClicked; - break; + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3TripleClicked; + break; + } + _isButtonDoubleClicked = false; + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) { + switch ((int)mouseEvent.ButtonState) { + case int v when v > 0: + mouseFlag = MouseFlags.WheeledUp; + break; - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3DoubleClicked; - break; - } - _isButtonDoubleClicked = true; - } else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && _isButtonDoubleClicked) { - switch (mouseEvent.ButtonState) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1TripleClicked; - break; + case int v when v < 0: + mouseFlag = MouseFlags.WheeledDown; + break; + } - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2TripleClicked; - break; + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled && + mouseEvent.ControlKeyState == WindowsConsole.ControlKeyState.ShiftPressed) { + switch ((int)mouseEvent.ButtonState) { + case int v when v > 0: + mouseFlag = MouseFlags.WheeledLeft; + break; - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3TripleClicked; - break; - } - _isButtonDoubleClicked = false; - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) { - switch ((int)mouseEvent.ButtonState) { - case int v when v > 0: - mouseFlag = MouseFlags.WheeledUp; - break; + case int v when v < 0: + mouseFlag = MouseFlags.WheeledRight; + break; + } - case int v when v < 0: - mouseFlag = MouseFlags.WheeledDown; - break; - } + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseHorizontalWheeled) { + switch ((int)mouseEvent.ButtonState) { + case int v when v < 0: + mouseFlag = MouseFlags.WheeledLeft; + break; - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled && - mouseEvent.ControlKeyState == WindowsConsole.ControlKeyState.ShiftPressed) { - switch ((int)mouseEvent.ButtonState) { - case int v when v > 0: - mouseFlag = MouseFlags.WheeledLeft; - break; + case int v when v > 0: + mouseFlag = MouseFlags.WheeledRight; + break; + } - case int v when v < 0: - mouseFlag = MouseFlags.WheeledRight; - break; - } + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { + mouseFlag = MouseFlags.ReportMousePosition; + if (mouseEvent.MousePosition.X != _pointMove.X || mouseEvent.MousePosition.Y != _pointMove.Y) { + _pointMove = new Point (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y); + } + } else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) { + mouseFlag = 0; + } - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseHorizontalWheeled) { - switch ((int)mouseEvent.ButtonState) { - case int v when v < 0: - mouseFlag = MouseFlags.WheeledLeft; - break; + mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag); - case int v when v > 0: - mouseFlag = MouseFlags.WheeledRight; - break; - } + //System.Diagnostics.Debug.WriteLine ( + // $"point.X:{(point != null ? ((Point)point).X : -1)};point.Y:{(point != null ? ((Point)point).Y : -1)}"); - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { - mouseFlag = MouseFlags.ReportMousePosition; - if (mouseEvent.MousePosition.X != _pointMove.X || mouseEvent.MousePosition.Y != _pointMove.Y) { - _pointMove = new Point (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y); - } - } else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) { - mouseFlag = 0; - } + return new MouseEvent () { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y, + Flags = mouseFlag + }; + } - mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag); + MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent) + { + MouseFlags mouseFlag = 0; + switch (_lastMouseButtonPressed) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Clicked; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Clicked; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Clicked; + break; + } + _point = new Point () { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y + }; + _lastMouseButtonPressed = null; + _isButtonReleased = false; + _processButtonClick = false; + _point = null; + return mouseFlag; + } - //System.Diagnostics.Debug.WriteLine ( - // $"point.X:{(point != null ? ((Point)point).X : -1)};point.Y:{(point != null ? ((Point)point).Y : -1)}"); + async Task ProcessButtonDoubleClickedAsync () + { + await Task.Delay (300); + _isButtonDoubleClicked = false; + _isOneFingerDoubleClicked = false; + //buttonPressedCount = 0; + } - return new MouseEvent () { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y, + async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag) + { + while (_isButtonPressed) { + await Task.Delay (100); + var me = new MouseEvent () { + X = _pointMove.X, + Y = _pointMove.Y, Flags = mouseFlag }; - } - MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent) - { - MouseFlags mouseFlag = 0; - switch (_lastMouseButtonPressed) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Clicked; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Clicked; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Clicked; + var view = Application.WantContinuousButtonPressedView; + if (view == null) { break; } - _point = new Point () { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y - }; - _lastMouseButtonPressed = null; - _isButtonReleased = false; - _processButtonClick = false; - _point = null; - return mouseFlag; + if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { + Application.MainLoop.Invoke (() => _mouseHandler (me)); + } } + } - async Task ProcessButtonDoubleClickedAsync () - { - await Task.Delay (300); - _isButtonDoubleClicked = false; - _isOneFingerDoubleClicked = false; - //buttonPressedCount = 0; + static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag) + { + if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed) || + mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed)) { + mouseFlag |= MouseFlags.ButtonCtrl; } - async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag) - { - while (_isButtonPressed) { - await Task.Delay (100); - var me = new MouseEvent () { - X = _pointMove.X, - Y = _pointMove.Y, - Flags = mouseFlag - }; - - var view = Application.WantContinuousButtonPressedView; - if (view == null) { - break; - } - if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.Invoke (() => _mouseHandler (me)); - } - } + if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) { + mouseFlag |= MouseFlags.ButtonShift; } - static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag) - { - if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed) || - mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed)) { - mouseFlag |= MouseFlags.ButtonCtrl; - } - - if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) { - mouseFlag |= MouseFlags.ButtonShift; - } - - if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) || - mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) { - mouseFlag |= MouseFlags.ButtonAlt; - } - return mouseFlag; + if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) || + mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) { + mouseFlag |= MouseFlags.ButtonAlt; } + return mouseFlag; + } - KeyModifiers _keyModifiers; - - public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent) - { - var state = keyEvent.dwControlKeyState; - - bool shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0; - bool alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0; - bool control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0; - bool capslock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0; - bool numlock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0; - bool scrolllock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0; + KeyModifiers _keyModifiers; - if (_keyModifiers == null) { - _keyModifiers = new KeyModifiers (); - } - if (shift) { - _keyModifiers.Shift = shift; - } - if (alt) { - _keyModifiers.Alt = alt; - } - if (control) { - _keyModifiers.Ctrl = control; - } - if (capslock) { - _keyModifiers.Capslock = capslock; - } - if (numlock) { - _keyModifiers.Numlock = numlock; - } - if (scrolllock) { - _keyModifiers.Scrolllock = scrolllock; - } + public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent) + { + var state = keyEvent.dwControlKeyState; - var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); + bool shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0; + bool alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0; + bool control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0; + bool capslock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0; + bool numlock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0; + bool scrolllock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0; - return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock, scrolllock); + if (_keyModifiers == null) { + _keyModifiers = new KeyModifiers (); + } + if (shift) { + _keyModifiers.Shift = shift; + } + if (alt) { + _keyModifiers.Alt = alt; + } + if (control) { + _keyModifiers.Ctrl = control; + } + if (capslock) { + _keyModifiers.Capslock = capslock; + } + if (numlock) { + _keyModifiers.Numlock = numlock; + } + if (scrolllock) { + _keyModifiers.Scrolllock = scrolllock; } - public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent) - { - if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) { - return keyEvent; - } + var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); - var mod = new ConsoleModifiers (); - if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) { - mod |= ConsoleModifiers.Shift; - } - if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) || - keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) { - mod |= ConsoleModifiers.Alt; - } - if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed) || - keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed)) { - mod |= ConsoleModifiers.Control; - } - var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyEvent.UnicodeChar, mod, out uint virtualKey, out uint scanCode); - - return new WindowsConsole.KeyEventRecord { - UnicodeChar = (char)keyChar, - bKeyDown = keyEvent.bKeyDown, - dwControlKeyState = keyEvent.dwControlKeyState, - wRepeatCount = keyEvent.wRepeatCount, - wVirtualKeyCode = (ushort)virtualKey, - wVirtualScanCode = (ushort)scanCode - }; - } + return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock, scrolllock); + } - public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx) - { - var keyInfo = keyInfoEx.ConsoleKeyInfo; - switch (keyInfo.Key) { - case ConsoleKey.Escape: - return MapKeyModifiers (keyInfo, Key.Esc); - case ConsoleKey.Tab: - return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; - case ConsoleKey.Clear: - return MapKeyModifiers (keyInfo, Key.Clear); - case ConsoleKey.Home: - return MapKeyModifiers (keyInfo, Key.Home); - case ConsoleKey.End: - return MapKeyModifiers (keyInfo, Key.End); - case ConsoleKey.LeftArrow: - return MapKeyModifiers (keyInfo, Key.CursorLeft); - case ConsoleKey.RightArrow: - return MapKeyModifiers (keyInfo, Key.CursorRight); - case ConsoleKey.UpArrow: - return MapKeyModifiers (keyInfo, Key.CursorUp); - case ConsoleKey.DownArrow: - return MapKeyModifiers (keyInfo, Key.CursorDown); - case ConsoleKey.PageUp: - return MapKeyModifiers (keyInfo, Key.PageUp); - case ConsoleKey.PageDown: - return MapKeyModifiers (keyInfo, Key.PageDown); - case ConsoleKey.Enter: - return MapKeyModifiers (keyInfo, Key.Enter); - case ConsoleKey.Spacebar: - return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); - case ConsoleKey.Backspace: - return MapKeyModifiers (keyInfo, Key.Backspace); - case ConsoleKey.Delete: - return MapKeyModifiers (keyInfo, Key.DeleteChar); - case ConsoleKey.Insert: - return MapKeyModifiers (keyInfo, Key.InsertChar); - case ConsoleKey.PrintScreen: - return MapKeyModifiers (keyInfo, Key.PrintScreen); - - case ConsoleKey.NumPad0: - return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar; - case ConsoleKey.NumPad1: - return keyInfoEx.NumLock ? Key.D1 : Key.End; - case ConsoleKey.NumPad2: - return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown; - case ConsoleKey.NumPad3: - return keyInfoEx.NumLock ? Key.D3 : Key.PageDown; - case ConsoleKey.NumPad4: - return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft; - case ConsoleKey.NumPad5: - return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar); - case ConsoleKey.NumPad6: - return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight; - case ConsoleKey.NumPad7: - return keyInfoEx.NumLock ? Key.D7 : Key.Home; - case ConsoleKey.NumPad8: - return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp; - case ConsoleKey.NumPad9: - return keyInfoEx.NumLock ? Key.D9 : Key.PageUp; - - case ConsoleKey.Oem1: - case ConsoleKey.Oem2: - case ConsoleKey.Oem3: - case ConsoleKey.Oem4: - case ConsoleKey.Oem5: - case ConsoleKey.Oem6: - case ConsoleKey.Oem7: - case ConsoleKey.Oem8: - case ConsoleKey.Oem102: - case ConsoleKey.OemPeriod: - case ConsoleKey.OemComma: - case ConsoleKey.OemPlus: - case ConsoleKey.OemMinus: - if (keyInfo.KeyChar == 0) { - return Key.Unknown; - } + public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent) + { + if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) { + return keyEvent; + } - return (Key)((uint)keyInfo.KeyChar); - } + var mod = new ConsoleModifiers (); + if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) { + mod |= ConsoleModifiers.Shift; + } + if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) || + keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) { + mod |= ConsoleModifiers.Alt; + } + if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed) || + keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed)) { + mod |= ConsoleModifiers.Control; + } + var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyEvent.UnicodeChar, mod, out uint virtualKey, out uint scanCode); - var key = keyInfo.Key; - //var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a'; + return new WindowsConsole.KeyEventRecord { + UnicodeChar = (char)keyChar, + bKeyDown = keyEvent.bKeyDown, + dwControlKeyState = keyEvent.dwControlKeyState, + wRepeatCount = keyEvent.wRepeatCount, + wVirtualKeyCode = (ushort)virtualKey, + wVirtualScanCode = (ushort)scanCode + }; + } - if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { - var delta = key - ConsoleKey.A; - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { + public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx) + { + var keyInfo = keyInfoEx.ConsoleKeyInfo; + switch (keyInfo.Key) { + case ConsoleKey.Escape: + return MapKeyModifiers (keyInfo, Key.Esc); + case ConsoleKey.Tab: + return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; + case ConsoleKey.Clear: + return MapKeyModifiers (keyInfo, Key.Clear); + case ConsoleKey.Home: + return MapKeyModifiers (keyInfo, Key.Home); + case ConsoleKey.End: + return MapKeyModifiers (keyInfo, Key.End); + case ConsoleKey.LeftArrow: + return MapKeyModifiers (keyInfo, Key.CursorLeft); + case ConsoleKey.RightArrow: + return MapKeyModifiers (keyInfo, Key.CursorRight); + case ConsoleKey.UpArrow: + return MapKeyModifiers (keyInfo, Key.CursorUp); + case ConsoleKey.DownArrow: + return MapKeyModifiers (keyInfo, Key.CursorDown); + case ConsoleKey.PageUp: + return MapKeyModifiers (keyInfo, Key.PageUp); + case ConsoleKey.PageDown: + return MapKeyModifiers (keyInfo, Key.PageDown); + case ConsoleKey.Enter: + return MapKeyModifiers (keyInfo, Key.Enter); + case ConsoleKey.Spacebar: + return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); + case ConsoleKey.Backspace: + return MapKeyModifiers (keyInfo, Key.Backspace); + case ConsoleKey.Delete: + return MapKeyModifiers (keyInfo, Key.DeleteChar); + case ConsoleKey.Insert: + return MapKeyModifiers (keyInfo, Key.InsertChar); + case ConsoleKey.PrintScreen: + return MapKeyModifiers (keyInfo, Key.PrintScreen); + + case ConsoleKey.NumPad0: + return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar; + case ConsoleKey.NumPad1: + return keyInfoEx.NumLock ? Key.D1 : Key.End; + case ConsoleKey.NumPad2: + return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown; + case ConsoleKey.NumPad3: + return keyInfoEx.NumLock ? Key.D3 : Key.PageDown; + case ConsoleKey.NumPad4: + return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft; + case ConsoleKey.NumPad5: + return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar); + case ConsoleKey.NumPad6: + return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight; + case ConsoleKey.NumPad7: + return keyInfoEx.NumLock ? Key.D7 : Key.Home; + case ConsoleKey.NumPad8: + return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp; + case ConsoleKey.NumPad9: + return keyInfoEx.NumLock ? Key.D9 : Key.PageUp; + + case ConsoleKey.Oem1: + case ConsoleKey.Oem2: + case ConsoleKey.Oem3: + case ConsoleKey.Oem4: + case ConsoleKey.Oem5: + case ConsoleKey.Oem6: + case ConsoleKey.Oem7: + case ConsoleKey.Oem8: + case ConsoleKey.Oem102: + case ConsoleKey.OemPeriod: + case ConsoleKey.OemComma: + case ConsoleKey.OemPlus: + case ConsoleKey.OemMinus: + if (keyInfo.KeyChar == 0) { + return Key.Unknown; + } + + return (Key)((uint)keyInfo.KeyChar); + } + + var key = keyInfo.Key; + //var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a'; + + if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { + var delta = key - ConsoleKey.A; + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); - } - } - //return (Key)((uint)alphaBase + delta); - return (Key)((uint)keyInfo.KeyChar); } - if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { - var delta = key - ConsoleKey.D0; - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); - } - } - return (Key)((uint)keyInfo.KeyChar); + //return (Key)((uint)alphaBase + delta); + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { + var delta = key - ConsoleKey.D0; + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); } - if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { - var delta = key - ConsoleKey.F1; - if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); + if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); } - - return (Key)((uint)Key.F1 + delta); } - if (keyInfo.KeyChar != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { + var delta = key - ConsoleKey.F1; + if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); } - return (Key)(0xffffffff); + return (Key)((uint)Key.F1 + delta); + } + if (keyInfo.KeyChar != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); } - private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) - { - Key keyMod = new Key (); - if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { - keyMod = Key.ShiftMask; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { - keyMod |= Key.CtrlMask; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { - keyMod |= Key.AltMask; - } + return (Key)(0xffffffff); + } - return keyMod != Key.Null ? keyMod | key : key; + private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) + { + Key keyMod = new Key (); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { + keyMod = Key.ShiftMask; } - - int GetOutputBufferPosition () - { - return Row * Cols + Col; + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { + keyMod |= Key.CtrlMask; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { + keyMod |= Key.AltMask; } - public override void AddRune (Rune nStackrune) - { - // BUGBUG: https://github.com/gui-cs/Terminal.Gui/issues/2610 - System.Text.Rune rune = (System.Text.Rune)MakePrintable (nStackrune).Value; - var runeWidth = Rune.ColumnWidth (nStackrune); - var position = GetOutputBufferPosition (); - var validLocation = IsValidLocation (Col, Row); - - if (validLocation) { - if (runeWidth == 0 && Col > 0) { - // Non spacing glyph beyond first col - var r = Contents [Row, Col - 1, 0]; - var s = new string (new char [] { (char)r, (char)rune.Value }); - var sn = !s.IsNormalized () ? s.Normalize () : s; - var c = sn [0]; - Contents [Row, Col - 1, 0] = c; - Contents [Row, Col - 1, 1] = CurrentAttribute; - Contents [Row, Col - 1, 2] = 1; - - var prevPosition = Row * Cols + (Col - 1); - _outputBuffer [prevPosition].Char.UnicodeChar = c; - _outputBuffer [prevPosition].Attributes = (ushort)CurrentAttribute; - WindowsConsole.SmallRect.Update (ref _damageRegion, (short)(Col - 1), (short)Row); - } else { - if (runeWidth < 2) { - // Single column glyph - if (Col > 0 && Rune.ColumnWidth ((char)Contents [Row, Col - 1, 0]) > 1) { - // glyph to left is wide; nuke it - // BUGBUG: Does this make sense? - Contents [Row, Col - 1, 0] = System.Text.Rune.ReplacementChar.Value; - var prevPosition = Row * Cols + (Col - 1); - _outputBuffer [prevPosition].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; - } else if (Col <= Clip.Right - 1 && Rune.ColumnWidth ((char)Contents [Row, Col, 0]) > 1) { - // we're just inside the clip rect and the glyph there is wide - // nuke the existing glyph to right - Contents [Row, Col + 1, 0] = System.Text.Rune.ReplacementChar.Value; - var prevPosition = GetOutputBufferPosition () + 1; - _outputBuffer [prevPosition].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; - } - } + return keyMod != Key.Null ? keyMod | key : key; + } - if (runeWidth > 1 && Col == Clip.Right - 1) { - // Wide glyph and we're just inside the clip rect - // ignore it - // BUGBUG: Shouldn't we write the unknown glyph glyph? - Contents [Row, Col, 0] = System.Text.Rune.ReplacementChar.Value; - _outputBuffer [position].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; - } else { - // Add the glyph (wide or not) - if (rune.IsBmp) { - Contents [Row, Col, 0] = rune.Value; - _outputBuffer [position].Char.UnicodeChar = (char) rune.Value; - } else { - Contents [Row, Col, 0] = (char)System.Text.Rune.ReplacementChar.Value; - _outputBuffer [position].Char.UnicodeChar = (char)System.Text.Rune.ReplacementChar.Value; - - } - } - Contents [Row, Col, 1] = CurrentAttribute; - Contents [Row, Col, 2] = 1; + int GetOutputBufferPosition () + { + return Row * Cols + Col; + } - _outputBuffer [position].Attributes = (ushort)CurrentAttribute; - WindowsConsole.SmallRect.Update (ref _damageRegion, (short)Col, (short)Row); - } + public override void AddRune (System.Rune systemRune) + { + var rune = new Rune (systemRune).MakePrintable (); + var runeWidth = rune.GetColumnWidth (); + var validLocation = IsValidLocation (Col, Row); + + if (validLocation) { + if (!rune.IsBmp) { + rune = Rune.ReplacementChar; } + Contents [Row, Col, 0] = rune.Value; + Contents [Row, Col, 1] = CurrentAttribute; - if (runeWidth < 0 || runeWidth > 0) { - Col++; + if (Col > 0) { + var left = new Rune (Contents [Row, Col - 1, 0]); + if (left.GetColumnWidth () > 1) { + Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; + } } if (runeWidth > 1) { - if (rune.IsBmp && Col < Clip.Right) { - position = GetOutputBufferPosition (); - _outputBuffer [position].Attributes = (ushort)CurrentAttribute; - _outputBuffer [position].Char.UnicodeChar = (char)0x00; - Contents [Row, Col, 0] = (char)0x00; - Contents [Row, Col, 1] = CurrentAttribute; - Contents [Row, Col, 2] = 0; - } Col++; - } - } + Contents [Row, Col, 0] = Rune.ReplacementChar.Value; + Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 2] = 1; + } + + //var extraColumns = 0; + //ReadOnlySpan remainingInput = rune.ToString ().AsSpan (); + //while (!remainingInput.IsEmpty) { + // // Decode + // OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); + + // if (opStatus == OperationStatus.DestinationTooSmall || opStatus == OperationStatus.InvalidData) { + // result = Rune.ReplacementChar; + // } + // Contents [Row, Col + extraColumns, 0] = result.Value; + // Contents [Row, Col + extraColumns, 1] = CurrentAttribute; + + // // Slice and loop again + // remainingInput = remainingInput.Slice (charsConsumed); + // if (runeWidth > 1) { + // extraColumns++; + // Contents [Row, Col + extraColumns, 0] = ' '; + // Contents [Row, Col + extraColumns, 1] = CurrentAttribute; + // } + //} + } + //if (runeWidth == 0 && Col > 0) { + // // This is a combining character, and we are not at the beginning of the line. + // var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); + // var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; + // Contents [Row, Col - 1, 0] = normalized [0]; + // Contents [Row, Col - 1, 1] = CurrentAttribute; + // Contents [Row, Col - 1, 2] = 1; + // } else { + // Contents [Row, Col, 1] = CurrentAttribute; + // Contents [Row, Col, 2] = 1; + + // if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumnWidth () > 1) { + // // This is a single-width character, and we are not at the beginning of the line. + // Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; + + // } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumnWidth () > 1) { + // // This is a single-width character, and we are not at the end of the line. + // Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; + // Contents [Row, Col + 1, 2] = 1; + // } + + // if (runeWidth > 1 && Col == Clip.Right - 1) { + // // This is a double-width character, and we are at the end of the line. + // Contents [Row, Col, 0] = Rune.ReplacementChar.Value; + // } else { + // // This is a single-width character, or we are not at the end of the line. + // // Add the glyph (wide or not) + // //if (rune.IsBmp) { + // // Contents [Row, Col, 0] = rune.Value; + // // _outputBuffer [position].Char.UnicodeChar = (char)rune.Value; + // //} else { + // var column = Col; + // ReadOnlySpan remainingInput = rune.ToString ().AsSpan (); + // while (!remainingInput.IsEmpty) { + // // Decode + // OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); + + // if (opStatus != OperationStatus.Done) { + // result = Rune.ReplacementChar; + // } + // Contents [Row, column, 0] = result.Value; + // Contents [Row, column, 1] = CurrentAttribute; + + // // Slice and loop again + // remainingInput = remainingInput.Slice (charsConsumed); + // Col = ++column; + // } + // //// BUGBUG: workaround #2610 + // //Contents [Row, Col, 0] = (char)Rune.ReplacementChar.Value; + // //_outputBuffer [position].Char.UnicodeChar = (char)Rune.ReplacementChar.Value; + // //} + // } + // } + //} + + ////Col++; + ////if (runeWidth is < 0 or > 0) { + //// // This is a double-width codepoint, and we are not at the end of the line. + //// // Move to the right one and... + //// Col++; + ////} + + ////if (runeWidth > 1) { + //// // This is a double-width character, and we are not at the end of the line. + //// // ...clear the next cell. + //// if (validLocation && Col < Clip.Right) { + //// Contents [Row, Col, 1] = CurrentAttribute; + //// Contents [Row, Col, 2] = 0; + + //// //position = GetOutputBufferPosition (); + //// //_outputBuffer [position].Attributes = (ushort)CurrentAttribute; + + //// if (rune.IsBmp) { + //// // // BUGBUG: workaround #2610 - put a null char in the second position + //// //Contents [Row, Col, 0] = (char)0x00; + //// // _outputBuffer [position].Char.UnicodeChar = (char)0x00; + //// } + //// } + //// Col++; + ////} + /// + Col++; - public override Attribute MakeColor (Color foreground, Color background) - { - return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); + } + + public override Attribute MakeColor (Color foreground, Color background) + { + return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); + } + + Attribute MakeColor (ConsoleColor f, ConsoleColor b) + { + // Encode the colors into the int value. + return new Attribute ( + value: ((int)f | (int)b << 4), + foreground: (Color)f, + background: (Color)b + ); + } + + public override bool GetColors (int value, out Color foreground, out Color background) + { + bool hasColor = false; + foreground = default; + background = default; + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains ((value >> 4) & 0xffff)) { + hasColor = true; + background = (Color)(ConsoleColor)((value >> 4) & 0xffff); + } + if (values.Contains (value - ((int)background << 4))) { + hasColor = true; + foreground = (Color)(ConsoleColor)(value - ((int)background << 4)); + } + return hasColor; + } + + + public override void Init (Action terminalResized) + { + TerminalResized = terminalResized; + + try { + // Needed for Windows Terminal + // ESC [ ? 1047 h Activate xterm alternative buffer (no backscroll) + // ESC [ ? 1047 l Restore xterm working buffer (with backscroll) + // ESC [ ? 1048 h Save cursor position + // ESC [ ? 1048 l Restore cursor position + // ESC [ ? 1049 h Save cursor position and activate xterm alternative buffer (no backscroll) + // ESC [ ? 1049 l Restore cursor position and restore xterm working buffer (with backscroll) + // Per Issue #2264 using the alterantive screen buffer is required for Windows Terminal to not + // wipe out the backscroll buffer when the application exits. + Console.Out.Write ("\x1b[?1047h"); + + var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); + Cols = winSize.Width; + Rows = winSize.Height; + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); + + } catch (Win32Exception e) { + // Likely running unit tests. Set WinConsole to null so we can test it elsewhere. + WinConsole = null; + //throw new InvalidOperationException ("The Windows Console output window is not available.", e); } + + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitializeColorSchemes (); - Attribute MakeColor (ConsoleColor f, ConsoleColor b) - { - // Encode the colors into the int value. - return new Attribute ( - value: ((int)f | (int)b << 4), - foreground: (Color)f, - background: (Color)b - ); + ResizeScreen (); + UpdateOffScreen (); + } + + public virtual void ResizeScreen () + { + if (WinConsole == null) { + return; } - public override bool GetColors (int value, out Color foreground, out Color background) - { - bool hasColor = false; - foreground = default; - background = default; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains ((value >> 4) & 0xffff)) { - hasColor = true; - background = (Color)(ConsoleColor)((value >> 4) & 0xffff); - } - if (values.Contains (value - ((int)background << 4))) { - hasColor = true; - foreground = (Color)(ConsoleColor)(value - ((int)background << 4)); - } - return hasColor; + _outputBuffer = new WindowsConsole.CharInfo [Rows * Cols]; + Clip = new Rect (0, 0, Cols, Rows); + _damageRegion = new WindowsConsole.SmallRect () { + Top = 0, + Left = 0, + Bottom = (short)Rows, + Right = (short)Cols + }; + WinConsole.ForceRefreshCursorVisibility (); + if (!EnableConsoleScrolling) { + // ANSI ESC "[xJ" Clears part of the screen. + // If n is 0 (or missing), clear from cursor to end of screen. + // If n is 1, clear from cursor to beginning of the screen. + // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer + Console.Out.Write ("\x1b[3J"); } + } + public override void UpdateOffScreen () + { + Contents = new int [Rows, Cols, 3]; + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - public override void Init (Action terminalResized) - { - TerminalResized = terminalResized; - - try { - // Needed for Windows Terminal - // ESC [ ? 1047 h Activate xterm alternative buffer (no backscroll) - // ESC [ ? 1047 l Restore xterm working buffer (with backscroll) - // ESC [ ? 1048 h Save cursor position - // ESC [ ? 1048 l Restore cursor position - // ESC [ ? 1049 h Save cursor position and activate xterm alternative buffer (no backscroll) - // ESC [ ? 1049 l Restore cursor position and restore xterm working buffer (with backscroll) - // Per Issue #2264 using the alterantive screen buffer is required for Windows Terminal to not - // wipe out the backscroll buffer when the application exits. - Console.Out.Write ("\x1b[?1047h"); - - var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); - Cols = winSize.Width; - Rows = winSize.Height; - WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - - CurrentAttribute = MakeColor (Color.White, Color.Black); - InitializeColorSchemes (); - - ResizeScreen (); - UpdateOffScreen (); - } catch (Win32Exception e) { - throw new InvalidOperationException ("The Windows Console output window is not available.", e); + for (int row = 0; row < Rows; row++) { + for (int col = 0; col < Cols; col++) { + int position = row * Cols + col; + _outputBuffer [position].Attributes = (ushort)new Attribute (Color.White, Color.Black).Value; ; + _outputBuffer [position].Char.UnicodeChar = ' '; + Contents [row, col, 0] = _outputBuffer [position].Char.UnicodeChar; + Contents [row, col, 1] = _outputBuffer [position].Attributes; + Contents [row, col, 2] = 0; + WindowsConsole.SmallRect.Update (ref _damageRegion, (short)col, (short)row); } } + } - public virtual void ResizeScreen () - { - _outputBuffer = new WindowsConsole.CharInfo [Rows * Cols]; - Clip = new Rect (0, 0, Cols, Rows); - _damageRegion = new WindowsConsole.SmallRect () { - Top = 0, - Left = 0, - Bottom = (short)Rows, - Right = (short)Cols - }; - WinConsole.ForceRefreshCursorVisibility (); - if (!EnableConsoleScrolling) { - // ANSI ESC "[xJ" Clears part of the screen. - // If n is 0 (or missing), clear from cursor to end of screen. - // If n is 1, clear from cursor to beginning of the screen. - // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). - // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer - // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer - Console.Out.Write ("\x1b[3J"); + public override void UpdateScreen () + { + //if (_damageRegion.Left == -1) { + // return; + //} + + if (!EnableConsoleScrolling) { + var windowSize = WinConsole.GetConsoleBufferWindow (out _); + if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) { + return; } } - public override void UpdateOffScreen () - { - Contents = new int [Rows, Cols, 3]; - for (int row = 0; row < Rows; row++) { - for (int col = 0; col < Cols; col++) { - int position = row * Cols + col; - _outputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal; - _outputBuffer [position].Char.UnicodeChar = ' '; - Contents [row, col, 0] = _outputBuffer [position].Char.UnicodeChar; - Contents [row, col, 1] = _outputBuffer [position].Attributes; - Contents [row, col, 2] = 0; + var bufferCoords = new WindowsConsole.Coord () { + X = (short)Clip.Width, + Y = (short)Clip.Height + }; + + // BUGBUG: Temp hack to simplify - copy output buffer here instead of AddRune + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); + for (var row = 0; row < Rows; row++) { + for (var col = 0; col < Cols; col++) { + var rune = new Rune (Contents [row, col, 0]); + var position = row * Cols + col; + _outputBuffer [position].Char.UnicodeChar = (char)rune.Value; + _outputBuffer [position].Attributes = (ushort)Contents [row, col, 1]; + WindowsConsole.SmallRect.Update (ref _damageRegion, (short)col, (short)row); + var width = rune.GetColumnWidth (); + while (width > 1 && col < Cols - 1) { + col++; + position = row * Cols + col; + _outputBuffer [position].Char.UnicodeChar = (char)0x00; + _outputBuffer [position].Attributes = (ushort)Contents [row, col, 1]; + WindowsConsole.SmallRect.Update (ref _damageRegion, (short)col, (short)row); + width--; } } } - - public override void UpdateScreen () - { - if (_damageRegion.Left == -1) { - return; - } - if (!EnableConsoleScrolling) { - var windowSize = WinConsole.GetConsoleBufferWindow (out _); - if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) { - return; - } - } + WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion); + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); + } - var bufferCoords = new WindowsConsole.Coord () { - X = (short)Clip.Width, - Y = (short)Clip.Height - }; + public override void Refresh () + { + UpdateScreen (); + WinConsole.SetInitialCursorVisibility (); + UpdateCursor (); + } - WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion); - WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - } + CursorVisibility savedCursorVisibility; - public override void Refresh () - { - UpdateScreen (); - WinConsole.SetInitialCursorVisibility (); - UpdateCursor (); + public override void UpdateCursor () + { + if (Col < 0 || Row < 0 || Col > Cols || Row > Rows) { + GetCursorVisibility (out CursorVisibility cursorVisibility); + savedCursorVisibility = cursorVisibility; + SetCursorVisibility (CursorVisibility.Invisible); + return; } - CursorVisibility savedCursorVisibility; + SetCursorVisibility (savedCursorVisibility); + var position = new WindowsConsole.Coord () { + X = (short)Col, + Y = (short)Row + }; + WinConsole.SetCursorPosition (position); + } - public override void UpdateCursor () - { - if (Col < 0 || Row < 0 || Col > Cols || Row > Rows) { - GetCursorVisibility (out CursorVisibility cursorVisibility); - savedCursorVisibility = cursorVisibility; - SetCursorVisibility (CursorVisibility.Invisible); - return; - } + /// + public override bool GetCursorVisibility (out CursorVisibility visibility) + { + return WinConsole.GetCursorVisibility (out visibility); + } - SetCursorVisibility (savedCursorVisibility); - var position = new WindowsConsole.Coord () { - X = (short)Col, - Y = (short)Row - }; - WinConsole.SetCursorPosition (position); - } + /// + public override bool SetCursorVisibility (CursorVisibility visibility) + { + savedCursorVisibility = visibility; + return WinConsole.SetCursorVisibility (visibility); + } - /// - public override bool GetCursorVisibility (out CursorVisibility visibility) - { - return WinConsole.GetCursorVisibility (out visibility); - } + /// + public override bool EnsureCursorVisibility () + { + return WinConsole.EnsureCursorVisibility (); + } - /// - public override bool SetCursorVisibility (CursorVisibility visibility) - { - savedCursorVisibility = visibility; - return WinConsole.SetCursorVisibility (visibility); - } + public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) + { + WindowsConsole.InputRecord input = new WindowsConsole.InputRecord { + EventType = WindowsConsole.EventType.Key + }; - /// - public override bool EnsureCursorVisibility () - { - return WinConsole.EnsureCursorVisibility (); + WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord { + bKeyDown = true + }; + WindowsConsole.ControlKeyState controlKey = new WindowsConsole.ControlKeyState (); + if (shift) { + controlKey |= WindowsConsole.ControlKeyState.ShiftPressed; + keyEvent.UnicodeChar = '\0'; + keyEvent.wVirtualKeyCode = 16; } + if (alt) { + controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed; + controlKey |= WindowsConsole.ControlKeyState.RightAltPressed; + keyEvent.UnicodeChar = '\0'; + keyEvent.wVirtualKeyCode = 18; + } + if (control) { + controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed; + controlKey |= WindowsConsole.ControlKeyState.RightControlPressed; + keyEvent.UnicodeChar = '\0'; + keyEvent.wVirtualKeyCode = 17; + } + keyEvent.dwControlKeyState = controlKey; - public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) - { - WindowsConsole.InputRecord input = new WindowsConsole.InputRecord { - EventType = WindowsConsole.EventType.Key - }; - - WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord { - bKeyDown = true - }; - WindowsConsole.ControlKeyState controlKey = new WindowsConsole.ControlKeyState (); - if (shift) { - controlKey |= WindowsConsole.ControlKeyState.ShiftPressed; - keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = 16; - } - if (alt) { - controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed; - controlKey |= WindowsConsole.ControlKeyState.RightAltPressed; - keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = 18; - } - if (control) { - controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed; - controlKey |= WindowsConsole.ControlKeyState.RightControlPressed; - keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = 17; - } - keyEvent.dwControlKeyState = controlKey; + input.KeyEvent = keyEvent; - input.KeyEvent = keyEvent; + if (shift || alt || control) { + ProcessInput (input); + } - if (shift || alt || control) { - ProcessInput (input); - } + keyEvent.UnicodeChar = keyChar; + if ((uint)key < 255) { + keyEvent.wVirtualKeyCode = (ushort)key; + } else { + keyEvent.wVirtualKeyCode = '\0'; + } - keyEvent.UnicodeChar = keyChar; - if ((uint)key < 255) { - keyEvent.wVirtualKeyCode = (ushort)key; - } else { - keyEvent.wVirtualKeyCode = '\0'; - } + input.KeyEvent = keyEvent; + try { + ProcessInput (input); + } catch (OverflowException) { } finally { + keyEvent.bKeyDown = false; input.KeyEvent = keyEvent; - - try { - ProcessInput (input); - } catch (OverflowException) { } finally { - keyEvent.bKeyDown = false; - input.KeyEvent = keyEvent; - ProcessInput (input); - } + ProcessInput (input); } - - public override void End () - { - WinConsole.Cleanup (); - WinConsole = null; + } - // Needed for Windows Terminal - // Clear the alternative screen buffer from the cursor to the - // end of the screen. - // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE - // backbuffer! So we need to use [0J instead. - Console.Out.Write ("\x1b[0J"); + public override void End () + { + WinConsole.Cleanup (); + WinConsole = null; - // Disable alternative screen buffer. - Console.Out.Write ("\x1b[?1047l"); + // Needed for Windows Terminal + // Clear the alternative screen buffer from the cursor to the + // end of the screen. + // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE + // backbuffer! So we need to use [0J instead. + Console.Out.Write ("\x1b[0J"); - // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 - } + // Disable alternative screen buffer. + Console.Out.Write ("\x1b[?1047l"); - #region Not Implemented - public override void Suspend () - { - throw new NotImplementedException (); - } - #endregion + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 } + #region Not Implemented + public override void Suspend () + { + throw new NotImplementedException (); + } + #endregion +} + +/// +/// Mainloop intended to be used with the , and can +/// only be used on Windows. +/// +/// +/// This implementation is used for WindowsDriver. +/// +internal class WindowsMainLoop : IMainLoopDriver { + ManualResetEventSlim _eventReady = new ManualResetEventSlim (false); + ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); + ManualResetEventSlim _winChange = new ManualResetEventSlim (false); + MainLoop _mainLoop; + ConsoleDriver _consoleDriver; + WindowsConsole _winConsole; + bool _winChanged; + Size _windowSize; + CancellationTokenSource _tokenSource = new CancellationTokenSource (); + + // The records that we keep fetching + Queue _resultQueue = new Queue (); + /// - /// Mainloop intended to be used with the , and can - /// only be used on Windows. + /// Invoked when a Key is pressed or released. /// - /// - /// This implementation is used for WindowsDriver. - /// - internal class WindowsMainLoop : IMainLoopDriver { - ManualResetEventSlim _eventReady = new ManualResetEventSlim (false); - ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); - ManualResetEventSlim _winChange = new ManualResetEventSlim (false); - MainLoop _mainLoop; - ConsoleDriver _consoleDriver; - WindowsConsole _winConsole; - bool _winChanged; - Size _windowSize; - CancellationTokenSource _tokenSource = new CancellationTokenSource (); - - // The records that we keep fetching - Queue _resultQueue = new Queue (); - - /// - /// Invoked when a Key is pressed or released. - /// - public Action ProcessInput; - - /// - /// Invoked when the window is changed. - /// - public EventHandler WinChanged; - - public WindowsMainLoop (ConsoleDriver consoleDriver = null) - { - _consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided."); - _winConsole = ((WindowsDriver)consoleDriver).WinConsole; - } + public Action ProcessInput; - void IMainLoopDriver.Setup (MainLoop mainLoop) - { - _mainLoop = mainLoop; - Task.Run (WindowsInputHandler); - Task.Run (CheckWinChange); - } + /// + /// Invoked when the window is changed. + /// + public EventHandler WinChanged; - void WindowsInputHandler () - { - while (true) { - _waitForProbe.Wait (); - _waitForProbe.Reset (); + public WindowsMainLoop (ConsoleDriver consoleDriver = null) + { + _consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided."); + _winConsole = ((WindowsDriver)consoleDriver).WinConsole; + } - if (_resultQueue?.Count == 0) { - _resultQueue.Enqueue (_winConsole.ReadConsoleInput ()); - } + void IMainLoopDriver.Setup (MainLoop mainLoop) + { + _mainLoop = mainLoop; + Task.Run (WindowsInputHandler); + Task.Run (CheckWinChange); + } + + void WindowsInputHandler () + { + while (true) { + _waitForProbe.Wait (); + _waitForProbe.Reset (); - _eventReady.Set (); + if (_resultQueue?.Count == 0) { + _resultQueue.Enqueue (_winConsole.ReadConsoleInput ()); } + + _eventReady.Set (); } + } - void CheckWinChange () - { - while (true) { - _winChange.Wait (); - _winChange.Reset (); - WaitWinChange (); - _winChanged = true; - _eventReady.Set (); - } + void CheckWinChange () + { + while (true) { + _winChange.Wait (); + _winChange.Reset (); + WaitWinChange (); + _winChanged = true; + _eventReady.Set (); } + } - void WaitWinChange () - { - while (true) { - Thread.Sleep (100); - if (!_consoleDriver.EnableConsoleScrolling) { - _windowSize = _winConsole.GetConsoleBufferWindow (out _); - //System.Diagnostics.Debug.WriteLine ($"{consoleDriver.EnableConsoleScrolling},{windowSize.Width},{windowSize.Height}"); - if (_windowSize != Size.Empty && _windowSize.Width != _consoleDriver.Cols - || _windowSize.Height != _consoleDriver.Rows) { - return; - } + void WaitWinChange () + { + while (true) { + Thread.Sleep (100); + if (!_consoleDriver.EnableConsoleScrolling) { + _windowSize = _winConsole.GetConsoleBufferWindow (out _); + //System.Diagnostics.Debug.WriteLine ($"{consoleDriver.EnableConsoleScrolling},{windowSize.Width},{windowSize.Height}"); + if (_windowSize != Size.Empty && _windowSize.Width != _consoleDriver.Cols + || _windowSize.Height != _consoleDriver.Rows) { + return; } } } + } - void IMainLoopDriver.Wakeup () - { - //tokenSource.Cancel (); - _eventReady.Set (); - } - - bool IMainLoopDriver.EventsPending (bool wait) - { - _waitForProbe.Set (); - _winChange.Set (); + void IMainLoopDriver.Wakeup () + { + //tokenSource.Cancel (); + _eventReady.Set (); + } - if (CheckTimers (wait, out var waitTimeout)) { - return true; - } + bool IMainLoopDriver.EventsPending (bool wait) + { + _waitForProbe.Set (); + _winChange.Set (); - try { - if (!_tokenSource.IsCancellationRequested) { - _eventReady.Wait (waitTimeout, _tokenSource.Token); - } - } catch (OperationCanceledException) { - return true; - } finally { - _eventReady.Reset (); - } + if (CheckTimers (wait, out var waitTimeout)) { + return true; + } + try { if (!_tokenSource.IsCancellationRequested) { - return _resultQueue.Count > 0 || CheckTimers (wait, out _) || _winChanged; + _eventReady.Wait (waitTimeout, _tokenSource.Token); } - - _tokenSource.Dispose (); - _tokenSource = new CancellationTokenSource (); + } catch (OperationCanceledException) { return true; + } finally { + _eventReady.Reset (); } - bool CheckTimers (bool wait, out int waitTimeout) - { - long now = DateTime.UtcNow.Ticks; + if (!_tokenSource.IsCancellationRequested) { + return _resultQueue.Count > 0 || CheckTimers (wait, out _) || _winChanged; + } - if (_mainLoop.timeouts.Count > 0) { - waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); - if (waitTimeout < 0) - return true; - } else { - waitTimeout = -1; - } + _tokenSource.Dispose (); + _tokenSource = new CancellationTokenSource (); + return true; + } - if (!wait) - waitTimeout = 0; + bool CheckTimers (bool wait, out int waitTimeout) + { + long now = DateTime.UtcNow.Ticks; - int ic; - lock (_mainLoop.idleHandlers) { - ic = _mainLoop.idleHandlers.Count; - } + if (_mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) + return true; + } else { + waitTimeout = -1; + } - return ic > 0; + if (!wait) + waitTimeout = 0; + + int ic; + lock (_mainLoop.idleHandlers) { + ic = _mainLoop.idleHandlers.Count; } - void IMainLoopDriver.Iteration () - { - while (_resultQueue.Count > 0) { - var inputRecords = _resultQueue.Dequeue (); - if (inputRecords != null && inputRecords.Length > 0) { - var inputEvent = inputRecords [0]; - ProcessInput?.Invoke (inputEvent); - } - } - if (_winChanged) { - _winChanged = false; - WinChanged?.Invoke (this, new SizeChangedEventArgs (_windowSize)); + return ic > 0; + } + + void IMainLoopDriver.Iteration () + { + while (_resultQueue.Count > 0) { + var inputRecords = _resultQueue.Dequeue (); + if (inputRecords != null && inputRecords.Length > 0) { + var inputEvent = inputRecords [0]; + ProcessInput?.Invoke (inputEvent); } } + if (_winChanged) { + _winChanged = false; + WinChanged?.Invoke (this, new SizeChangedEventArgs (_windowSize)); + } } +} - class WindowsClipboard : ClipboardBase { - public WindowsClipboard () - { - IsSupported = IsClipboardFormatAvailable (_cfUnicodeText); - } +class WindowsClipboard : ClipboardBase { + public WindowsClipboard () + { + IsSupported = IsClipboardFormatAvailable (_cfUnicodeText); + } - public override bool IsSupported { get; } + public override bool IsSupported { get; } - protected override string GetClipboardDataImpl () - { - //if (!IsClipboardFormatAvailable (cfUnicodeText)) - // return null; + protected override string GetClipboardDataImpl () + { + //if (!IsClipboardFormatAvailable (cfUnicodeText)) + // return null; - try { - if (!OpenClipboard (IntPtr.Zero)) { - return null; - } + try { + if (!OpenClipboard (IntPtr.Zero)) { + return null; + } - IntPtr handle = GetClipboardData (_cfUnicodeText); - if (handle == IntPtr.Zero) { - return null; - } + IntPtr handle = GetClipboardData (_cfUnicodeText); + if (handle == IntPtr.Zero) { + return null; + } - IntPtr pointer = IntPtr.Zero; + IntPtr pointer = IntPtr.Zero; - try { - pointer = GlobalLock (handle); - if (pointer == IntPtr.Zero) { - return null; - } + try { + pointer = GlobalLock (handle); + if (pointer == IntPtr.Zero) { + return null; + } - int size = GlobalSize (handle); - byte [] buff = new byte [size]; + int size = GlobalSize (handle); + byte [] buff = new byte [size]; - Marshal.Copy (pointer, buff, 0, size); + Marshal.Copy (pointer, buff, 0, size); - return System.Text.Encoding.Unicode.GetString (buff).TrimEnd ('\0'); - } finally { - if (pointer != IntPtr.Zero) { - GlobalUnlock (handle); - } - } + return System.Text.Encoding.Unicode.GetString (buff).TrimEnd ('\0'); } finally { - CloseClipboard (); + if (pointer != IntPtr.Zero) { + GlobalUnlock (handle); + } } + } finally { + CloseClipboard (); } + } - protected override void SetClipboardDataImpl (string text) - { - OpenClipboard (); - - EmptyClipboard (); - IntPtr hGlobal = default; - try { - var bytes = (text.Length + 1) * 2; - hGlobal = Marshal.AllocHGlobal (bytes); - - if (hGlobal == default) { - ThrowWin32 (); - } + protected override void SetClipboardDataImpl (string text) + { + OpenClipboard (); - var target = GlobalLock (hGlobal); + EmptyClipboard (); + IntPtr hGlobal = default; + try { + var bytes = (text.Length + 1) * 2; + hGlobal = Marshal.AllocHGlobal (bytes); - if (target == default) { - ThrowWin32 (); - } + if (hGlobal == default) { + ThrowWin32 (); + } - try { - Marshal.Copy (text.ToCharArray (), 0, target, text.Length); - } finally { - GlobalUnlock (target); - } + var target = GlobalLock (hGlobal); - if (SetClipboardData (_cfUnicodeText, hGlobal) == default) { - ThrowWin32 (); - } + if (target == default) { + ThrowWin32 (); + } - hGlobal = default; + try { + Marshal.Copy (text.ToCharArray (), 0, target, text.Length); } finally { - if (hGlobal != default) { - Marshal.FreeHGlobal (hGlobal); - } + GlobalUnlock (target); + } - CloseClipboard (); + if (SetClipboardData (_cfUnicodeText, hGlobal) == default) { + ThrowWin32 (); } - } - void OpenClipboard () - { - var num = 10; - while (true) { - if (OpenClipboard (default)) { - break; - } + hGlobal = default; + } finally { + if (hGlobal != default) { + Marshal.FreeHGlobal (hGlobal); + } - if (--num == 0) { - ThrowWin32 (); - } + CloseClipboard (); + } + } - Thread.Sleep (100); + void OpenClipboard () + { + var num = 10; + while (true) { + if (OpenClipboard (default)) { + break; } - } - const uint _cfUnicodeText = 13; + if (--num == 0) { + ThrowWin32 (); + } - void ThrowWin32 () - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); + Thread.Sleep (100); } + } - [DllImport ("User32.dll", SetLastError = true)] - [return: MarshalAs (UnmanagedType.Bool)] - static extern bool IsClipboardFormatAvailable (uint format); + const uint _cfUnicodeText = 13; - [DllImport ("kernel32.dll", SetLastError = true)] - static extern int GlobalSize (IntPtr handle); + void ThrowWin32 () + { + throw new Win32Exception (Marshal.GetLastWin32Error ()); + } - [DllImport ("kernel32.dll", SetLastError = true)] - static extern IntPtr GlobalLock (IntPtr hMem); + [DllImport ("User32.dll", SetLastError = true)] + [return: MarshalAs (UnmanagedType.Bool)] + static extern bool IsClipboardFormatAvailable (uint format); - [DllImport ("kernel32.dll", SetLastError = true)] - [return: MarshalAs (UnmanagedType.Bool)] - static extern bool GlobalUnlock (IntPtr hMem); + [DllImport ("kernel32.dll", SetLastError = true)] + static extern int GlobalSize (IntPtr handle); - [DllImport ("user32.dll", SetLastError = true)] - [return: MarshalAs (UnmanagedType.Bool)] - static extern bool OpenClipboard (IntPtr hWndNewOwner); + [DllImport ("kernel32.dll", SetLastError = true)] + static extern IntPtr GlobalLock (IntPtr hMem); - [DllImport ("user32.dll", SetLastError = true)] - [return: MarshalAs (UnmanagedType.Bool)] - static extern bool CloseClipboard (); + [DllImport ("kernel32.dll", SetLastError = true)] + [return: MarshalAs (UnmanagedType.Bool)] + static extern bool GlobalUnlock (IntPtr hMem); - [DllImport ("user32.dll", SetLastError = true)] - static extern IntPtr SetClipboardData (uint uFormat, IntPtr data); + [DllImport ("user32.dll", SetLastError = true)] + [return: MarshalAs (UnmanagedType.Bool)] + static extern bool OpenClipboard (IntPtr hWndNewOwner); - [DllImport ("user32.dll")] - static extern bool EmptyClipboard (); + [DllImport ("user32.dll", SetLastError = true)] + [return: MarshalAs (UnmanagedType.Bool)] + static extern bool CloseClipboard (); - [DllImport ("user32.dll", SetLastError = true)] - static extern IntPtr GetClipboardData (uint uFormat); - } -} + [DllImport ("user32.dll", SetLastError = true)] + static extern IntPtr SetClipboardData (uint uFormat, IntPtr data); + + [DllImport ("user32.dll")] + static extern bool EmptyClipboard (); + + [DllImport ("user32.dll", SetLastError = true)] + static extern IntPtr GetClipboardData (uint uFormat); +} \ No newline at end of file diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index f05cd5ec34..5777b88551 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -19,7 +19,7 @@ namespace UICatalog.Scenarios { /// - Illustrates how to use ScrollView to do infinite scrolling /// [ScenarioMetadata (Name: "Character Map", - Description: "A Unicode character set viewier built as a custom control using the ScrollView control.")] + Description: "A Unicode character set viewer built as a custom control using the ScrollView control.")] [ScenarioCategory ("Text and Formatting")] [ScenarioCategory ("Controls")] [ScenarioCategory ("ScrollView")] @@ -275,19 +275,14 @@ public override void OnDrawContentComplete (Rect contentArea) int val = (row) * 16; Driver.SetAttribute (GetNormalColor ()); Move (firstColumnX, y + 1); - Driver.AddStr (new string (' ', 16 * COLUMN_WIDTH)); + //Driver.AddStr (new string (' ', 16 * COLUMN_WIDTH)); if (val <= MaxCodePointVal) { Driver.SetAttribute (GetNormalColor ()); + Move (firstColumnX + COLUMN_WIDTH, y + 1); for (int col = 0; col < 16; col++) { uint glyph = (uint)((uint)val + col); var rune = new Rune (glyph); - //if (rune >= 0x00D800 && rune <= 0x00DFFF) { - // if (col == 0) { - // Driver.AddStr ("Reserved for surrogate pairs."); - // } - // continue; - //} - Move (firstColumnX + (col * COLUMN_WIDTH) + 1, y + 1); + Move (firstColumnX + COLUMN_WIDTH * col + 1, y + 1); if (glyph == SelectedGlyph) { Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.HotNormal); } else { diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs new file mode 100644 index 0000000000..8e1a432da2 --- /dev/null +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Xunit; +using Xunit.Abstractions; + +// Alias Console to MockConsole so we don't accidentally use Console +using Console = Terminal.Gui.FakeConsole; + +namespace Terminal.Gui.DriverTests; +public class ContentsTests { + readonly ITestOutputHelper output; + + public ContentsTests (ITestOutputHelper output) + { + this.output = output; + } + + [Theory] + [InlineData (typeof (FakeDriver))] + //[InlineData (typeof (NetDriver))] + //[InlineData (typeof (CursesDriver))] + //[InlineData (typeof (WindowsDriver))] + public void AddStr_With_Combining_Characters (Type driverType) + { + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + Application.Init (driver); + // driver.Init (null); + + var acuteaccent = new System.Text.Rune (0x0301); // Combining acute accent (é) + var combined = "e" + acuteaccent; + var expected = "é"; + + driver.AddStr (combined); + TestHelpers.AssertDriverContentsWithFrameAre (expected, output); + + // 3 char combine + // a + ogonek + acute = ( ǫ́ ) + var ogonek = new System.Text.Rune (0x0328); // Combining ogonek (a small hook or comma shape) + combined = "a" + ogonek + acuteaccent; + expected = "ǫ́"; + + driver.Move (0, 0); + driver.AddStr (combined); + TestHelpers.AssertDriverContentsWithFrameAre (expected, output); + + } +} + diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index 67d39c84bc..9d0cbd3ff1 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -169,7 +169,7 @@ public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestO { var lines = new List> (); var sb = new StringBuilder (); - var driver = ((FakeDriver)Application.Driver); + var driver = Application.Driver; var x = -1; var y = -1; int w = -1; diff --git a/UnitTests/Text/UnicodeTests.cs b/UnitTests/Text/UnicodeTests.cs index a5c0c46b20..b327c19cc8 100644 --- a/UnitTests/Text/UnicodeTests.cs +++ b/UnitTests/Text/UnicodeTests.cs @@ -25,9 +25,9 @@ public UnicodeTests (ITestOutputHelper output) [InlineData (0x0001001A, 0x1001A)] public void MakePrintable_Converts_Control_Chars_To_Proper_Unicode (uint code, uint expected) { - var actual = ConsoleDriver.MakePrintable (code); + var actual = new System.Text.Rune (code).MakePrintable (); - Assert.Equal (expected, actual.Value); + Assert.Equal (expected, (uint)actual.Value); } [Theory] @@ -37,11 +37,20 @@ public void MakePrintable_Converts_Control_Chars_To_Proper_Unicode (uint code, u [InlineData (0x010020)] public void MakePrintable_Does_Not_Convert_Ansi_Chars_To_Unicode (uint code) { - var actual = ConsoleDriver.MakePrintable (code); + var actual = new System.Text.Rune (code).MakePrintable (); - Assert.Equal (code, actual.Value); + Assert.Equal (code, (uint)actual.Value); } - + + [Fact] + public void MakePrintable_Combining_Character_Is_Not_Printable () + { + var actual = new System.Text.Rune (0x0301).MakePrintable (); + + Assert.Equal (0x0301, actual.Value); + } + + [Fact, AutoInitShutdown] public void AddRune_On_Clip_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Replacement () { diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index b2907d0916..a7dce80639 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -57,5 +57,6 @@ False + C:\Users\charlie\s\gui-cs\Terminal.Gui\UnitTests\bin\Debug\net7.0\fine-code-coverage\coverage-tool-output\UnitTests-fcc-mscodecoverage-generated.runsettings \ No newline at end of file diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index dd463d46a6..1611e82b0f 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -2014,7 +2014,7 @@ public void LoadStream_IsDirty_With_Null_On_The_Text () Assert.Equal (8, tv.Text.Length); Assert.Equal (text, tv.Text); Assert.False (tv.IsDirty); - Assert.Equal ('\u2400', ConsoleDriver.MakePrintable ((Rune)tv.Text [4])); + Assert.Equal ('\u2400', new System.Text.Rune(tv.Text [4]).MakePrintable().Value); } } From 1225e4b4603058b25bfa5c955f5c3abd83bc602a Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 9 May 2023 16:07:22 +0200 Subject: [PATCH 22/99] Fixed unit tests --- UnitTests/ConsoleDrivers/ContentsTests.cs | 6 ++++++ UnitTests/UnitTests.csproj | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs index 8e1a432da2..17e5d1e360 100644 --- a/UnitTests/ConsoleDrivers/ContentsTests.cs +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -35,6 +35,8 @@ public void AddStr_With_Combining_Characters (Type driverType) driver.AddStr (combined); TestHelpers.AssertDriverContentsWithFrameAre (expected, output); +#if false // Disabled Until #2616 is fixed + // 3 char combine // a + ogonek + acute = ( ǫ́ ) var ogonek = new System.Text.Rune (0x0328); // Combining ogonek (a small hook or comma shape) @@ -45,6 +47,10 @@ public void AddStr_With_Combining_Characters (Type driverType) driver.AddStr (combined); TestHelpers.AssertDriverContentsWithFrameAre (expected, output); +#endif + + // Shutdown must be called to safely clean up Application if Init has been called + Application.Shutdown (); } } diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index a7dce80639..b2907d0916 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -57,6 +57,5 @@ False - C:\Users\charlie\s\gui-cs\Terminal.Gui\UnitTests\bin\Debug\net7.0\fine-code-coverage\coverage-tool-output\UnitTests-fcc-mscodecoverage-generated.runsettings \ No newline at end of file From 47f9cb8c0cd75ab0f4334ac91a84bee8c6124bce Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 9 May 2023 17:15:05 +0200 Subject: [PATCH 23/99] Fixed unit tests --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 6 ++++ UnitTests/ConsoleDrivers/ContentsTests.cs | 37 ++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 51f4c3f2b6..91f526d436 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -147,7 +147,13 @@ public abstract class ConsoleDriver { /// Used by and to determine where to add content. /// /// + /// /// This does not move the cursor on the screen, it only updates the internal state of the driver. + /// + /// + /// If or are negative or beyond and , + /// the method still sets those properties. + /// /// /// Column to move to. /// Row to move to. diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs index 17e5d1e360..0e38b2d409 100644 --- a/UnitTests/ConsoleDrivers/ContentsTests.cs +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -52,5 +52,42 @@ public void AddStr_With_Combining_Characters (Type driverType) // Shutdown must be called to safely clean up Application if Init has been called Application.Shutdown (); } + + [Theory] + [InlineData (typeof (FakeDriver))] + //[InlineData (typeof (NetDriver))] + //[InlineData (typeof (CursesDriver))] + //[InlineData (typeof (WindowsDriver))] + public void Move_Bad_Coordinates (Type driverType) + { + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + Application.Init (driver); + + Assert.Equal (0, driver.Col); + Assert.Equal (0, driver.Row); + + driver.Move (-1, 0); + Assert.Equal (-1, driver.Col); + Assert.Equal (0, driver.Row); + + driver.Move (0, -1); + Assert.Equal (0, driver.Col); + Assert.Equal (-1, driver.Row); + + driver.Move (driver.Cols, 0); + Assert.Equal (driver.Cols, driver.Col); + Assert.Equal (0, driver.Row); + + driver.Move (0, driver.Rows); + Assert.Equal (0, driver.Col); + Assert.Equal (driver.Rows, driver.Row); + + driver.Move (500, 500); + Assert.Equal (500, driver.Col); + Assert.Equal (500, driver.Row); + + // Shutdown must be called to safely clean up Application if Init has been called + Application.Shutdown (); + } } From 466a0c91ddfd323da747c6b162441507d493f295 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 9 May 2023 17:25:30 +0200 Subject: [PATCH 24/99] Added list of unit tests needed --- UnitTests/ConsoleDrivers/ContentsTests.cs | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs index 0e38b2d409..2bd0a10b45 100644 --- a/UnitTests/ConsoleDrivers/ContentsTests.cs +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -89,5 +89,41 @@ public void Move_Bad_Coordinates (Type driverType) // Shutdown must be called to safely clean up Application if Init has been called Application.Shutdown (); } + + // TODO: Add these unit tests + + // AddRune moves correctly + + // AddRune with wide characters are handled correctly + + // AddRune with wide characters and Col < 0 are handled correctly + + // AddRune with wide characters and Col == Cols - 1 are handled correctly + + // AddRune with wide characters and Col == Cols are handled correctly + + // AddStr moves correctly + + // AddStr with wide characters moves correctly + + // AddStr where Col is negative works + + // AddStr where Col is negative and characters include wide / combining characters works + + // AddStr where Col is near Cols and characters include wide / combining characters works + + // Clipping works correctly + + // Clipping works correctly with wide characters + + // Clipping works correctly with combining characters + + // Clipping works correctly with combining characters and wide characters + + // ResizeScreen works correctly + + // Refresh works correctly + + // IsDirty tests } From 679bfff09fdf787554729216c9475ce7d2082ce6 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 9 May 2023 22:43:48 +0200 Subject: [PATCH 25/99] Did some perf testing; tweaked code and charmap to address --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 6 +- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 15 +- UICatalog/Scenarios/CharacterMap.cs | 630 ++++++++++--------- 3 files changed, 335 insertions(+), 316 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index dd4220fa8b..3bd9003a14 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -633,11 +633,11 @@ public override void AddRune (System.Rune systemRune) return; } - var rune = new Rune (systemRune).MakePrintable (); - var runeWidth = rune.GetColumnWidth (); + int runeWidth = -1; var validLocation = IsValidLocation (Col, Row); - if (validLocation) { + var rune = new Rune (systemRune).MakePrintable (); + runeWidth = rune.GetColumnWidth (); if (runeWidth == 0 && Col > 0) { // This is a combining character, and we are not at the beginning of the line. var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index f25229d2f5..1d8c8e3fa0 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1384,11 +1384,9 @@ int GetOutputBufferPosition () public override void AddRune (System.Rune systemRune) { - var rune = new Rune (systemRune).MakePrintable (); - var runeWidth = rune.GetColumnWidth (); - var validLocation = IsValidLocation (Col, Row); + if (IsValidLocation (Col, Row)) { + var rune = new Rune (systemRune).MakePrintable (); - if (validLocation) { if (!rune.IsBmp) { rune = Rune.ReplacementChar; } @@ -1402,11 +1400,12 @@ public override void AddRune (System.Rune systemRune) } } - if (runeWidth > 1) { - Col++; + if (rune.GetColumnWidth () > 1 && Col == Clip.Right - 1) { + // This is a double-width character, and we are at the end of the line. Contents [Row, Col, 0] = Rune.ReplacementChar.Value; Contents [Row, Col, 1] = CurrentAttribute; Contents [Row, Col, 2] = 1; + Col++; } //var extraColumns = 0; @@ -1576,7 +1575,7 @@ public override void Init (Action terminalResized) WinConsole = null; //throw new InvalidOperationException ("The Windows Console output window is not available.", e); } - + CurrentAttribute = MakeColor (Color.White, Color.Black); InitializeColorSchemes (); @@ -1589,7 +1588,7 @@ public virtual void ResizeScreen () if (WinConsole == null) { return; } - + _outputBuffer = new WindowsConsole.CharInfo [Rows * Cols]; Clip = new Rect (0, 0, Cols, Rows); _damageRegion = new WindowsConsole.SmallRect () { diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 5777b88551..d3e01fabbd 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -1,367 +1,389 @@ #define DRAW_CONTENT //#define BASE_DRAW_CONTENT -using Microsoft.VisualBasic; using NStack; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using Terminal.Gui; -using Terminal.Gui.Resources; using Rune = System.Rune; -namespace UICatalog.Scenarios { - /// - /// This Scenario demonstrates building a custom control (a class deriving from View) that: - /// - Provides a "Character Map" application (like Windows' charmap.exe). - /// - Helps test unicode character rendering in Terminal.Gui - /// - Illustrates how to use ScrollView to do infinite scrolling - /// - [ScenarioMetadata (Name: "Character Map", - Description: "A Unicode character set viewer built as a custom control using the ScrollView control.")] - [ScenarioCategory ("Text and Formatting")] - [ScenarioCategory ("Controls")] - [ScenarioCategory ("ScrollView")] - public class CharacterMap : Scenario { - CharMap _charMap; - public override void Setup () - { - _charMap = new CharMap () { - X = 0, - Y = 0, - Height = Dim.Fill () - }; - Win.Add (_charMap); - - var jumpLabel = new Label ("Jump To Glyph:") { X = Pos.Right (_charMap) + 1, Y = Pos.Y (_charMap) }; - Win.Add (jumpLabel); - var jumpEdit = new TextField () { X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, Caption = "e.g. 01BE3" }; - Win.Add (jumpEdit); - var errorLabel = new Label ("") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] }; - Win.Add (errorLabel); - jumpEdit.TextChanged += (s, e) => { - uint result = 0; - if (jumpEdit.Text.Length == 0) return; +namespace UICatalog.Scenarios; + +/// +/// This Scenario demonstrates building a custom control (a class deriving from View) that: +/// - Provides a "Character Map" application (like Windows' charmap.exe). +/// - Helps test unicode character rendering in Terminal.Gui +/// - Illustrates how to use ScrollView to do infinite scrolling +/// +[ScenarioMetadata (Name: "Character Map", Description: "A Unicode character set viewer built as a custom control using the ScrollView control.")] +[ScenarioCategory ("Text and Formatting")] +[ScenarioCategory ("Controls")] +[ScenarioCategory ("ScrollView")] +public class CharacterMap : Scenario { + CharMap _charMap; + public override void Setup () + { + _charMap = new CharMap () { + X = 0, + Y = 0, + Height = Dim.Fill () + }; + Win.Add (_charMap); + + var jumpLabel = new Label ("Jump To Code Point:") { X = Pos.Right (_charMap) + 1, Y = Pos.Y (_charMap) }; + Win.Add (jumpLabel); + var jumpEdit = new TextField () { X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, Caption = "e.g. 01BE3" }; + Win.Add (jumpEdit); + var errorLabel = new Label ("") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] }; + Win.Add (errorLabel); + jumpEdit.TextChanged += (s, e) => { + uint result = 0; + if (jumpEdit.Text.Length == 0) return; + try { + result = Convert.ToUInt32 (jumpEdit.Text.ToString (), 10); + } catch (OverflowException) { + errorLabel.Text = $"Invalid (overflow)"; + return; + } catch (FormatException) { try { - result = Convert.ToUInt32 (jumpEdit.Text.ToString (), 10); + result = Convert.ToUInt32 (jumpEdit.Text.ToString (), 16); } catch (OverflowException) { errorLabel.Text = $"Invalid (overflow)"; return; } catch (FormatException) { - try { - result = Convert.ToUInt32 (jumpEdit.Text.ToString (), 16); - } catch (OverflowException) { - errorLabel.Text = $"Invalid (overflow)"; - return; - } catch (FormatException) { - errorLabel.Text = $"Invalid (can't parse)"; - return; - } + errorLabel.Text = $"Invalid (can't parse)"; + return; } - errorLabel.Text = $"U+{result:x4}"; - _charMap.SelectedGlyph = result; - }; + } + errorLabel.Text = $"U+{result:x4}"; + _charMap.SelectedCodePoint = result; + }; - var radioItems = new (ustring radioLabel, uint start, uint end) [UnicodeRange.Ranges.Count]; + var radioItems = new (ustring radioLabel, uint start, uint end) [UnicodeRange.Ranges.Count]; - for (var i = 0; i < UnicodeRange.Ranges.Count; i++) { - var range = UnicodeRange.Ranges [i]; - radioItems [i] = CreateRadio (range.Category, range.Start, range.End); - } - (ustring radioLabel, uint start, uint end) CreateRadio (ustring title, uint start, uint end) - { - return ($"{title} (U+{start:x5}-{end:x5})", start, end); - } + for (var i = 0; i < UnicodeRange.Ranges.Count; i++) { + var range = UnicodeRange.Ranges [i]; + radioItems [i] = CreateRadio (range.Category, range.Start, range.End); + } + (ustring radioLabel, uint start, uint end) CreateRadio (ustring title, uint start, uint end) + { + return ($"{title} (U+{start:x5}-{end:x5})", start, end); + } - var label = new Label ("Jump To Unicode Block:") { X = Pos.Right (_charMap) + 1, Y = Pos.Bottom (jumpLabel) + 1 }; - Win.Add (label); + var label = new Label ("Jump To Unicode Block:") { X = Pos.Right (_charMap) + 1, Y = Pos.Bottom (jumpLabel) + 1 }; + Win.Add (label); - var jumpList = new ListView (radioItems.Select (t => t.radioLabel).ToArray ()) { - X = Pos.X (label) + 1, - Y = Pos.Bottom (label), - Width = radioItems.Max (r => r.radioLabel.Length) + 2, - Height = Dim.Fill (1), - SelectedItem = 0 - }; - jumpList.SelectedItemChanged += (s, args) => { - _charMap.StartGlyph = radioItems [jumpList.SelectedItem].start; - }; + var jumpList = new ListView (radioItems.Select (t => t.radioLabel).ToArray ()) { + X = Pos.X (label) + 1, + Y = Pos.Bottom (label), + Width = radioItems.Max (r => r.radioLabel.Length) + 2, + Height = Dim.Fill (1), + SelectedItem = 0 + }; + jumpList.SelectedItemChanged += (s, args) => { + _charMap.StartCodePoint = radioItems [jumpList.SelectedItem].start; + }; - Win.Add (jumpList); + Win.Add (jumpList); - //jumpList.Refresh (); - _charMap.SetFocus (); + //jumpList.Refresh (); + _charMap.SetFocus (); - _charMap.Width = Dim.Fill () - jumpList.Width; - } + _charMap.Width = Dim.Fill () - jumpList.Width; } +} - class CharMap : ScrollView { - - /// - /// Specifies the starting offset for the character map. The default is 0x2500 - /// which is the Box Drawing characters. - /// - public uint StartGlyph { - get => _start; - set { - _start = value; - _selected = value; - ContentOffset = new Point (0, (int)(_start / 16)); - SetNeedsDisplay (); - } +class CharMap : ScrollView { + + /// + /// Specifies the starting offset for the character map. The default is 0x2500 + /// which is the Box Drawing characters. + /// + public uint StartCodePoint { + get => _start; + set { + _start = value; + _selected = value; + ContentOffset = new Point (0, (int)(_start / 16)); + SetNeedsDisplay (); } + } - /// - /// Specifies the starting offset for the character map. The default is 0x2500 - /// which is the Box Drawing characters. - /// - public uint SelectedGlyph { - get => _selected; - set { - _selected = value; - int row = (int)_selected / 16; - int height = (Bounds.Height / ROW_HEIGHT) - (ShowHorizontalScrollIndicator ? 2 : 1); - if (row + ContentOffset.Y < 0) { - // Moving up. - ContentOffset = new Point (ContentOffset.X, row); - } else if (row + ContentOffset.Y >= height) { - // Moving down. - ContentOffset = new Point (ContentOffset.X, Math.Min (row, row - height + ROW_HEIGHT)); - } - int col = (((int)_selected - (row * 16)) * COLUMN_WIDTH); - int width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth); - if (col + ContentOffset.X < 0) { - // Moving left. - ContentOffset = new Point (col, ContentOffset.Y); - } else if (col + ContentOffset.X >= width) { - // Moving right. - ContentOffset = new Point (Math.Min (col, col - width + COLUMN_WIDTH), ContentOffset.Y); - } - SetNeedsDisplay (); + /// + /// Specifies the starting offset for the character map. The default is 0x2500 + /// which is the Box Drawing characters. + /// + public uint SelectedCodePoint { + get => _selected; + set { + _selected = value; + int row = (int)_selected / 16; + int height = (Bounds.Height / ROW_HEIGHT) - (ShowHorizontalScrollIndicator ? 2 : 1); + if (row + ContentOffset.Y < 0) { + // Moving up. + ContentOffset = new Point (ContentOffset.X, row); + } else if (row + ContentOffset.Y >= height) { + // Moving down. + ContentOffset = new Point (ContentOffset.X, Math.Min (row, row - height + ROW_HEIGHT)); + } + int col = (((int)_selected - (row * 16)) * COLUMN_WIDTH); + int width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth); + if (col + ContentOffset.X < 0) { + // Moving left. + ContentOffset = new Point (col, ContentOffset.Y); + } else if (col + ContentOffset.X >= width) { + // Moving right. + ContentOffset = new Point (Math.Min (col, col - width + COLUMN_WIDTH), ContentOffset.Y); } + SetNeedsDisplay (); + Move (col + ContentOffset.X + RowLabelWidth + 1, row + ContentOffset.Y + 1); + Driver.UpdateCursor (); + } + } - uint _start = 0; - uint _selected = 0; + uint _start = 0; + uint _selected = 0; - public const int COLUMN_WIDTH = 3; - public const int ROW_HEIGHT = 1; + public const int COLUMN_WIDTH = 2; + public const int ROW_HEIGHT = 1; - public static uint MaxCodePointVal => 0x10FFFF; + public static uint MaxCodePoint => 0x10FFFF; - public static int RowLabelWidth => $"U+{MaxCodePointVal:x5}".Length + 1; - public static int RowWidth => RowLabelWidth + (COLUMN_WIDTH * 16); + public static int RowLabelWidth => $"U+{MaxCodePoint:x5}".Length + 1; + public static int RowWidth => RowLabelWidth + (COLUMN_WIDTH * 16); - public CharMap () - { - ColorScheme = Colors.Dialog; - CanFocus = true; + public CharMap () + { + ColorScheme = Colors.Dialog; + CanFocus = true; - ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePointVal / 16 + (ShowHorizontalScrollIndicator ? 2 : 1))); + ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + (ShowHorizontalScrollIndicator ? 2 : 1))); - AddCommand (Command.ScrollUp, () => { - if (SelectedGlyph >= 16) { - SelectedGlyph = SelectedGlyph - 16; - } - return true; - }); - AddCommand (Command.ScrollDown, () => { - if (SelectedGlyph < MaxCodePointVal - 16) { - SelectedGlyph = SelectedGlyph + 16; - } - return true; - }); - AddCommand (Command.ScrollLeft, () => { - if (SelectedGlyph > 0) { - SelectedGlyph--; - } - return true; - }); - AddCommand (Command.ScrollRight, () => { - if (SelectedGlyph < MaxCodePointVal) { - SelectedGlyph++; - } - return true; - }); - AddCommand (Command.PageUp, () => { - var page = (uint)(Bounds.Height / ROW_HEIGHT - 1) * 16; - SelectedGlyph -= Math.Min (page, SelectedGlyph); - return true; - }); - AddCommand (Command.PageDown, () => { - var page = (uint)(Bounds.Height / ROW_HEIGHT - 1) * 16; - SelectedGlyph += Math.Min (page, MaxCodePointVal - SelectedGlyph); - return true; - }); - AddCommand (Command.TopHome, () => { - SelectedGlyph = 0; - return true; - }); - AddCommand (Command.BottomEnd, () => { - SelectedGlyph = MaxCodePointVal; - return true; - }); - AddKeyBinding (Key.Enter, Command.Accept); - AddCommand (Command.Accept, () => { - MessageBox.Query ("Glyph", $"{new Rune ((uint)SelectedGlyph)} U+{SelectedGlyph:x4}", "Ok"); - return true; - }); - - MouseClick += Handle_MouseClick; - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - } + AddCommand (Command.ScrollUp, () => { + if (SelectedCodePoint >= 16) { + SelectedCodePoint = SelectedCodePoint - 16; + } + return true; + }); + AddCommand (Command.ScrollDown, () => { + if (SelectedCodePoint < MaxCodePoint - 16) { + SelectedCodePoint = SelectedCodePoint + 16; + } + return true; + }); + AddCommand (Command.ScrollLeft, () => { + if (SelectedCodePoint > 0) { + SelectedCodePoint--; + } + return true; + }); + AddCommand (Command.ScrollRight, () => { + if (SelectedCodePoint < MaxCodePoint) { + SelectedCodePoint++; + } + return true; + }); + AddCommand (Command.PageUp, () => { + var page = (uint)(Bounds.Height / ROW_HEIGHT - 1) * 16; + SelectedCodePoint -= Math.Min (page, SelectedCodePoint); + return true; + }); + AddCommand (Command.PageDown, () => { + var page = (uint)(Bounds.Height / ROW_HEIGHT - 1) * 16; + SelectedCodePoint += Math.Min (page, MaxCodePoint - SelectedCodePoint); + return true; + }); + AddCommand (Command.TopHome, () => { + SelectedCodePoint = 0; + return true; + }); + AddCommand (Command.BottomEnd, () => { + SelectedCodePoint = MaxCodePoint; + return true; + }); + AddKeyBinding (Key.Enter, Command.Accept); + AddCommand (Command.Accept, () => { + ShowDetails (); + return true; + }); + + MouseClick += Handle_MouseClick; + + // Reset cursor on resize + LayoutComplete += (s, a) => { + Driver.SetCursorVisibility (CursorVisibility.Default); + SelectedCodePoint = SelectedCodePoint; + }; + } - private void CopyValue () - { - Clipboard.Contents = $"U+{SelectedGlyph:x5}"; - } + private void CopyCodePoint () + { + Clipboard.Contents = $"U+{SelectedCodePoint:x5}"; + } - private void CopyGlyph () - { - Clipboard.Contents = $"{new Rune (SelectedGlyph)}"; - } + private void CopyGlyph () + { + Clipboard.Contents = $"{new Rune (SelectedCodePoint)}"; + } - public override void OnDrawContent (Rect contentArea) - { - base.OnDrawContent (contentArea); - - if (ShowHorizontalScrollIndicator && ContentSize.Height < (int)(MaxCodePointVal / 16 + 2)) { - ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePointVal / 16 + 2)); - int row = (int)_selected / 16; - int col = (((int)_selected - (row * 16)) * COLUMN_WIDTH); - int width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth); - if (col + ContentOffset.X >= width) { - // Snap to the selected glyph. - ContentOffset = new Point (Math.Min (col, col - width + COLUMN_WIDTH), ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); - } else { - ContentOffset = new Point (ContentOffset.X - col, ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); - } - SetNeedsDisplay (); - } else if (!ShowHorizontalScrollIndicator && ContentSize.Height > (int)(MaxCodePointVal / 16 + 1)) { - ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePointVal / 16 + 1)); - // Snap 1st column into view if it's been scrolled horizontally - ContentOffset = new Point (0, ContentOffset.Y < -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); - SetNeedsDisplay (); + public override void OnDrawContent (Rect contentArea) + { + base.OnDrawContent (contentArea); + + if (ShowHorizontalScrollIndicator && ContentSize.Height < (int)(MaxCodePoint / 16 + 2)) { + ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 2)); + int row = (int)_selected / 16; + int col = (((int)_selected - (row * 16)) * COLUMN_WIDTH); + int width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth); + if (col + ContentOffset.X >= width) { + // Snap to the selected glyph. + ContentOffset = new Point (Math.Min (col, col - width + COLUMN_WIDTH), ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); + } else { + ContentOffset = new Point (ContentOffset.X - col, ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); } + SetNeedsDisplay (); + } else if (!ShowHorizontalScrollIndicator && ContentSize.Height > (int)(MaxCodePoint / 16 + 1)) { + ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 1)); + // Snap 1st column into view if it's been scrolled horizontally + ContentOffset = new Point (0, ContentOffset.Y < -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); + SetNeedsDisplay (); } + } - public override void OnDrawContentComplete (Rect contentArea) - { - Rect viewport = new Rect (ContentOffset, - new Size (Math.Max (Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0), 0), - Math.Max (Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0), 0))); + public override void OnDrawContentComplete (Rect contentArea) + { + Rect viewport = new Rect (ContentOffset, + new Size (Math.Max (Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0), 0), + Math.Max (Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0), 0))); - var oldClip = Driver.Clip; - Driver.Clip = Bounds; + var oldClip = Driver.Clip; + Driver.Clip = Bounds; + if (!ShowHorizontalScrollIndicator) { // Redraw doesn't know about the scroll indicators, so if off, add one to height - if (!ShowHorizontalScrollIndicator) { - Driver.Clip = new Rect (Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height + 1); - } - Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.Focus); - Move (0, 0); - Driver.AddStr (new string (' ', RowLabelWidth + 1)); - for (int hexDigit = 0; hexDigit < 16; hexDigit++) { - var x = ContentOffset.X + RowLabelWidth + (hexDigit * COLUMN_WIDTH); - if (x > RowLabelWidth - 2) { - Move (x, 0); - Driver.AddStr ($" {hexDigit:x} "); - } + Driver.Clip = new Rect (Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height + 1); + } + Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.Focus); + Move (0, 0); + Driver.AddStr (new string (' ', RowLabelWidth + 1)); + for (int hexDigit = 0; hexDigit < 16; hexDigit++) { + var x = ContentOffset.X + RowLabelWidth + (hexDigit * COLUMN_WIDTH); + if (x > RowLabelWidth - 2) { + Move (x, 0); + Driver.AddStr ($" {hexDigit:x} "); } + } - var firstColumnX = viewport.X + RowLabelWidth; - for (int row = -ContentOffset.Y, y = 0; row <= (-ContentOffset.Y) + (Bounds.Height / ROW_HEIGHT); row++, y += ROW_HEIGHT) { - int val = (row) * 16; - Driver.SetAttribute (GetNormalColor ()); - Move (firstColumnX, y + 1); - //Driver.AddStr (new string (' ', 16 * COLUMN_WIDTH)); - if (val <= MaxCodePointVal) { - Driver.SetAttribute (GetNormalColor ()); - Move (firstColumnX + COLUMN_WIDTH, y + 1); - for (int col = 0; col < 16; col++) { - uint glyph = (uint)((uint)val + col); - var rune = new Rune (glyph); - Move (firstColumnX + COLUMN_WIDTH * col + 1, y + 1); - if (glyph == SelectedGlyph) { - Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.HotNormal); - } else { - Driver.SetAttribute (GetNormalColor ()); - } - Driver.AddRune (rune); - } - Move (0, y + 1); - Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.Focus); - var rowLabel = $"U+{val / 16:x5}_ "; - Driver.AddStr (rowLabel); + var firstColumnX = viewport.X + RowLabelWidth; + Driver.SetAttribute (GetNormalColor ()); + for (int row = -ContentOffset.Y, y = 0; row <= (-ContentOffset.Y) + (Bounds.Height / ROW_HEIGHT); row++, y += ROW_HEIGHT) { + var val = (row) * 16; + Move (firstColumnX, y + 1); + if (val <= MaxCodePoint) { + Move (firstColumnX + COLUMN_WIDTH, y + 1); + for (int col = 0; col < 16; col++) { + Move (firstColumnX + COLUMN_WIDTH * col + 1, y + 1); + Driver.AddRune (new Rune ((char)(val + col))); } + Move (0, y + 1); + Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.Focus); + var rowLabel = $"U+{val / 16:x5}_ "; + Driver.AddStr (rowLabel); + Driver.SetAttribute (GetNormalColor ()); } - Driver.Clip = oldClip; } + Driver.Clip = oldClip; + } - ContextMenu _contextMenu = new ContextMenu (); - void Handle_MouseClick (object sender, MouseEventEventArgs args) - { - var me = args.MouseEvent; - if (me.Flags == MouseFlags.ReportMousePosition || (me.Flags != MouseFlags.Button1Clicked && - me.Flags != MouseFlags.Button1DoubleClicked && - me.Flags != _contextMenu.MouseFlags)) { - return; - } + ContextMenu _contextMenu = new ContextMenu (); + void Handle_MouseClick (object sender, MouseEventEventArgs args) + { + var me = args.MouseEvent; + if (me.Flags == MouseFlags.ReportMousePosition || (me.Flags != MouseFlags.Button1Clicked && + me.Flags != MouseFlags.Button1DoubleClicked && + me.Flags != _contextMenu.MouseFlags)) { + return; + } - if (me.X < RowLabelWidth) { - return; - } + if (me.X < RowLabelWidth) { + return; + } - if (me.Y < 1) { - return; - } + if (me.Y < 1) { + return; + } - var row = (me.Y - 1); - var col = (me.X - RowLabelWidth - ContentOffset.X) / COLUMN_WIDTH; - uint val = (uint)((((uint)row - (uint)ContentOffset.Y) * 16) + col); - if (val > MaxCodePointVal) { - return; - } + var row = (me.Y - 1); + var col = (me.X - RowLabelWidth - ContentOffset.X) / COLUMN_WIDTH; + uint val = (uint)((((uint)row - (uint)ContentOffset.Y) * 16) + col); + if (val > MaxCodePoint) { + return; + } - if (me.Flags == MouseFlags.Button1Clicked) { - SelectedGlyph = (uint)val; - return; - } + if (me.Flags == MouseFlags.Button1Clicked) { + SelectedCodePoint = (uint)val; + return; + } - if (me.Flags == MouseFlags.Button1DoubleClicked) { - SelectedGlyph = (uint)val; - MessageBox.Query ("Glyph", $"{new Rune (val)} U+{SelectedGlyph:x4}", "Ok"); - return; - } + if (me.Flags == MouseFlags.Button1DoubleClicked) { + SelectedCodePoint = (uint)val; + ShowDetails (); + return; + } - if (me.Flags == _contextMenu.MouseFlags) { - SelectedGlyph = (uint)val; - _contextMenu = new ContextMenu (me.X + 1, me.Y + 1, - new MenuBarItem (new MenuItem [] { + if (me.Flags == _contextMenu.MouseFlags) { + SelectedCodePoint = (uint)val; + _contextMenu = new ContextMenu (me.X + 1, me.Y + 1, + new MenuBarItem (new MenuItem [] { new MenuItem ("_Copy Glyph", "", () => CopyGlyph (), null, null, Key.C | Key.CtrlMask), - new MenuItem ("Copy _Value", "", () => CopyValue (), null, null, Key.C | Key.ShiftMask | Key.CtrlMask), - }) { + new MenuItem ("Copy Code _Point", "", () => CopyCodePoint (), null, null, Key.C | Key.ShiftMask | Key.CtrlMask), + }) { - } - ); - _contextMenu.Show (); - } + } + ); + _contextMenu.Show (); } } - class UnicodeRange { - public uint Start; - public uint End; - public string Category; - public UnicodeRange (uint start, uint end, string category) - { - this.Start = start; - this.End = end; - this.Category = category; + void ShowDetails () + { + var title = $"{new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x4}"; + switch (MessageBox.Query (title, $"{new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x4}", "Copy _Glyph", "Copy Code _Point", "Cancel")) { + case 0: + CopyGlyph (); + break; + case 1: + CopyCodePoint (); + break; } + } - public static List Ranges = new List { + public override bool OnEnter (View view) + { + if (IsInitialized) { + Application.Driver.SetCursorVisibility (CursorVisibility.Default); + SelectedCodePoint = SelectedCodePoint; // update cursor location + } else { + Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); + + } + return base.OnEnter (view); + } +} + +class UnicodeRange { + public uint Start; + public uint End; + public string Category; + public UnicodeRange (uint start, uint end, string category) + { + this.Start = start; + this.End = end; + this.Category = category; + } + + public static List Ranges = new List { new UnicodeRange (0x0000, 0x001F, "ASCII Control Characters"), new UnicodeRange (0x0080, 0x009F, "C0 Control Characters"), new UnicodeRange(0x1100, 0x11ff,"Hangul Jamo"), // This is where wide chars tend to start @@ -505,8 +527,6 @@ public UnicodeRange (uint start, uint end, string category) new UnicodeRange (0x20000, 0x2A6DF ,"CJK Unified Ideographs Extension B"), new UnicodeRange (0x2F800, 0x2FA1F ,"CJK Compatibility Ideographs Supplement"), new UnicodeRange (0xE0000, 0xE007F ,"Tags"), - new UnicodeRange((uint)(CharMap.MaxCodePointVal - 16), (uint)CharMap.MaxCodePointVal,"End"), + new UnicodeRange((uint)(CharMap.MaxCodePoint - 16), (uint)CharMap.MaxCodePoint,"End"), }; - } - -} +} \ No newline at end of file From b4fa712100471f558b31f876a2de0295c21edae9 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 10 May 2023 06:21:17 +0200 Subject: [PATCH 26/99] Brough in code from PR #2264 (but commented) --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 1d8c8e3fa0..d286ebff6c 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -9,9 +9,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using System.Buffers; -using System.ComponentModel.Design.Serialization; -using Unix.Terminal; namespace Terminal.Gui; @@ -1561,7 +1558,7 @@ public override void Init (Action terminalResized) // ESC [ ? 1048 l Restore cursor position // ESC [ ? 1049 h Save cursor position and activate xterm alternative buffer (no backscroll) // ESC [ ? 1049 l Restore cursor position and restore xterm working buffer (with backscroll) - // Per Issue #2264 using the alterantive screen buffer is required for Windows Terminal to not + // Per Issue #2264 using the alternative screen buffer is required for Windows Terminal to not // wipe out the backscroll buffer when the application exits. Console.Out.Write ("\x1b[?1047h"); @@ -1606,6 +1603,7 @@ public virtual void ResizeScreen () // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer Console.Out.Write ("\x1b[3J"); + //Console.Out.Write ("\x1b[0J"); } } From 77e3184588b3ad2d9b3c2c68c931c81e59b1648c Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 10 May 2023 07:37:29 +0200 Subject: [PATCH 27/99] Tons of code cleanup --- Terminal.Gui/Application.cs | 5 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 23 +- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 6 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 88 +++---- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 116 ++++------ Terminal.Gui/Views/CheckBox.cs | 14 +- Terminal.Gui/Views/ColorPicker.cs | 9 +- UICatalog/Scenarios/CharacterMap.cs | 25 +- UICatalog/Scenarios/TableEditor.cs | 2 +- UICatalog/UICatalog.cs | 11 +- UnitTests/AssemblyInfo.cs | 5 - UnitTests/Clipboard/ClipboardTests.cs | 4 - UnitTests/TestHelpers.cs | 219 ++++++++---------- 13 files changed, 214 insertions(+), 313 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index 5a5667aee5..dd0c022138 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -1207,10 +1207,7 @@ static void OnUnGrabbedMouse (View view) static void ProcessMouseEvent (MouseEvent me) { - bool OutsideBounds (Point p, Rect r) - { - return p.X < 0 || p.X > r.Right || p.Y < 0 || p.Y > r.Bottom; - } + static bool OutsideBounds (Point p, Rect r) => p.X < 0 || p.X > r.Right || p.Y < 0 || p.Y > r.Bottom; if (IsMouseDisabled) { return; diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 91f526d436..167fa7695b 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -12,14 +12,15 @@ namespace Terminal.Gui; /// public static class RuneExtensions { - + /// + /// Returns if the is a combining character. + /// + /// + /// public static bool IsCombiningMark (this System.Text.Rune rune) { - // Convert Rune to string UnicodeCategory category = Rune.GetUnicodeCategory (rune); - - // Check if the category corresponds to a combining mark - return category == UnicodeCategory.NonSpacingMark + return Rune.GetUnicodeCategory (rune) == UnicodeCategory.NonSpacingMark || category == UnicodeCategory.SpacingCombiningMark || category == UnicodeCategory.EnclosingMark; } @@ -40,7 +41,6 @@ public static int GetColumnWidth (this System.Text.Rune rune) /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 /// to equivalent, printable, Unicode chars. /// - /// /// /// public static Rune MakePrintable (this System.Text.Rune rune) => Rune.IsControl (rune) ? new Rune (rune.Value + 0x2400) : rune; @@ -217,8 +217,8 @@ public bool IsValidLocation (int col, int row) => Clip.Contains (col, row); /// - /// Gets or sets the clip rectangle that and are - /// subject to. + /// Gets or sets the clip rectangle that and are + /// subject to. /// /// The rectangle describing the bounds of . public Rect Clip { @@ -274,8 +274,8 @@ public Rect Clip { public Attribute CurrentAttribute { get => _currentAttribute; set { - if (!value.Initialized && value.HasValidColors && Application.Driver != null) { - CurrentAttribute = Application.Driver.MakeAttribute (value.Foreground, value.Background); + if (value is { Initialized: false, HasValidColors: true } && Application.Driver != null) { + _currentAttribute = Application.Driver.MakeAttribute (value.Foreground, value.Background); return; } if (!value.Initialized) Debug.WriteLine ("ConsoleDriver.CurrentAttribute: Attributes must be initialized before use."); @@ -331,8 +331,7 @@ public virtual Attribute MakeAttribute (Color fore, Color back) /// Ensures all s in are correctly /// initialized by the driver. /// - /// Flag indicating if colors are supported (not used). - public void InitializeColorSchemes (bool supportsColors = true) + public void InitializeColorSchemes () { // Ensure all Attributes are initialized by the driver foreach (var s in Colors.ColorSchemes) { diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index bdc2a8f861..c01bced26f 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -97,13 +97,13 @@ public override void AddRune (System.Rune systemRune) // Decode OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); - if (opStatus == OperationStatus.DestinationTooSmall || opStatus == OperationStatus.InvalidData) { + if (opStatus is OperationStatus.DestinationTooSmall or OperationStatus.InvalidData) { result = Rune.ReplacementChar; } newCombo.Append (result); // Slice and loop again - remainingInput = remainingInput.Slice (charsConsumed); + remainingInput = remainingInput [charsConsumed..]; } newCombo.Append (rune); @@ -513,12 +513,10 @@ public virtual void ResizeScreen () if (FakeConsole.WindowHeight > 0) { // Can raise an exception while is still resizing. try { -#pragma warning disable CA1416 FakeConsole.CursorTop = 0; FakeConsole.CursorLeft = 0; FakeConsole.WindowTop = 0; FakeConsole.WindowLeft = 0; -#pragma warning restore CA1416 } catch (System.IO.IOException) { return; } catch (ArgumentOutOfRangeException) { diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 3bd9003a14..d3274371f9 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -123,10 +123,7 @@ internal class NetEvents { public NetEvents (ConsoleDriver consoleDriver) { - if (consoleDriver == null) { - throw new ArgumentNullException ("Console driver instance must be provided."); - } - _consoleDriver = consoleDriver; + _consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver)); Task.Run (ProcessInputResultQueue); Task.Run (CheckWinChange); } @@ -306,14 +303,8 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) { - string c1Control, code, terminating; - string [] values; // isKeyMouse is true if it's CSI<, false otherwise - bool isKeyMouse; - bool isReq; - List mouseFlags; - Point pos; - EscSeqUtils.DecodeEscSeq (EscSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); + EscSeqUtils.DecodeEscSeq (EscSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out var c1Control, out var code, out var values, out var terminating, out var isKeyMouse, out var mouseFlags, out var pos, out var isReq, ProcessContinuousButtonPressed); if (isKeyMouse) { foreach (var mf in mouseFlags) { @@ -440,7 +431,6 @@ MouseButtonState MapMouseFlags (MouseFlags mouseFlags) void GetRequestEvent (string c1Control, string code, string [] values, string terminating) { - EventType eventType = new EventType (); switch (terminating) { case "R": // Reports cursor position as CSI r ; c R Point point = new Point { @@ -449,7 +439,7 @@ void GetRequestEvent (string c1Control, string code, string [] values, string te }; if (_lastCursorPosition.Y != point.Y) { _lastCursorPosition = point; - eventType = EventType.WindowPosition; + var eventType = EventType.WindowPosition; var winPositionEv = new WindowPositionEvent () { CursorPosition = point }; @@ -630,7 +620,7 @@ public override void AddRune (System.Rune systemRune) if (Contents.Length != Rows * Cols * 3) { // BUGBUG: Shouldn't this throw an exception? Doing so to see what happens throw new InvalidOperationException ("Driver contents are wrong size"); - return; + //return; } int runeWidth = -1; @@ -725,7 +715,7 @@ public override void Init (Action terminalResized) IsWinPlatform = true; try { NetWinConsole = new NetWinVTConsole (); - } catch (ApplicationException e) { + } catch (ApplicationException) { // Likely running as a unit test, or in a non-interactive session. } } @@ -960,41 +950,25 @@ System.Text.StringBuilder WriteAttributes (int attr) int MapColors (ConsoleColor color, bool isForeground = true) { - switch (color) { - case ConsoleColor.Black: - return isForeground ? COLOR_BLACK : COLOR_BLACK + 10; - case ConsoleColor.DarkBlue: - return isForeground ? COLOR_BLUE : COLOR_BLUE + 10; - case ConsoleColor.DarkGreen: - return isForeground ? COLOR_GREEN : COLOR_GREEN + 10; - case ConsoleColor.DarkCyan: - return isForeground ? COLOR_CYAN : COLOR_CYAN + 10; - case ConsoleColor.DarkRed: - return isForeground ? COLOR_RED : COLOR_RED + 10; - case ConsoleColor.DarkMagenta: - return isForeground ? COLOR_MAGENTA : COLOR_MAGENTA + 10; - case ConsoleColor.DarkYellow: - return isForeground ? COLOR_YELLOW : COLOR_YELLOW + 10; - case ConsoleColor.Gray: - return isForeground ? COLOR_WHITE : COLOR_WHITE + 10; - case ConsoleColor.DarkGray: - return isForeground ? COLOR_BRIGHT_BLACK : COLOR_BRIGHT_BLACK + 10; - case ConsoleColor.Blue: - return isForeground ? COLOR_BRIGHT_BLUE : COLOR_BRIGHT_BLUE + 10; - case ConsoleColor.Green: - return isForeground ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_GREEN + 10; - case ConsoleColor.Cyan: - return isForeground ? COLOR_BRIGHT_CYAN : COLOR_BRIGHT_CYAN + 10; - case ConsoleColor.Red: - return isForeground ? COLOR_BRIGHT_RED : COLOR_BRIGHT_RED + 10; - case ConsoleColor.Magenta: - return isForeground ? COLOR_BRIGHT_MAGENTA : COLOR_BRIGHT_MAGENTA + 10; - case ConsoleColor.Yellow: - return isForeground ? COLOR_BRIGHT_YELLOW : COLOR_BRIGHT_YELLOW + 10; - case ConsoleColor.White: - return isForeground ? COLOR_BRIGHT_WHITE : COLOR_BRIGHT_WHITE + 10; - } - return 0; + return color switch { + ConsoleColor.Black => isForeground ? COLOR_BLACK : COLOR_BLACK + 10, + ConsoleColor.DarkBlue => isForeground ? COLOR_BLUE : COLOR_BLUE + 10, + ConsoleColor.DarkGreen => isForeground ? COLOR_GREEN : COLOR_GREEN + 10, + ConsoleColor.DarkCyan => isForeground ? COLOR_CYAN : COLOR_CYAN + 10, + ConsoleColor.DarkRed => isForeground ? COLOR_RED : COLOR_RED + 10, + ConsoleColor.DarkMagenta => isForeground ? COLOR_MAGENTA : COLOR_MAGENTA + 10, + ConsoleColor.DarkYellow => isForeground ? COLOR_YELLOW : COLOR_YELLOW + 10, + ConsoleColor.Gray => isForeground ? COLOR_WHITE : COLOR_WHITE + 10, + ConsoleColor.DarkGray => isForeground ? COLOR_BRIGHT_BLACK : COLOR_BRIGHT_BLACK + 10, + ConsoleColor.Blue => isForeground ? COLOR_BRIGHT_BLUE : COLOR_BRIGHT_BLUE + 10, + ConsoleColor.Green => isForeground ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_GREEN + 10, + ConsoleColor.Cyan => isForeground ? COLOR_BRIGHT_CYAN : COLOR_BRIGHT_CYAN + 10, + ConsoleColor.Red => isForeground ? COLOR_BRIGHT_RED : COLOR_BRIGHT_RED + 10, + ConsoleColor.Magenta => isForeground ? COLOR_BRIGHT_MAGENTA : COLOR_BRIGHT_MAGENTA + 10, + ConsoleColor.Yellow => isForeground ? COLOR_BRIGHT_YELLOW : COLOR_BRIGHT_YELLOW + 10, + ConsoleColor.White => isForeground ? COLOR_BRIGHT_WHITE : COLOR_BRIGHT_WHITE + 10, + var _ => 0 + }; } bool SetCursorPosition (int col, int row) @@ -1221,9 +1195,7 @@ Key MapKey (ConsoleKeyInfo keyInfo) Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) { - if (_keyModifiers == null) { - _keyModifiers = new KeyModifiers (); - } + _keyModifiers ??= new KeyModifiers (); Key keyMod = new Key (); if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { keyMod = Key.ShiftMask; @@ -1419,9 +1391,10 @@ MouseEvent ToDriverMouse (NetEvents.MouseEvent me) public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) { - NetEvents.InputResult input = new NetEvents.InputResult (); - input.EventType = NetEvents.EventType.Key; - input.ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control); + NetEvents.InputResult input = new NetEvents.InputResult { + EventType = NetEvents.EventType.Key, + ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control) + }; try { ProcessInput (input); @@ -1476,10 +1449,11 @@ internal class NetMainLoop : IMainLoopDriver { /// Passing a consoleDriver is provided to capture windows resizing. /// /// The console driver used by this Net main loop. + /// public NetMainLoop (ConsoleDriver consoleDriver = null) { if (consoleDriver == null) { - throw new ArgumentNullException ("Console driver instance must be provided."); + throw new ArgumentNullException (nameof (consoleDriver)); } _netEvents = new NetEvents (consoleDriver); } diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index d286ebff6c..fce0bb8559 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -28,12 +28,12 @@ public WindowsConsole () { _inputHandle = GetStdHandle (STD_INPUT_HANDLE); _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); - _originalConsoleMode = _consoleMode; + _originalConsoleMode = ConsoleMode; var newConsoleMode = _originalConsoleMode; newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags); newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode; newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput; - _consoleMode = newConsoleMode; + ConsoleMode = newConsoleMode; } CharInfo [] _originalStdOutChars; @@ -169,7 +169,7 @@ public void Cleanup () SetConsoleOutputWindow (out _); - _consoleMode = _originalConsoleMode; + ConsoleMode = _originalConsoleMode; //ContinueListeningForConsoleEvents = false; if (!SetConsoleActiveScreenBuffer (_outputHandle)) { var err = Marshal.GetLastWin32Error (); @@ -279,7 +279,7 @@ internal Size SetConsoleOutputWindow (out Point position) //bool ContinueListeningForConsoleEvents = true; - uint _consoleMode { + uint ConsoleMode { get { GetConsoleMode (_inputHandle, out uint v); return v; @@ -354,10 +354,8 @@ public struct MouseEventRecord { [FieldOffset (12)] public EventFlags EventFlags; - public override string ToString () - { - return $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}"; - } + public override readonly string ToString () => $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}"; + } public struct WindowBufferSizeRecord { @@ -368,7 +366,7 @@ public WindowBufferSizeRecord (short x, short y) _size = new Coord (x, y); } - public override string ToString () => $"[WindowBufferSize{_size}"; + public override readonly string ToString () => $"[WindowBufferSize{_size}"; } [StructLayout (LayoutKind.Sequential)] @@ -404,22 +402,16 @@ public struct InputRecord { [FieldOffset (4)] public FocusEventRecord FocusEvent; - public override string ToString () + public override readonly string ToString () { - switch (EventType) { - case EventType.Focus: - return FocusEvent.ToString (); - case EventType.Key: - return KeyEvent.ToString (); - case EventType.Menu: - return MenuEvent.ToString (); - case EventType.Mouse: - return MouseEvent.ToString (); - case EventType.WindowBufferSize: - return WindowBufferSizeEvent.ToString (); - default: - return "Unknown event type: " + EventType; - } + return EventType switch { + EventType.Focus => FocusEvent.ToString (), + EventType.Key => KeyEvent.ToString (), + EventType.Menu => MenuEvent.ToString (), + EventType.Mouse => MouseEvent.ToString (), + EventType.WindowBufferSize => WindowBufferSizeEvent.ToString (), + _ => "Unknown event type: " + EventType + }; } }; @@ -454,7 +446,7 @@ public Coord (short x, short y) X = x; Y = y; } - public override string ToString () => $"({X},{Y})"; + public override readonly string ToString () => $"({X},{Y})"; }; [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] @@ -508,10 +500,7 @@ public static void Update (ref SmallRect rect, short col, short row) rect.Bottom = row; } - public override string ToString () - { - return $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}"; - } + public override readonly string ToString () => $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}"; } [StructLayout (LayoutKind.Sequential)] @@ -519,14 +508,14 @@ public struct ConsoleKeyInfoEx { public ConsoleKeyInfo ConsoleKeyInfo; public bool CapsLock; public bool NumLock; - public bool Scrolllock; + public bool ScrollLock; public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock) { ConsoleKeyInfo = consoleKeyInfo; CapsLock = capslock; NumLock = numlock; - Scrolllock = scrolllock; + ScrollLock = scrolllock; } } @@ -818,20 +807,12 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) case WindowsConsole.ControlKeyState.CapslockOn: break; default: - switch (inputEvent.KeyEvent.wVirtualKeyCode) { - case 0x10: - key = new KeyEvent (Key.ShiftMask, _keyModifiers); - break; - case 0x11: - key = new KeyEvent (Key.CtrlMask, _keyModifiers); - break; - case 0x12: - key = new KeyEvent (Key.AltMask, _keyModifiers); - break; - default: - key = new KeyEvent (Key.Unknown, _keyModifiers); - break; - } + key = inputEvent.KeyEvent.wVirtualKeyCode switch { + 0x10 => new KeyEvent (Key.ShiftMask, _keyModifiers), + 0x11 => new KeyEvent (Key.CtrlMask, _keyModifiers), + 0x12 => new KeyEvent (Key.AltMask, _keyModifiers), + _ => new KeyEvent (Key.Unknown, _keyModifiers) + }; break; } @@ -843,9 +824,7 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) } else { if (inputEvent.KeyEvent.bKeyDown) { // May occurs using SendKeys - if (_keyModifiers == null) { - _keyModifiers = new KeyModifiers (); - } + _keyModifiers ??= new KeyModifiers (); // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event _keyDownHandler (new KeyEvent (map, _keyModifiers)); _keyHandler (new KeyEvent (map, _keyModifiers)); @@ -1159,38 +1138,36 @@ public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEve { var state = keyEvent.dwControlKeyState; - bool shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0; - bool alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0; - bool control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0; - bool capslock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0; - bool numlock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0; - bool scrolllock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0; + var shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0; + var alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0; + var control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0; + var capsLock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0; + var numLock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0; + var scrollLock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0; - if (_keyModifiers == null) { - _keyModifiers = new KeyModifiers (); - } + _keyModifiers ??= new KeyModifiers (); if (shift) { - _keyModifiers.Shift = shift; + _keyModifiers.Shift = true; } if (alt) { - _keyModifiers.Alt = alt; + _keyModifiers.Alt = true; } if (control) { - _keyModifiers.Ctrl = control; + _keyModifiers.Ctrl = true; } - if (capslock) { - _keyModifiers.Capslock = capslock; + if (capsLock) { + _keyModifiers.Capslock = true; } - if (numlock) { - _keyModifiers.Numlock = numlock; + if (numLock) { + _keyModifiers.Numlock = true; } - if (scrolllock) { - _keyModifiers.Scrolllock = scrolllock; + if (scrollLock) { + _keyModifiers.Scrolllock = true; } - var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); + var consoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); - return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock, scrolllock); + return new WindowsConsole.ConsoleKeyInfoEx (consoleKeyInfo, capsLock, numLock, scrollLock); } public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent) @@ -1506,7 +1483,6 @@ public override void AddRune (System.Rune systemRune) //// } //// Col++; ////} - /// Col++; } @@ -1567,7 +1543,7 @@ public override void Init (Action terminalResized) Rows = winSize.Height; WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - } catch (Win32Exception e) { + } catch (Win32Exception) { // Likely running unit tests. Set WinConsole to null so we can test it elsewhere. WinConsole = null; //throw new InvalidOperationException ("The Windows Console output window is not available.", e); diff --git a/Terminal.Gui/Views/CheckBox.cs b/Terminal.Gui/Views/CheckBox.cs index 5195c02216..0435f0941d 100644 --- a/Terminal.Gui/Views/CheckBox.cs +++ b/Terminal.Gui/Views/CheckBox.cs @@ -119,11 +119,11 @@ protected override void UpdateTextFormatterText () Rune GetCheckedState () { - switch (Checked) { - case true: return charChecked; - case false: return charUnChecked; - default: return charNullChecked; - } + return Checked switch { + true => charChecked, + false => charUnChecked, + var _ => charNullChecked + }; } ustring GetFormatterText () @@ -157,9 +157,7 @@ public bool AllowNullChecked { get => allowNullChecked; set { allowNullChecked = value; - if (Checked == null) { - Checked = false; - } + Checked ??= false; } } diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs index 4aaeddf86f..d363ea6bf3 100644 --- a/Terminal.Gui/Views/ColorPicker.cs +++ b/Terminal.Gui/Views/ColorPicker.cs @@ -70,14 +70,7 @@ public int BoxHeight { } } } - private int _boxHeight = 2; - - // Cursor runes. - private static readonly Rune [] _cursorRunes = new Rune [] - { - 0x250C, 0x2500, 0x2500, 0x2510, - 0x2514, 0x2500, 0x2500, 0x2518 - }; + int _boxHeight = 2; /// /// Cursor for the selected color. diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index d3e01fabbd..c7e7e2d3ec 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -137,12 +137,21 @@ public uint SelectedCodePoint { ContentOffset = new Point (Math.Min (col, col - width + COLUMN_WIDTH), ContentOffset.Y); } SetNeedsDisplay (); - Move (col + ContentOffset.X + RowLabelWidth + 1, row + ContentOffset.Y + 1); - Driver.UpdateCursor (); + UpdateCursor (); } } + void UpdateCursor () + { + int row = (int)_selected / 16; + int col = (((int)_selected - (row * 16)) * COLUMN_WIDTH); + + Move (col + ContentOffset.X + RowLabelWidth + 1, row + ContentOffset.Y + 1); + Driver.UpdateCursor (); + } + + uint _start = 0; uint _selected = 0; @@ -163,13 +172,13 @@ public CharMap () AddCommand (Command.ScrollUp, () => { if (SelectedCodePoint >= 16) { - SelectedCodePoint = SelectedCodePoint - 16; + SelectedCodePoint -= 16; } return true; }); AddCommand (Command.ScrollDown, () => { if (SelectedCodePoint < MaxCodePoint - 16) { - SelectedCodePoint = SelectedCodePoint + 16; + SelectedCodePoint += 16; } return true; }); @@ -212,10 +221,7 @@ public CharMap () MouseClick += Handle_MouseClick; // Reset cursor on resize - LayoutComplete += (s, a) => { - Driver.SetCursorVisibility (CursorVisibility.Default); - SelectedCodePoint = SelectedCodePoint; - }; + LayoutComplete += (s, a) => { UpdateCursor (); }; } private void CopyCodePoint () @@ -362,8 +368,7 @@ void ShowDetails () public override bool OnEnter (View view) { if (IsInitialized) { - Application.Driver.SetCursorVisibility (CursorVisibility.Default); - SelectedCodePoint = SelectedCodePoint; // update cursor location + UpdateCursor (); } else { Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); diff --git a/UICatalog/Scenarios/TableEditor.cs b/UICatalog/Scenarios/TableEditor.cs index fde6005fe7..a7d6671d80 100644 --- a/UICatalog/Scenarios/TableEditor.cs +++ b/UICatalog/Scenarios/TableEditor.cs @@ -607,7 +607,7 @@ public UnicodeRange (uint start, uint end, string category) new UnicodeRange(0xFB00, 0xFb4f,"Alphabetic Presentation Forms"), new UnicodeRange(0x12400, 0x1240f,"Cuneiform Numbers and Punctuation"), new UnicodeRange(0x1FA00, 0x1FA0f,"Chess Symbols"), - new UnicodeRange((uint)(CharMap.MaxCodePointVal - 16), (uint)CharMap.MaxCodePointVal,"End"), + new UnicodeRange((uint)(CharMap.MaxCodePoint - 16), (uint)CharMap.MaxCodePoint,"End"), new UnicodeRange (0x0020 ,0x007F ,"Basic Latin"), new UnicodeRange (0x00A0 ,0x00FF ,"Latin-1 Supplement"), diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 9ffa9454dc..d18356ac7a 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -384,8 +384,8 @@ public UICatalogTopLevel () ScenarioList.KeyDown += (s, a) => { if (CollectionNavigator.IsCompatibleKey (a.KeyEvent)) { var newItem = _scenarioCollectionNav?.GetNextMatchingItem (ScenarioList.SelectedRow, (char)a.KeyEvent.KeyValue); - if (newItem is int && newItem != -1) { - ScenarioList.SelectedRow = (int)newItem; + if (newItem is int v && newItem != -1) { + ScenarioList.SelectedRow = v; ScenarioList.EnsureSelectedCellIsVisible (); ScenarioList.SetNeedsDisplay (); a.Handled = true; @@ -490,7 +490,7 @@ List CreateDiagnosticMenuItems () { List menuItems = new List { CreateDiagnosticFlagsMenuItems (), - new MenuItem [] { }, + Array.Empty (), CreateEnableConsoleScrollingMenuItems (), CreateDisabledEnabledMouseItems (), CreateDisabledEnabledMenuBorder (), @@ -571,8 +571,9 @@ MenuItem [] CreateKeybindingsMenuItems () MenuItem [] CreateEnableConsoleScrollingMenuItems () { List menuItems = new List (); - miEnableConsoleScrolling = new MenuItem (); - miEnableConsoleScrolling.Title = "_Enable Console Scrolling"; + miEnableConsoleScrolling = new MenuItem { + Title = "_Enable Console Scrolling" + }; miEnableConsoleScrolling.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miEnableConsoleScrolling.Title.ToString ()!.Substring (1, 1) [0]; miEnableConsoleScrolling.CheckType |= MenuItemCheckStyle.Checked; miEnableConsoleScrolling.Action += () => { diff --git a/UnitTests/AssemblyInfo.cs b/UnitTests/AssemblyInfo.cs index 8739e61a25..e5b9a14979 100644 --- a/UnitTests/AssemblyInfo.cs +++ b/UnitTests/AssemblyInfo.cs @@ -1,9 +1,4 @@ global using CM = Terminal.Gui.ConfigurationManager; - -using System; -using System.Diagnostics; -using System.Reflection; -using Terminal.Gui; using Xunit; // Since Application is a singleton we can't run tests in parallel diff --git a/UnitTests/Clipboard/ClipboardTests.cs b/UnitTests/Clipboard/ClipboardTests.cs index 67d62923d4..ecfbd41f91 100644 --- a/UnitTests/Clipboard/ClipboardTests.cs +++ b/UnitTests/Clipboard/ClipboardTests.cs @@ -1,10 +1,6 @@ using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using Terminal.Gui; using Xunit; using Xunit.Abstractions; -using static AutoInitShutdownAttribute; namespace Terminal.Gui.ClipboardTests { public class ClipboardTests { diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index 9d0cbd3ff1..b0424d24c1 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -4,16 +4,13 @@ using System.Text; using Xunit.Abstractions; using Xunit; -using Terminal.Gui; using System.Text.RegularExpressions; using System.Reflection; using System.Diagnostics; using Rune = System.Rune; -using Attribute = Terminal.Gui.Attribute; -using Microsoft.VisualStudio.TestPlatform.Utilities; using NStack; -using Xunit.Sdk; +namespace Terminal.Gui; // This class enables test functions annotated with the [AutoInitShutdown] attribute to // automatically call Application.Init at start of the test and Application.Shutdown after the // test exits. @@ -32,11 +29,11 @@ public class AutoInitShutdownAttribute : Xunit.Sdk.BeforeAfterTestAttribute { /// CursesDriver, NetDriver) will be used when Application.Init is called. If null FakeDriver will be used. /// Only valid if is true. /// If true, will force the use of . - /// Only valid if == and is true. + /// Only valid if == and is true. /// Only valid if is true. - /// Only valid if == and is true. + /// Only valid if == and is true. /// Only valid if is true. - /// Only valid if == and is true. + /// Only valid if == and is true. /// Determines what config file locations will /// load from. public AutoInitShutdownAttribute (bool autoInit = true, bool autoShutdown = true, @@ -50,7 +47,7 @@ public AutoInitShutdownAttribute (bool autoInit = true, bool autoShutdown = true AutoInit = autoInit; AutoShutdown = autoShutdown; - DriverType = consoleDriverType ?? typeof (FakeDriver); + _driverType = consoleDriverType ?? typeof (FakeDriver); FakeDriver.FakeBehaviors.UseFakeClipboard = useFakeClipboard; FakeDriver.FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; FakeDriver.FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue; @@ -60,7 +57,7 @@ public AutoInitShutdownAttribute (bool autoInit = true, bool autoShutdown = true static bool _init = false; bool AutoInit { get; } bool AutoShutdown { get; } - Type DriverType; + Type _driverType; public override void Before (MethodInfo methodUnderTest) { @@ -69,7 +66,7 @@ public override void Before (MethodInfo methodUnderTest) throw new InvalidOperationException ("After did not run when AutoShutdown was specified."); } if (AutoInit) { - Application.Init ((ConsoleDriver)Activator.CreateInstance (DriverType)); + Application.Init ((ConsoleDriver)Activator.CreateInstance (_driverType)); _init = true; } } @@ -108,61 +105,56 @@ public override void After (MethodInfo methodUnderTest) } } -class TestHelpers { +partial class TestHelpers { + [GeneratedRegex ("\\s+$", RegexOptions.Multiline)] + private static partial Regex TrailingWhiteSpaceRegEx (); + [GeneratedRegex ("^\\s+", RegexOptions.Multiline)] + private static partial Regex LeadingWhitespaceRegEx (); + #pragma warning disable xUnit1013 // Public method should be marked as test public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelper output, bool ignoreLeadingWhitespace = false) { #pragma warning restore xUnit1013 // Public method should be marked as test var sb = new StringBuilder (); - var driver = ((FakeDriver)Application.Driver); + var driver = (FakeDriver)Application.Driver; var contents = driver.Contents; for (int r = 0; r < driver.Rows; r++) { for (int c = 0; c < driver.Cols; c++) { Rune rune = contents [r, c, 0]; - if (Rune.DecodeSurrogatePair (rune, out char [] spair)) { - sb.Append (spair); - } else { - sb.Append ((char)rune); - } - if (Rune.ColumnWidth (rune) > 1) { - c++; - } + if (Rune.DecodeSurrogatePair (rune, out char [] spair)) sb.Append (spair); + else sb.Append ((char)rune); + if (Rune.ColumnWidth (rune) > 1) c++; } sb.AppendLine (); } var actualLook = sb.ToString (); - if (!string.Equals (expectedLook, actualLook)) { - - // ignore trailing whitespace on each line - var trailingWhitespace = new Regex (@"\s+$", RegexOptions.Multiline); - var leadingWhitespace = new Regex (@"^\s+", RegexOptions.Multiline); - - // get rid of trailing whitespace on each line (and leading/trailing whitespace of start/end of full string) - expectedLook = trailingWhitespace.Replace (expectedLook, "").Trim (); - actualLook = trailingWhitespace.Replace (actualLook, "").Trim (); + if (string.Equals (expectedLook, actualLook)) return; + + // get rid of trailing whitespace on each line (and leading/trailing whitespace of start/end of full string) + expectedLook = TrailingWhiteSpaceRegEx ().Replace (expectedLook, "").Trim (); + actualLook = TrailingWhiteSpaceRegEx ().Replace (actualLook, "").Trim (); - if (ignoreLeadingWhitespace) { - expectedLook = leadingWhitespace.Replace (expectedLook, "").Trim (); - actualLook = leadingWhitespace.Replace (actualLook, "").Trim (); - } - - // standardize line endings for the comparison - expectedLook = expectedLook.Replace ("\r\n", "\n"); - actualLook = actualLook.Replace ("\r\n", "\n"); + if (ignoreLeadingWhitespace) { + expectedLook = LeadingWhitespaceRegEx().Replace (expectedLook, "").Trim (); + actualLook = LeadingWhitespaceRegEx().Replace (actualLook, "").Trim (); + } - // If test is about to fail show user what things looked like - if (!string.Equals (expectedLook, actualLook)) { - output?.WriteLine ("Expected:" + Environment.NewLine + expectedLook); - output?.WriteLine ("But Was:" + Environment.NewLine + actualLook); - } + // standardize line endings for the comparison + expectedLook = expectedLook.Replace ("\r\n", "\n"); + actualLook = actualLook.Replace ("\r\n", "\n"); - Assert.Equal (expectedLook, actualLook); + // If test is about to fail show user what things looked like + if (!string.Equals (expectedLook, actualLook)) { + output?.WriteLine ("Expected:" + Environment.NewLine + expectedLook); + output?.WriteLine ("But Was:" + Environment.NewLine + actualLook); } + + Assert.Equal (expectedLook, actualLook); } public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestOutputHelper output) @@ -172,90 +164,69 @@ public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestO var driver = Application.Driver; var x = -1; var y = -1; - int w = -1; - int h = -1; - + var w = -1; + var h = -1; + var contents = driver.Contents; - for (int r = 0; r < driver.Rows; r++) { + for (var r = 0; r < driver.Rows; r++) { var runes = new List (); - for (int c = 0; c < driver.Cols; c++) { + for (var c = 0; c < driver.Cols; c++) { var rune = (Rune)contents [r, c, 0]; if (rune != ' ') { if (x == -1) { x = c; y = r; - for (int i = 0; i < c; i++) { - runes.InsertRange (i, new List () { ' ' }); - } - } - if (Rune.ColumnWidth (rune) > 1) { - c++; - } - if (c + 1 > w) { - w = c + 1; + for (int i = 0; i < c; i++) runes.InsertRange (i, new List () { ' ' }); } + if (Rune.ColumnWidth (rune) > 1) c++; + if (c + 1 > w) w = c + 1; h = r - y + 1; } - if (x > -1) { - runes.Add (rune); - } - } - if (runes.Count > 0) { - lines.Add (runes); + if (x > -1) runes.Add (rune); } + if (runes.Count > 0) lines.Add (runes); } // Remove unnecessary empty lines if (lines.Count > 0) { - for (int r = lines.Count - 1; r > h - 1; r--) { - lines.RemoveAt (r); - } + for (var r = lines.Count - 1; r > h - 1; r--) lines.RemoveAt (r); } // Remove trailing whitespace on each line - for (int r = 0; r < lines.Count; r++) { - List row = lines [r]; - for (int c = row.Count - 1; c >= 0; c--) { + foreach (var row in lines) { + for (var c = row.Count - 1; c >= 0; c--) { var rune = row [c]; - if (rune != ' ' || (row.Sum (x => Rune.ColumnWidth (x)) == w)) { - break; - } + if (rune != ' ' || row.Sum (x => Rune.ColumnWidth (x)) == w) break; row.RemoveAt (c); } } // Convert Rune list to string - for (int r = 0; r < lines.Count; r++) { - var line = NStack.ustring.Make (lines [r]).ToString (); - if (r == lines.Count - 1) { - sb.Append (line); - } else { - sb.AppendLine (line); - } + for (var r = 0; r < lines.Count; r++) { + var line = ustring.Make (lines [r]).ToString (); + if (r == lines.Count - 1) sb.Append (line); + else sb.AppendLine (line); } var actualLook = sb.ToString (); - if (!string.Equals (expectedLook, actualLook)) { + if (string.Equals (expectedLook, actualLook)) { + return new Rect (x > -1 ? x : 0, y > -1 ? y : 0, w > -1 ? w : 0, h > -1 ? h : 0); + } + + // standardize line endings for the comparison + expectedLook = expectedLook.Replace ("\r\n", "\n"); + actualLook = actualLook.Replace ("\r\n", "\n"); - // standardize line endings for the comparison - expectedLook = expectedLook.Replace ("\r\n", "\n"); - actualLook = actualLook.Replace ("\r\n", "\n"); + // Remove the first and the last line ending from the expectedLook + if (expectedLook.StartsWith ("\n")) expectedLook = expectedLook [1..]; + if (expectedLook.EndsWith ("\n")) expectedLook = expectedLook [..^1]; - // Remove the first and the last line ending from the expectedLook - if (expectedLook.StartsWith ("\n")) { - expectedLook = expectedLook [1..]; - } - if (expectedLook.EndsWith ("\n")) { - expectedLook = expectedLook [..^1]; - } + output?.WriteLine ("Expected:" + Environment.NewLine + expectedLook); + output?.WriteLine ("But Was:" + Environment.NewLine + actualLook); - output?.WriteLine ("Expected:" + Environment.NewLine + expectedLook); - output?.WriteLine ("But Was:" + Environment.NewLine + actualLook); - - Assert.Equal (expectedLook, actualLook); - } + Assert.Equal (expectedLook, actualLook); return new Rect (x > -1 ? x : 0, y > -1 ? y : 0, w > -1 ? w : 0, h > -1 ? h : 0); } @@ -271,35 +242,32 @@ public static void AssertDriverColorsAre (string expectedLook, params Attribute { #pragma warning restore xUnit1013 // Public method should be marked as test - if (expectedColors.Length > 10) { - throw new ArgumentException ("This method only works for UIs that use at most 10 colors"); - } + if (expectedColors.Length > 10) throw new ArgumentException ("This method only works for UIs that use at most 10 colors"); expectedLook = expectedLook.Trim (); - var driver = ((FakeDriver)Application.Driver); - + var driver = (FakeDriver)Application.Driver; + var contents = driver.Contents; - int r = 0; + var r = 0; foreach (var line in expectedLook.Split ('\n').Select (l => l.Trim ())) { - for (int c = 0; c < line.Length; c++) { + for (var c = 0; c < line.Length; c++) { - int val = contents [r, c, 1]; + var val = contents [r, c, 1]; var match = expectedColors.Where (e => e.Value == val).ToList (); - if (match.Count == 0) { + switch (match.Count) { + case 0: throw new Exception ($"Unexpected color {DescribeColor (val)} was used at row {r} and col {c} (indexes start at 0). Color value was {val} (expected colors were {string.Join (",", expectedColors.Select (c => DescribeColor (c.Value)))})"); - } else if (match.Count > 1) { + case > 1: throw new ArgumentException ($"Bad value for expectedColors, {match.Count} Attributes had the same Value"); } var colorUsed = Array.IndexOf (expectedColors, match [0]).ToString () [0]; var userExpected = line [c]; - if (colorUsed != userExpected) { - throw new Exception ($"Colors used did not match expected at row {r} and col {c} (indexes start at 0). Color index used was {colorUsed} ({DescribeColor (val)}) but test expected {userExpected} ({DescribeColor (expectedColors [int.Parse (userExpected.ToString ())].Value)}) (these are indexes into the expectedColors array)"); - } + if (colorUsed != userExpected) throw new Exception ($"Colors used did not match expected at row {r} and col {c} (indexes start at 0). Color index used was {colorUsed} ({DescribeColor (val)}) but test expected {userExpected} ({DescribeColor (expectedColors [int.Parse (userExpected.ToString ())].Value)}) (these are indexes into the expectedColors array)"); } r++; @@ -313,7 +281,7 @@ public static void AssertDriverColorsAre (string expectedLook, params Attribute /// internal static void AssertDriverUsedColors (params Attribute [] expectedColors) { - var driver = ((FakeDriver)Application.Driver); + var driver = (FakeDriver)Application.Driver; var contents = driver.Contents; @@ -321,10 +289,10 @@ internal static void AssertDriverUsedColors (params Attribute [] expectedColors) var colorsUsed = new HashSet (); - for (int r = 0; r < driver.Rows; r++) { - for (int c = 0; c < driver.Cols; c++) { - int val = contents [r, c, 1]; - + for (var r = 0; r < driver.Rows; r++) { + for (var c = 0; c < driver.Cols; c++) { + var val = contents [r, c, 1]; + colorsUsed.Add (val); var match = toFind.FirstOrDefault (e => e.Value == val); @@ -333,16 +301,17 @@ internal static void AssertDriverUsedColors (params Attribute [] expectedColors) if (toFind.Any (e => e.Value == val)) { toFind.Remove (match); } - } - } + }} - if(toFind.Any()) { - var sb = new StringBuilder (); - sb.AppendLine ("The following colors were not used:" + string.Join ("; ", toFind.Select (a => DescribeColor (a)))); - sb.AppendLine ("Colors used were:" + string.Join ("; ", colorsUsed.Select (DescribeColor))); - throw new Exception (sb.ToString()); + if (!toFind.Any ()) { + return; } + var sb = new StringBuilder (); + sb.AppendLine ("The following colors were not used:" + string.Join ("; ", toFind.Select (a => DescribeColor (a)))); + sb.AppendLine ("Colors used were:" + string.Join ("; ", colorsUsed.Select (DescribeColor))); + throw new Exception (sb.ToString ()); } + private static object DescribeColor (int userExpected) { var a = new Attribute (userExpected); @@ -399,11 +368,11 @@ private static string ReplaceNewLinesToPlatformSpecific (string toReplace) { var replaced = toReplace; - if (Environment.NewLine.Length == 2 && !replaced.Contains ("\r\n")) { - replaced = replaced.Replace ("\n", Environment.NewLine); - } else if (Environment.NewLine.Length == 1) { - replaced = replaced.Replace ("\r\n", Environment.NewLine); - } + replaced = Environment.NewLine.Length switch { + 2 when !replaced.Contains ("\r\n") => replaced.Replace ("\n", Environment.NewLine), + 1 => replaced.Replace ("\r\n", Environment.NewLine), + var _ => replaced + }; return replaced; } From aaf1a80429676d1e9c7d154dcc5a317112a0b8ee Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Thu, 11 May 2023 13:35:32 +0200 Subject: [PATCH 28/99] Fighting with ScrollView --- Terminal.Gui/Application.cs | 31 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 7 +- Terminal.Gui/View/Frame.cs | 9 +- Terminal.Gui/View/ViewDrawing.cs | 207 +++++---- Terminal.Gui/View/ViewLayout.cs | 9 +- Terminal.Gui/Views/ScrollView.cs | 7 +- Terminal.Gui/Views/TextView.cs | 14 +- Terminal.Gui/Views/Toplevel.cs | 10 +- Terminal.Gui/Views/ToplevelOverlapped.cs | 2 +- UICatalog/Scenarios/CharacterMap.cs | 452 ++++++++++--------- UnitTests/View/Layout/LayoutTests.cs | 4 +- UnitTests/View/ViewTests.cs | 26 +- UnitTests/Views/ToplevelTests.cs | 6 +- 13 files changed, 423 insertions(+), 361 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index dd0c022138..9a73cd5364 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -765,42 +765,41 @@ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool } firstIteration = false; - if (state.Toplevel != Top - && (!Top._needsDisplay.IsEmpty || Top._subViewNeedsDisplay || Top.LayoutNeeded)) { - state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds); + if (state.Toplevel != Top && + (Top.NeedsDisplay|| Top.SubViewNeedsDisplay || Top.LayoutNeeded)) { + state.Toplevel.SetNeedsDisplay (state.Toplevel.Frame); + Top.Clear (); Top.Draw (); foreach (var top in _toplevels.Reverse ()) { if (top != Top && top != state.Toplevel) { top.SetNeedsDisplay (); top.SetSubViewNeedsDisplay (); + top.Clear (); top.Draw (); } } } if (_toplevels.Count == 1 && state.Toplevel == Top && (Driver.Cols != state.Toplevel.Frame.Width || Driver.Rows != state.Toplevel.Frame.Height) - && (!state.Toplevel._needsDisplay.IsEmpty || state.Toplevel._subViewNeedsDisplay || state.Toplevel.LayoutNeeded)) { - - Driver.SetAttribute (Colors.TopLevel.Normal); - state.Toplevel.Clear (new Rect (0, 0, Driver.Cols, Driver.Rows)); + && (state.Toplevel.NeedsDisplay || state.Toplevel.SubViewNeedsDisplay || state.Toplevel.LayoutNeeded)) { + state.Toplevel.Clear (); } - if (!state.Toplevel._needsDisplay.IsEmpty || state.Toplevel._subViewNeedsDisplay || state.Toplevel.LayoutNeeded - || OverlappedChildNeedsDisplay ()) { + if (state.Toplevel.NeedsDisplay || + state.Toplevel.SubViewNeedsDisplay || + state.Toplevel.LayoutNeeded || + OverlappedChildNeedsDisplay ()) { + state.Toplevel.Clear (); state.Toplevel.Draw (); - //if (state.Toplevel.SuperView != null) { - // state.Toplevel.SuperView?.OnRenderLineCanvas (); - //} else { - // state.Toplevel.OnRenderLineCanvas (); - //} state.Toplevel.PositionCursor (); Driver.Refresh (); } else { Driver.UpdateCursor (); } - if (state.Toplevel != Top && !state.Toplevel.Modal - && (!Top._needsDisplay.IsEmpty || Top._subViewNeedsDisplay || Top.LayoutNeeded)) { + if (state.Toplevel != Top && + !state.Toplevel.Modal && + (Top.NeedsDisplay|| Top.SubViewNeedsDisplay || Top.LayoutNeeded)) { Top.Draw (); } } diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 167fa7695b..d37346a4e0 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -291,7 +291,12 @@ public Attribute CurrentAttribute { /// Implementations should call base.SetAttribute(c). /// /// C. - public void SetAttribute (Attribute c) => CurrentAttribute = c; + public Attribute SetAttribute (Attribute c) + { + var prevAttribute = CurrentAttribute; + CurrentAttribute = c; + return prevAttribute; + } /// /// Make the attribute for the foreground and background colors. diff --git a/Terminal.Gui/View/Frame.cs b/Terminal.Gui/View/Frame.cs index beed23d88b..a69b418763 100644 --- a/Terminal.Gui/View/Frame.cs +++ b/Terminal.Gui/View/Frame.cs @@ -126,7 +126,9 @@ public virtual void OnDrawSubViews (Rect clipRect) /// public override void OnDrawContent (Rect contentArea) { - if (Thickness == Thickness.Empty) return; + if (Thickness == Thickness.Empty) { + return; + } if (ColorScheme != null) { Driver.SetAttribute (GetNormalColor ()); @@ -139,9 +141,6 @@ public override void OnDrawContent (Rect contentArea) } //Driver.SetAttribute (Colors.Error.Normal); - - var prevClip = SetClip (Frame); - var screenBounds = ViewToScreen (Frame); // This just draws/clears the thickness, not the insides. @@ -305,7 +304,7 @@ public override void OnDrawContent (Rect contentArea) } } - Driver.Clip = prevClip; + ClearNeedsDisplay (); } // TODO: v2 - Frame.BorderStyle is temporary - Eventually the border will be drawn by a "BorderView" that is a subview of the Frame. diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs index 5dc5236b3c..013a61c391 100644 --- a/Terminal.Gui/View/ViewDrawing.cs +++ b/Terminal.Gui/View/ViewDrawing.cs @@ -9,7 +9,7 @@ public partial class View { /// /// The color scheme for this view, if it is not defined, it returns the 's - /// color scheme. + /// color scheme. /// public virtual ColorScheme ColorScheme { get { @@ -34,6 +34,10 @@ public virtual ColorScheme ColorScheme { /// If it's overridden can return other values. public virtual Attribute GetNormalColor () { + if (ColorScheme == null) { + var cs = new ColorScheme (); + return Enabled ? cs.Normal : cs.Disabled; + } return Enabled ? ColorScheme.Normal : ColorScheme.Disabled; } @@ -76,72 +80,103 @@ public void AddRune (int col, int row, Rune ch) } /// - /// Removes the and the setting on this view. + /// Clears and . /// protected void ClearNeedsDisplay () { - _needsDisplay = Rect.Empty; + _needsDisplayRect = Rect.Empty; _subViewNeedsDisplay = false; } - // The view-relative region that needs to be redrawn - internal Rect _needsDisplay { get; private set; } = Rect.Empty; + // The view-relative region that needs to be redrawn. Marked internal for unit tests. + internal Rect _needsDisplayRect = Rect.Empty; + + /// + /// Gets or sets whether the view needs to be redrawn. + /// + public bool NeedsDisplay { + get => _needsDisplayRect != Rect.Empty; + set { + if (value) { + SetNeedsDisplay (); + } else { + ClearNeedsDisplay (); + } + } + } /// - /// Sets a flag indicating this view needs to be redisplayed because its state has changed. + /// Sets the area of this view needing to be redrawn to . /// public void SetNeedsDisplay () { - if (!IsInitialized) return; + if (!IsInitialized) { + return; + } SetNeedsDisplay (Bounds); } /// - /// Flags the view-relative region on this View as needing to be redrawn. + /// Expands the area of this view needing to be redrawn to include . /// /// The view-relative region that needs to be redrawn. public void SetNeedsDisplay (Rect region) { - if (_needsDisplay.IsEmpty) { - _needsDisplay = region; + if (_needsDisplayRect.IsEmpty) { + _needsDisplayRect = region; } else { - var x = Math.Min (_needsDisplay.X, region.X); - var y = Math.Min (_needsDisplay.Y, region.Y); - var w = Math.Max (_needsDisplay.Width, region.Width); - var h = Math.Max (_needsDisplay.Height, region.Height); - _needsDisplay = new Rect (x, y, w, h); + var x = Math.Min (_needsDisplayRect.X, region.X); + var y = Math.Min (_needsDisplayRect.Y, region.Y); + var w = Math.Max (_needsDisplayRect.Width, region.Width); + var h = Math.Max (_needsDisplayRect.Height, region.Height); + _needsDisplayRect = new Rect (x, y, w, h); } _superView?.SetSubViewNeedsDisplay (); - if (_subviews == null) + if (_needsDisplayRect.X < Bounds.X || + _needsDisplayRect.Y < Bounds.Y || + _needsDisplayRect.Width > Bounds.Width || + _needsDisplayRect.Height > Bounds.Height) { + Margin?.SetNeedsDisplay (Margin.Bounds); + Border?.SetNeedsDisplay (Border.Bounds); + Padding?.SetNeedsDisplay (Padding.Bounds); + } + + if (_subviews == null) { return; + } - foreach (var view in _subviews) - if (view.Frame.IntersectsWith (region)) { - var childRegion = Rect.Intersect (view.Frame, region); - childRegion.X -= view.Frame.X; - childRegion.Y -= view.Frame.Y; - view.SetNeedsDisplay (childRegion); + foreach (var subview in _subviews) { + if (subview.Frame.IntersectsWith (region)) { + var subviewRegion = Rect.Intersect (subview.Frame, region); + subviewRegion.X -= subview.Frame.X; + subviewRegion.Y -= subview.Frame.Y; + subview.SetNeedsDisplay (subviewRegion); } + } } - internal bool _subViewNeedsDisplay { get; private set; } + /// + /// Gets whether any Subviews need to be redrawn. + /// + public bool SubViewNeedsDisplay { + get => _subViewNeedsDisplay; + } + bool _subViewNeedsDisplay; /// - /// Indicates that any Subviews (in the list) need to be repainted. + /// Indicates that any Subviews (in the list) need to be redrawn. /// public void SetSubViewNeedsDisplay () { - if (_subViewNeedsDisplay) { - return; - } _subViewNeedsDisplay = true; - if (_superView != null && !_superView._subViewNeedsDisplay) + if (_superView is { _subViewNeedsDisplay: false }) { _superView.SetSubViewNeedsDisplay (); + } } /// - /// Clears the view region with the current color. + /// Clears the view Frame with the normal background color.s /// /// /// @@ -150,32 +185,24 @@ public void SetSubViewNeedsDisplay () /// public void Clear () { - var h = Frame.Height; - var w = Frame.Width; - for (var line = 0; line < h; line++) { - Move (0, line); - for (var col = 0; col < w; col++) - Driver.AddRune (' '); - } + var prev = Driver.SetAttribute (GetNormalColor ()); + Driver.FillRect (Frame); + Driver.SetAttribute (prev); } // BUGBUG: Stupid that this takes screen-relative. We should have a tenet that says // "View APIs only deal with View-relative coords". /// - /// Clears the specified region with the current color. + /// Clears the specified region with the normal background. /// /// /// /// The screen-relative region to clear. public void Clear (Rect regionScreen) { - var h = regionScreen.Height; - var w = regionScreen.Width; - for (var line = regionScreen.Y; line < regionScreen.Y + h; line++) { - Driver.Move (regionScreen.X, line); - for (var col = 0; col < w; col++) - Driver.AddRune (' '); - } + var prev = Driver.SetAttribute (GetNormalColor ()); + Driver.FillRect (regionScreen); + Driver.SetAttribute (prev); } // Clips a rectangle in screen coordinates to the dimensions currently available on the screen @@ -190,7 +217,7 @@ internal Rect ScreenClip (Rect regionScreen) } /// - /// Sets the 's clip region to . + /// Expands the 's clip region to include . /// /// The current screen-relative clip region, which can be then re-applied by setting . /// @@ -203,14 +230,12 @@ internal Rect ScreenClip (Rect regionScreen) /// public Rect ClipToBounds () { - var clip = Bounds; - - return SetClip (clip); + return SetClip (Bounds); } // BUGBUG: v2 - SetClip should return VIEW-relative so that it can be used to reset it; using Driver.Clip directly should not be necessary. /// - /// Sets the clip region to the specified view-relative region. + /// Expands the clip region to include the specified view-relative region. /// /// The current screen-relative clip region, which can be then re-applied by setting . /// View-relative clip region. @@ -306,22 +331,11 @@ public virtual bool OnDrawFrames () return false; } - var prevClip = Driver.Clip; - Driver.Clip = ViewToScreen (Frame); - - // TODO: Figure out what we should do if we have no superview - //if (SuperView != null) { - // TODO: Clipping is disabled for now to ensure we see errors - Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows);// screenBounds;// SuperView.ClipToBounds (); - //} - // Each of these renders lines to either this View's LineCanvas // Those lines will be finally rendered in OnRenderLineCanvas - Margin?.OnDrawContent (Bounds); - Border?.OnDrawContent (Bounds); - Padding?.OnDrawContent (Bounds); - - Driver.Clip = prevClip; + Margin?.OnDrawContent (Margin.Bounds); + Border?.OnDrawContent (Border.Bounds); + Padding?.OnDrawContent (Padding.Bounds); return true; } @@ -348,7 +362,6 @@ public void Draw () if (!CanBeVisible (this)) { return; } - OnDrawFrames (); var prevClip = ClipToBounds (); @@ -369,7 +382,6 @@ public void Draw () Driver.Clip = prevClip; OnRenderLineCanvas (); - // Invoke DrawContentCompleteEvent OnDrawContentComplete (Bounds); @@ -391,21 +403,19 @@ public virtual bool OnRenderLineCanvas () return false; } - //Driver.SetAttribute (new Attribute(Color.White, Color.Black)); - // If we have a SuperView, it'll render our frames. if (!SuperViewRendersLineCanvas && LineCanvas.Bounds != Rect.Empty) { foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map Driver.SetAttribute (p.Value.Attribute?.Value ?? ColorScheme.Normal); Driver.Move (p.Key.X, p.Key.Y); - Driver.AddRune (p.Value.Rune.Value); + Driver.AddRune (p.Value.Rune!.Value); } LineCanvas.Clear (); } - if (Subviews.Where (s => s.SuperViewRendersLineCanvas).Count () > 0) { + if (Subviews.Any (s => s.SuperViewRendersLineCanvas)) { foreach (var subview in Subviews.Where (s => s.SuperViewRendersLineCanvas == true)) { - // Combine the LineCavas' + // Combine the LineCanvas' LineCanvas.Merge (subview.LineCanvas); subview.LineCanvas.Clear (); } @@ -443,38 +453,45 @@ public virtual bool OnRenderLineCanvas () /// public virtual void OnDrawContent (Rect contentArea) { - if (SuperView != null) { - Clear (ViewToScreen (Bounds)); - } + if (NeedsDisplay) { + if (SuperView != null) { + Clear (ViewToScreen (Bounds)); + } - if (!ustring.IsNullOrEmpty (TextFormatter.Text)) { - if (TextFormatter != null) { - TextFormatter.NeedsFormat = true; + if (!ustring.IsNullOrEmpty (TextFormatter.Text)) { + if (TextFormatter != null) { + TextFormatter.NeedsFormat = true; + } + // This should NOT clear + TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (), + HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (), + Rect.Empty, false); + SetSubViewNeedsDisplay (); } - // This should NOT clear - TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (), - HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (), - Rect.Empty, false); - SetSubViewNeedsDisplay (); } // Draw subviews // TODO: Implement OnDrawSubviews (cancelable); - if (_subviews != null) { - foreach (var view in _subviews) { - if (view.Visible) { //!view._needsDisplay.IsEmpty || view._childNeedsDisplay || view.LayoutNeeded) { - if (true) { //view.Frame.IntersectsWith (bounds)) { // && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) { - if (view.LayoutNeeded) { - view.LayoutSubviews (); - } - - // Draw the subview - // Use the view's bounds (view-relative; Location will always be (0,0) - //if (view.Visible && view.Frame.Width > 0 && view.Frame.Height > 0) { - view.Draw (); - //} - } + if (_subviews != null && SubViewNeedsDisplay) { + var subviewsNeedingDraw = _subviews.Where ( + view => view.Visible && + (view.NeedsDisplay || + view.SubViewNeedsDisplay || + view.LayoutNeeded) + ); + + foreach (var view in subviewsNeedingDraw) { + //view.Frame.IntersectsWith (bounds)) { + // && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) { + if (view.LayoutNeeded) { + view.LayoutSubviews (); } + + // Draw the subview + // Use the view's bounds (view-relative; Location will always be (0,0) + //if (view.Visible && view.Frame.Width > 0 && view.Frame.Height > 0) { + view.Draw (); + //} } } } diff --git a/Terminal.Gui/View/ViewLayout.cs b/Terminal.Gui/View/ViewLayout.cs index f268ea02cd..5afa2de5c3 100644 --- a/Terminal.Gui/View/ViewLayout.cs +++ b/Terminal.Gui/View/ViewLayout.cs @@ -470,16 +470,15 @@ protected virtual void OnResizeNeeded () internal void SetNeedsLayout () { - if (LayoutNeeded) + if (LayoutNeeded) { return; + } LayoutNeeded = true; foreach (var view in Subviews) { view.SetNeedsLayout (); } TextFormatter.NeedsFormat = true; - if (SuperView == null) - return; - SuperView.SetNeedsLayout (); + SuperView?.SetNeedsLayout (); } /// @@ -541,7 +540,7 @@ public virtual void ViewToScreen (int col, int row, out int rcol, out int rrow, /// /// Converts a region in view-relative coordinates to screen-relative coordinates. /// - internal Rect ViewToScreen (Rect region) + public Rect ViewToScreen (Rect region) { ViewToScreen (region.X, region.Y, out var x, out var y, clamped: false); return new Rect (x, y, region.Width, region.Height); diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index a1dcf9fa8a..d5f32f7a02 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -13,6 +13,7 @@ using System; using System.Linq; +using NStack; namespace Terminal.Gui { /// @@ -360,10 +361,12 @@ public override void OnDrawContent (Rect contentArea) SetViewsNeedsDisplay (); var savedClip = ClipToBounds (); - Driver.SetAttribute (GetNormalColor ()); + // TODO: It's bad practice for views to always clear a view. It negates clipping. Clear (); - contentView.Draw (); + if (!ustring.IsNullOrEmpty (Text) || Subviews.Count > 0) { + contentView.Draw (); + } if (autoHideScrollBars) { ShowHideScrollBars (); diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 413b829e93..a9c3bdcdda 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2744,10 +2744,10 @@ public void InsertText (string toAdd) InsertText (new KeyEvent () { Key = key }); } - if (_needsDisplay.IsEmpty) { - PositionCursor (); - } else { + if (NeedsDisplay) { Adjust (); + } else { + PositionCursor (); } } @@ -2902,7 +2902,7 @@ void Adjust () { var offB = OffSetBackground (); var line = GetCurrentLine (); - bool need = !_needsDisplay.IsEmpty || _wrapNeeded; + bool need = NeedsDisplay|| _wrapNeeded; var tSize = TextModel.DisplaySize (line, -1, -1, false, TabWidth); var dSize = TextModel.DisplaySize (line, _leftColumn, _currentColumn, true, TabWidth); if (!_wordWrap && _currentColumn < _leftColumn) { @@ -3976,10 +3976,10 @@ public override bool OnKeyUp (KeyEvent kb) void DoNeededAction () { - if (_needsDisplay.IsEmpty) { - PositionCursor (); - } else { + if (NeedsDisplay) { Adjust (); + } else { + PositionCursor (); } } diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs index cbb392e4ce..270e88542a 100644 --- a/Terminal.Gui/Views/Toplevel.cs +++ b/Terminal.Gui/Views/Toplevel.cs @@ -757,9 +757,10 @@ public override void OnDrawContent (Rect contentArea) return; } - if (!_needsDisplay.IsEmpty || _subViewNeedsDisplay || LayoutNeeded) { - Driver.SetAttribute (GetNormalColor ()); - Clear (ViewToScreen (Bounds)); + if (NeedsDisplay || SubViewNeedsDisplay || LayoutNeeded) { + //Driver.SetAttribute (GetNormalColor ()); + // TODO: It's bad practice for views to always clear. Defeats the purpose of clipping etc... + Clear (); LayoutSubviews (); PositionToplevels (); @@ -776,9 +777,10 @@ public override void OnDrawContent (Rect contentArea) } } + // This should not be here, but in base foreach (var view in Subviews) { if (view.Frame.IntersectsWith (Bounds) && !OutsideTopFrame (this)) { - view.SetNeedsLayout (); + //view.SetNeedsLayout (); view.SetNeedsDisplay (view.Bounds); view.SetSubViewNeedsDisplay (); } diff --git a/Terminal.Gui/Views/ToplevelOverlapped.cs b/Terminal.Gui/Views/ToplevelOverlapped.cs index c28f4d7c79..1c95d66477 100644 --- a/Terminal.Gui/Views/ToplevelOverlapped.cs +++ b/Terminal.Gui/Views/ToplevelOverlapped.cs @@ -97,7 +97,7 @@ static bool OverlappedChildNeedsDisplay () } foreach (var top in _toplevels) { - if (top != Current && top.Visible && (!top._needsDisplay.IsEmpty || top._subViewNeedsDisplay || top.LayoutNeeded)) { + if (top != Current && top.Visible && (top.NeedsDisplay || top.SubViewNeedsDisplay || top.LayoutNeeded)) { OverlappedTop.SetSubViewNeedsDisplay (); return true; } diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index c7e7e2d3ec..005ed02d2c 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -4,8 +4,14 @@ using NStack; using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Text.Unicode; +using System.Threading.Tasks; using Terminal.Gui; +using static System.Net.WebRequestMethods; using Rune = System.Rune; namespace UICatalog.Scenarios; @@ -22,6 +28,7 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("ScrollView")] public class CharacterMap : Scenario { CharMap _charMap; + Label _errorLabel; public override void Setup () { _charMap = new CharMap () { @@ -35,63 +42,73 @@ public override void Setup () Win.Add (jumpLabel); var jumpEdit = new TextField () { X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, Caption = "e.g. 01BE3" }; Win.Add (jumpEdit); - var errorLabel = new Label ("") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] }; - Win.Add (errorLabel); - jumpEdit.TextChanged += (s, e) => { - uint result = 0; - if (jumpEdit.Text.Length == 0) return; - try { - result = Convert.ToUInt32 (jumpEdit.Text.ToString (), 10); - } catch (OverflowException) { - errorLabel.Text = $"Invalid (overflow)"; - return; - } catch (FormatException) { - try { - result = Convert.ToUInt32 (jumpEdit.Text.ToString (), 16); - } catch (OverflowException) { - errorLabel.Text = $"Invalid (overflow)"; - return; - } catch (FormatException) { - errorLabel.Text = $"Invalid (can't parse)"; - return; - } - } - errorLabel.Text = $"U+{result:x4}"; - _charMap.SelectedCodePoint = result; - }; - - var radioItems = new (ustring radioLabel, uint start, uint end) [UnicodeRange.Ranges.Count]; + _errorLabel = new Label ("") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] }; + Win.Add (_errorLabel); + jumpEdit.TextChanged += JumpEdit_TextChanged; + var rangeItems = new (ustring label, int start, int end) [UnicodeRange.Ranges.Count]; for (var i = 0; i < UnicodeRange.Ranges.Count; i++) { var range = UnicodeRange.Ranges [i]; - radioItems [i] = CreateRadio (range.Category, range.Start, range.End); + rangeItems [i] = CreateRangeItem (range.Category, range.Start, range.End); } - (ustring radioLabel, uint start, uint end) CreateRadio (ustring title, uint start, uint end) + static (ustring label, int start, int end) CreateRangeItem (ustring title, int start, int end) { return ($"{title} (U+{start:x5}-{end:x5})", start, end); } - var label = new Label ("Jump To Unicode Block:") { X = Pos.Right (_charMap) + 1, Y = Pos.Bottom (jumpLabel) + 1 }; + var label = new Label ("Jump To Unicode Range:") { X = Pos.Right (_charMap) + 1, Y = Pos.Bottom (jumpLabel) + 1 }; Win.Add (label); - var jumpList = new ListView (radioItems.Select (t => t.radioLabel).ToArray ()) { + var jumpList = new ListView (rangeItems.Select (t => t.label).ToArray ()) { X = Pos.X (label) + 1, Y = Pos.Bottom (label), - Width = radioItems.Max (r => r.radioLabel.Length) + 2, + Width = rangeItems.Max (r => r.label.Length) + 2, Height = Dim.Fill (1), SelectedItem = 0 }; jumpList.SelectedItemChanged += (s, args) => { - _charMap.StartCodePoint = radioItems [jumpList.SelectedItem].start; + _charMap.StartCodePoint = rangeItems [jumpList.SelectedItem].start; + }; + + _charMap.SelectedCodePointChanged += (s, args) => { + jumpEdit.TextChanged -= JumpEdit_TextChanged; + jumpEdit.Text = $"{args.Item:X6}"; + jumpEdit.TextChanged += JumpEdit_TextChanged; }; Win.Add (jumpList); + _charMap.SelectedCodePoint = 0; //jumpList.Refresh (); _charMap.SetFocus (); _charMap.Width = Dim.Fill () - jumpList.Width; } + + private void JumpEdit_TextChanged (object sender, TextChangedEventArgs e) + { + var jumpEdit = sender as TextField; + var result = 0; + if (jumpEdit.Text.Length == 0) return; + try { + result = Convert.ToInt32 (jumpEdit.Text.ToString (), 10); + } catch (OverflowException) { + _errorLabel.Text = $"Invalid (overflow)"; + return; + } catch (FormatException) { + try { + result = Convert.ToInt32 (jumpEdit.Text.ToString (), 16); + } catch (OverflowException) { + _errorLabel.Text = $"Invalid (overflow)"; + return; + } catch (FormatException) { + _errorLabel.Text = $"Invalid (can't parse)"; + return; + } + } + _errorLabel.Text = $"U+{result:x4}"; + _charMap.SelectedCodePoint = result; + } } class CharMap : ScrollView { @@ -100,7 +117,7 @@ class CharMap : ScrollView { /// Specifies the starting offset for the character map. The default is 0x2500 /// which is the Box Drawing characters. /// - public uint StartCodePoint { + public int StartCodePoint { get => _start; set { _start = value; @@ -110,16 +127,18 @@ public uint StartCodePoint { } } + public event EventHandler SelectedCodePointChanged; + /// /// Specifies the starting offset for the character map. The default is 0x2500 /// which is the Box Drawing characters. /// - public uint SelectedCodePoint { + public int SelectedCodePoint { get => _selected; set { _selected = value; - int row = (int)_selected / 16; - int height = (Bounds.Height / ROW_HEIGHT) - (ShowHorizontalScrollIndicator ? 2 : 1); + var row = (int)_selected / 16; + var height = (Bounds.Height / ROW_HEIGHT) - (ShowHorizontalScrollIndicator ? 2 : 1); if (row + ContentOffset.Y < 0) { // Moving up. ContentOffset = new Point (ContentOffset.X, row); @@ -127,8 +146,8 @@ public uint SelectedCodePoint { // Moving down. ContentOffset = new Point (ContentOffset.X, Math.Min (row, row - height + ROW_HEIGHT)); } - int col = (((int)_selected - (row * 16)) * COLUMN_WIDTH); - int width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth); + var col = (((int)_selected - (row * 16)) * COLUMN_WIDTH); + var width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth); if (col + ContentOffset.X < 0) { // Moving left. ContentOffset = new Point (col, ContentOffset.Y); @@ -136,29 +155,28 @@ public uint SelectedCodePoint { // Moving right. ContentOffset = new Point (Math.Min (col, col - width + COLUMN_WIDTH), ContentOffset.Y); } - SetNeedsDisplay (); - UpdateCursor (); + SetNeedsDisplay (); + SelectedCodePointChanged?.Invoke (this, new ListViewItemEventArgs (_selected, null)); } } - void UpdateCursor () + public override void PositionCursor () { - int row = (int)_selected / 16; - int col = (((int)_selected - (row * 16)) * COLUMN_WIDTH); + var row = (int)_selected / 16; + var col = (((int)_selected - (row * 16)) * COLUMN_WIDTH); Move (col + ContentOffset.X + RowLabelWidth + 1, row + ContentOffset.Y + 1); - Driver.UpdateCursor (); } - uint _start = 0; - uint _selected = 0; + int _start = 0; + int _selected = 0; public const int COLUMN_WIDTH = 2; public const int ROW_HEIGHT = 1; - public static uint MaxCodePoint => 0x10FFFF; + public static int MaxCodePoint => 0x10FFFF; public static int RowLabelWidth => $"U+{MaxCodePoint:x5}".Length + 1; public static int RowWidth => RowLabelWidth + (COLUMN_WIDTH * 16); @@ -167,6 +185,7 @@ public CharMap () { ColorScheme = Colors.Dialog; CanFocus = true; + KeepContentAlwaysInViewport = true; ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + (ShowHorizontalScrollIndicator ? 2 : 1))); @@ -195,12 +214,12 @@ public CharMap () return true; }); AddCommand (Command.PageUp, () => { - var page = (uint)(Bounds.Height / ROW_HEIGHT - 1) * 16; + var page = (Bounds.Height / ROW_HEIGHT - 1) * 16; SelectedCodePoint -= Math.Min (page, SelectedCodePoint); return true; }); AddCommand (Command.PageDown, () => { - var page = (uint)(Bounds.Height / ROW_HEIGHT - 1) * 16; + var page = (Bounds.Height / ROW_HEIGHT - 1) * 16; SelectedCodePoint += Math.Min (page, MaxCodePoint - SelectedCodePoint); return true; }); @@ -221,7 +240,10 @@ public CharMap () MouseClick += Handle_MouseClick; // Reset cursor on resize - LayoutComplete += (s, a) => { UpdateCursor (); }; + //LayoutComplete += (s, a) => { UpdateCursor (); }; + + //DrawContent += CharMap_DrawContent; + } private void CopyCodePoint () @@ -231,13 +253,11 @@ private void CopyCodePoint () private void CopyGlyph () { - Clipboard.Contents = $"{new Rune (SelectedCodePoint)}"; + Clipboard.Contents = $"{new Rune ((char)SelectedCodePoint)}"; } public override void OnDrawContent (Rect contentArea) { - base.OnDrawContent (contentArea); - if (ShowHorizontalScrollIndicator && ContentSize.Height < (int)(MaxCodePoint / 16 + 2)) { ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 2)); int row = (int)_selected / 16; @@ -249,26 +269,32 @@ public override void OnDrawContent (Rect contentArea) } else { ContentOffset = new Point (ContentOffset.X - col, ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); } - SetNeedsDisplay (); + //SetNeedsDisplay (); } else if (!ShowHorizontalScrollIndicator && ContentSize.Height > (int)(MaxCodePoint / 16 + 1)) { ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 1)); // Snap 1st column into view if it's been scrolled horizontally ContentOffset = new Point (0, ContentOffset.Y < -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); - SetNeedsDisplay (); + //SetNeedsDisplay (); } + base.OnDrawContent (contentArea); + } + //public void CharMap_DrawContent (object s, DrawEventArgs a) public override void OnDrawContentComplete (Rect contentArea) { Rect viewport = new Rect (ContentOffset, new Size (Math.Max (Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0), 0), Math.Max (Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0), 0))); - var oldClip = Driver.Clip; - Driver.Clip = Bounds; - if (!ShowHorizontalScrollIndicator) { - // Redraw doesn't know about the scroll indicators, so if off, add one to height - Driver.Clip = new Rect (Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height + 1); + var oldClip = ClipToBounds (); + if (ShowHorizontalScrollIndicator) { + // ClipToBounds doesn't know about the scroll indicators, so if off, subtract one from height + Driver.Clip = new Rect (Driver.Clip.Location, new Size (Driver.Clip.Width, Driver.Clip.Height - 1)); + } + if (ShowVerticalScrollIndicator) { + // ClipToBounds doesn't know about the scroll indicators, so if off, subtract one from width + Driver.Clip = new Rect (Driver.Clip.Location, new Size (Driver.Clip.Width - 1, Driver.Clip.Height)); } Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.Focus); Move (0, 0); @@ -320,26 +346,30 @@ void Handle_MouseClick (object sender, MouseEventEventArgs args) return; } - var row = (me.Y - 1); + var row = me.Y - 1; var col = (me.X - RowLabelWidth - ContentOffset.X) / COLUMN_WIDTH; - uint val = (uint)((((uint)row - (uint)ContentOffset.Y) * 16) + col); + if (row < 0 || row > Bounds.Height || col < 0 || col > 15) { + return; + } + + var val = (row - ContentOffset.Y) * 16 + col; if (val > MaxCodePoint) { return; } if (me.Flags == MouseFlags.Button1Clicked) { - SelectedCodePoint = (uint)val; + SelectedCodePoint = val; return; } if (me.Flags == MouseFlags.Button1DoubleClicked) { - SelectedCodePoint = (uint)val; + SelectedCodePoint = val; ShowDetails (); return; } if (me.Flags == _contextMenu.MouseFlags) { - SelectedCodePoint = (uint)val; + SelectedCodePoint = val; _contextMenu = new ContextMenu (me.X + 1, me.Y + 1, new MenuBarItem (new MenuItem [] { new MenuItem ("_Copy Glyph", "", () => CopyGlyph (), null, null, Key.C | Key.CtrlMask), @@ -352,168 +382,172 @@ void Handle_MouseClick (object sender, MouseEventEventArgs args) } } + public static string ToCamelCase (string str) + { + if (string.IsNullOrEmpty (str)) { + return str; + } + + TextInfo textInfo = new CultureInfo ("en-US", false).TextInfo; + + str = textInfo.ToLower (str); + str = textInfo.ToTitleCase (str); + + return str; + } + void ShowDetails () { - var title = $"{new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x4}"; - switch (MessageBox.Query (title, $"{new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x4}", "Copy _Glyph", "Copy Code _Point", "Cancel")) { - case 0: - CopyGlyph (); - break; - case 1: - CopyCodePoint (); - break; + var client = new UcdApiClient (); + string decResponse = string.Empty; + + var waitIndicator = new Dialog (new Button ("Cancel")) { + Title = "Getting Code Point Information", + X = Pos.Center (), + Y = Pos.Center (), + Height = 7, + Width = 50 + }; + var errorLabel = new Label () { + Text = UcdApiClient.BaseUrl, + AutoSize = false, + X = 0, + Y = 1, + Width = Dim.Fill (), + Height = Dim.Fill (1), + TextAlignment = TextAlignment.Centered + }; + var spinner = new SpinnerView () { + X = Pos.Center (), + Y = Pos.Center (), + Style = new SpinnerStyle.Aesthetic (), + + }; + spinner.AutoSpin (); + waitIndicator.Add (errorLabel); + waitIndicator.Add (spinner); + waitIndicator.Ready += async (s, a) => { + try { + decResponse = await client.GetCodepointDec ((int)SelectedCodePoint); + } catch (HttpRequestException e) { + (s as Dialog).Text = e.Message; + Application.MainLoop.Invoke (() => { + spinner.Visible = false; + errorLabel.Text = e.Message; + errorLabel.ColorScheme = Colors.ColorSchemes ["Error"]; + errorLabel.Visible = true; + }); + } + (s as Dialog)?.RequestStop (); + }; + Application.Run (waitIndicator); + + if (!string.IsNullOrEmpty (decResponse)) { + string name = string.Empty; + + using (JsonDocument document = JsonDocument.Parse (decResponse)) { + JsonElement root = document.RootElement; + + // Get a property by name and output its value + if (root.TryGetProperty ("name", out JsonElement nameElement)) { + name = nameElement.GetString (); + } + + //// Navigate to a nested property and output its value + //if (root.TryGetProperty ("property3", out JsonElement property3Element) + //&& property3Element.TryGetProperty ("nestedProperty", out JsonElement nestedPropertyElement)) { + // Console.WriteLine (nestedPropertyElement.GetString ()); + //} + } + + var title = $"{ToCamelCase (name)} - {new Rune ((char)SelectedCodePoint)} U+{SelectedCodePoint:x4}"; + switch (MessageBox.Query (title, decResponse, "Copy _Glyph", "Copy Code _Point", "Cancel")) { + case 0: + CopyGlyph (); + break; + case 1: + CopyCodePoint (); + break; + } + } else { + MessageBox.ErrorQuery ("Code Point API", $"{UcdApiClient.BaseUrl} did not return a result.", "Ok"); } + // BUGBUG: This is a workaround for some weird ScrollView related mouse grab bug + Application.GrabMouse (this); + PositionCursor (); + Driver.SetCursorVisibility (CursorVisibility.Default); + } + public override bool OnEnter (View view) { if (IsInitialized) { - UpdateCursor (); - } else { - Application.Driver.SetCursorVisibility (CursorVisibility.Invisible); - + Application.Driver.SetCursorVisibility (CursorVisibility.Default); } return base.OnEnter (view); } } +public class UcdApiClient { + private static readonly HttpClient httpClient = new HttpClient (); + public const string BaseUrl = "https://ucdapi.org/unicode/latest/"; + + public async Task GetCodepointHex (string hex) + { + var response = await httpClient.GetAsync ($"{BaseUrl}codepoint/hex/{hex}"); + response.EnsureSuccessStatusCode (); + return await response.Content.ReadAsStringAsync (); + } + + public async Task GetCodepointDec (int dec) + { + var response = await httpClient.GetAsync ($"{BaseUrl}codepoint/dec/{dec}"); + response.EnsureSuccessStatusCode (); + return await response.Content.ReadAsStringAsync (); + } + + public async Task GetChars (string chars) + { + var response = await httpClient.GetAsync ($"{BaseUrl}chars/{Uri.EscapeDataString (chars)}"); + response.EnsureSuccessStatusCode (); + return await response.Content.ReadAsStringAsync (); + } + + public async Task GetCharsName (string chars) + { + var response = await httpClient.GetAsync ($"{BaseUrl}chars/{Uri.EscapeDataString (chars)}/name"); + response.EnsureSuccessStatusCode (); + return await response.Content.ReadAsStringAsync (); + } +} + + class UnicodeRange { - public uint Start; - public uint End; + public int Start; + public int End; public string Category; - public UnicodeRange (uint start, uint end, string category) + public UnicodeRange (int start, int end, string category) { this.Start = start; this.End = end; this.Category = category; } - public static List Ranges = new List { - new UnicodeRange (0x0000, 0x001F, "ASCII Control Characters"), - new UnicodeRange (0x0080, 0x009F, "C0 Control Characters"), - new UnicodeRange(0x1100, 0x11ff,"Hangul Jamo"), // This is where wide chars tend to start - new UnicodeRange(0x20A0, 0x20CF,"Currency Symbols"), - new UnicodeRange(0x2100, 0x214F,"Letterlike Symbols"), - new UnicodeRange(0x2160, 0x218F, "Roman Numerals"), - new UnicodeRange(0x2190, 0x21ff,"Arrows" ), - new UnicodeRange(0x2200, 0x22ff,"Mathematical symbols"), - new UnicodeRange(0x2300, 0x23ff,"Miscellaneous Technical"), - new UnicodeRange(0x24B6, 0x24e9,"Circled Latin Capital Letters"), - new UnicodeRange(0x1F130, 0x1F149,"Squared Latin Capital Letters"), - new UnicodeRange(0x2500, 0x25ff,"Box Drawing & Geometric Shapes"), - new UnicodeRange(0x2600, 0x26ff,"Miscellaneous Symbols"), - new UnicodeRange(0x2700, 0x27ff,"Dingbats"), - new UnicodeRange(0x2800, 0x28ff,"Braille"), - new UnicodeRange(0x2b00, 0x2bff,"Miscellaneous Symbols and Arrows"), - new UnicodeRange(0xFB00, 0xFb4f,"Alphabetic Presentation Forms"), - new UnicodeRange(0x12400, 0x1240f,"Cuneiform Numbers and Punctuation"), - new UnicodeRange(0x1FA00, 0x1FA0f,"Chess Symbols"), - - new UnicodeRange (0x0020 ,0x007F ,"Basic Latin"), - new UnicodeRange (0x00A0 ,0x00FF ,"Latin-1 Supplement"), - new UnicodeRange (0x0100 ,0x017F ,"Latin Extended-A"), - new UnicodeRange (0x0180 ,0x024F ,"Latin Extended-B"), - new UnicodeRange (0x0250 ,0x02AF ,"IPA Extensions"), - new UnicodeRange (0x02B0 ,0x02FF ,"Spacing Modifier Letters"), - new UnicodeRange (0x0300 ,0x036F ,"Combining Diacritical Marks"), - new UnicodeRange (0x0370 ,0x03FF ,"Greek and Coptic"), - new UnicodeRange (0x0400 ,0x04FF ,"Cyrillic"), - new UnicodeRange (0x0500 ,0x052F ,"Cyrillic Supplementary"), - new UnicodeRange (0x0530 ,0x058F ,"Armenian"), - new UnicodeRange (0x0590 ,0x05FF ,"Hebrew"), - new UnicodeRange (0x0600 ,0x06FF ,"Arabic"), - new UnicodeRange (0x0700 ,0x074F ,"Syriac"), - new UnicodeRange (0x0780 ,0x07BF ,"Thaana"), - new UnicodeRange (0x0900 ,0x097F ,"Devanagari"), - new UnicodeRange (0x0980 ,0x09FF ,"Bengali"), - new UnicodeRange (0x0A00 ,0x0A7F ,"Gurmukhi"), - new UnicodeRange (0x0A80 ,0x0AFF ,"Gujarati"), - new UnicodeRange (0x0B00 ,0x0B7F ,"Oriya"), - new UnicodeRange (0x0B80 ,0x0BFF ,"Tamil"), - new UnicodeRange (0x0C00 ,0x0C7F ,"Telugu"), - new UnicodeRange (0x0C80 ,0x0CFF ,"Kannada"), - new UnicodeRange (0x0D00 ,0x0D7F ,"Malayalam"), - new UnicodeRange (0x0D80 ,0x0DFF ,"Sinhala"), - new UnicodeRange (0x0E00 ,0x0E7F ,"Thai"), - new UnicodeRange (0x0E80 ,0x0EFF ,"Lao"), - new UnicodeRange (0x0F00 ,0x0FFF ,"Tibetan"), - new UnicodeRange (0x1000 ,0x109F ,"Myanmar"), - new UnicodeRange (0x10A0 ,0x10FF ,"Georgian"), - new UnicodeRange (0x1100 ,0x11FF ,"Hangul Jamo"), - new UnicodeRange (0x1200 ,0x137F ,"Ethiopic"), - new UnicodeRange (0x13A0 ,0x13FF ,"Cherokee"), - new UnicodeRange (0x1400 ,0x167F ,"Unified Canadian Aboriginal Syllabics"), - new UnicodeRange (0x1680 ,0x169F ,"Ogham"), - new UnicodeRange (0x16A0 ,0x16FF ,"Runic"), - new UnicodeRange (0x1700 ,0x171F ,"Tagalog"), - new UnicodeRange (0x1720 ,0x173F ,"Hanunoo"), - new UnicodeRange (0x1740 ,0x175F ,"Buhid"), - new UnicodeRange (0x1760 ,0x177F ,"Tagbanwa"), - new UnicodeRange (0x1780 ,0x17FF ,"Khmer"), - new UnicodeRange (0x1800 ,0x18AF ,"Mongolian"), - new UnicodeRange (0x1900 ,0x194F ,"Limbu"), - new UnicodeRange (0x1950 ,0x197F ,"Tai Le"), - new UnicodeRange (0x19E0 ,0x19FF ,"Khmer Symbols"), - new UnicodeRange (0x1D00 ,0x1D7F ,"Phonetic Extensions"), - new UnicodeRange (0x1E00 ,0x1EFF ,"Latin Extended Additional"), - new UnicodeRange (0x1F00 ,0x1FFF ,"Greek Extended"), - new UnicodeRange (0x2000 ,0x206F ,"General Punctuation"), - new UnicodeRange (0x2070 ,0x209F ,"Superscripts and Subscripts"), - new UnicodeRange (0x20A0 ,0x20CF ,"Currency Symbols"), - new UnicodeRange (0x20D0 ,0x20FF ,"Combining Diacritical Marks for Symbols"), - new UnicodeRange (0x2100 ,0x214F ,"Letterlike Symbols"), - new UnicodeRange (0x2150 ,0x218F ,"Number Forms"), - new UnicodeRange (0x2190 ,0x21FF ,"Arrows"), - new UnicodeRange (0x2200 ,0x22FF ,"Mathematical Operators"), - new UnicodeRange (0x2300 ,0x23FF ,"Miscellaneous Technical"), - new UnicodeRange (0x2400 ,0x243F ,"Control Pictures"), - new UnicodeRange (0x2440 ,0x245F ,"Optical Character Recognition"), - new UnicodeRange (0x2460 ,0x24FF ,"Enclosed Alphanumerics"), - new UnicodeRange (0x2500 ,0x257F ,"Box Drawing"), - new UnicodeRange (0x2580 ,0x259F ,"Block Elements"), - new UnicodeRange (0x25A0 ,0x25FF ,"Geometric Shapes"), - new UnicodeRange (0x2600 ,0x26FF ,"Miscellaneous Symbols"), - new UnicodeRange (0x2700 ,0x27BF ,"Dingbats"), - new UnicodeRange (0x27C0 ,0x27EF ,"Miscellaneous Mathematical Symbols-A"), - new UnicodeRange (0x27F0 ,0x27FF ,"Supplemental Arrows-A"), - new UnicodeRange (0x2800 ,0x28FF ,"Braille Patterns"), - new UnicodeRange (0x2900 ,0x297F ,"Supplemental Arrows-B"), - new UnicodeRange (0x2980 ,0x29FF ,"Miscellaneous Mathematical Symbols-B"), - new UnicodeRange (0x2A00 ,0x2AFF ,"Supplemental Mathematical Operators"), - new UnicodeRange (0x2B00 ,0x2BFF ,"Miscellaneous Symbols and Arrows"), - new UnicodeRange (0x2E80 ,0x2EFF ,"CJK Radicals Supplement"), - new UnicodeRange (0x2F00 ,0x2FDF ,"Kangxi Radicals"), - new UnicodeRange (0x2FF0 ,0x2FFF ,"Ideographic Description Characters"), - new UnicodeRange (0x3000 ,0x303F ,"CJK Symbols and Punctuation"), - new UnicodeRange (0x3040 ,0x309F ,"Hiragana"), - new UnicodeRange (0x30A0 ,0x30FF ,"Katakana"), - new UnicodeRange (0x3100 ,0x312F ,"Bopomofo"), - new UnicodeRange (0x3130 ,0x318F ,"Hangul Compatibility Jamo"), - new UnicodeRange (0x3190 ,0x319F ,"Kanbun"), - new UnicodeRange (0x31A0 ,0x31BF ,"Bopomofo Extended"), - new UnicodeRange (0x31F0 ,0x31FF ,"Katakana Phonetic Extensions"), - new UnicodeRange (0x3200 ,0x32FF ,"Enclosed CJK Letters and Months"), - new UnicodeRange (0x3300 ,0x33FF ,"CJK Compatibility"), - new UnicodeRange (0x3400 ,0x4DBF ,"CJK Unified Ideographs Extension A"), - new UnicodeRange (0x4DC0 ,0x4DFF ,"Yijing Hexagram Symbols"), - new UnicodeRange (0x4E00 ,0x9FFF ,"CJK Unified Ideographs"), - new UnicodeRange (0xA000 ,0xA48F ,"Yi Syllables"), - new UnicodeRange (0xA490 ,0xA4CF ,"Yi Radicals"), - new UnicodeRange (0xAC00 ,0xD7AF ,"Hangul Syllables"), - new UnicodeRange (0xD800 ,0xDB7F ,"High Surrogates"), - new UnicodeRange (0xDB80 ,0xDBFF ,"High Private Use Surrogates"), - new UnicodeRange (0xDC00 ,0xDFFF ,"Low Surrogates"), - new UnicodeRange (0xE000 ,0xF8FF ,"Private Use Area"), - new UnicodeRange (0xF900 ,0xFAFF ,"CJK Compatibility Ideographs"), - new UnicodeRange (0xFB00 ,0xFB4F ,"Alphabetic Presentation Forms"), - new UnicodeRange (0xFB50 ,0xFDFF ,"Arabic Presentation Forms-A"), - new UnicodeRange (0xFE00 ,0xFE0F ,"Variation Selectors"), - new UnicodeRange (0xFE20 ,0xFE2F ,"Combining Half Marks"), - new UnicodeRange (0xFE30 ,0xFE4F ,"CJK Compatibility Forms"), - new UnicodeRange (0xFE50 ,0xFE6F ,"Small Form Variants"), - new UnicodeRange (0xFE70 ,0xFEFF ,"Arabic Presentation Forms-B"), - new UnicodeRange (0xFF00 ,0xFFEF ,"Halfwidth and Fullwidth Forms"), - new UnicodeRange (0xFFF0 ,0xFFFF ,"Specials"), + public static List GetRanges () + { + var ranges = (from r in typeof (UnicodeRanges).GetProperties (System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public) + let urange = r.GetValue (null) as System.Text.Unicode.UnicodeRange + let name = string.IsNullOrEmpty (r.Name) ? $"U+{urange.FirstCodePoint:x5}-U+{urange.FirstCodePoint + urange.Length:x5}" : r.Name + where name != "None" && name != "All" + select new UnicodeRange (urange.FirstCodePoint, urange.FirstCodePoint + urange.Length, name)); + + // .NET 8.0 only supports BMP in UnicodeRanges: https://learn.microsoft.com/en-us/dotnet/api/system.text.unicode.unicoderanges?view=net-8.0 + var nonBmpRanges = new List { + + new UnicodeRange (0x1F130, 0x1F149 ,"Squared Latin Capital Letters"), + new UnicodeRange (0x12400, 0x1240f ,"Cuneiform Numbers and Punctuation"), + new UnicodeRange (0x1FA00, 0x1FA0f ,"Chess Symbols"), new UnicodeRange (0x10000, 0x1007F ,"Linear B Syllabary"), new UnicodeRange (0x10080, 0x100FF ,"Linear B Ideograms"), new UnicodeRange (0x10100, 0x1013F ,"Aegean Numbers"), @@ -532,6 +566,10 @@ public UnicodeRange (uint start, uint end, string category) new UnicodeRange (0x20000, 0x2A6DF ,"CJK Unified Ideographs Extension B"), new UnicodeRange (0x2F800, 0x2FA1F ,"CJK Compatibility Ideographs Supplement"), new UnicodeRange (0xE0000, 0xE007F ,"Tags"), - new UnicodeRange((uint)(CharMap.MaxCodePoint - 16), (uint)CharMap.MaxCodePoint,"End"), }; + + return ranges.Concat (nonBmpRanges).ToList (); + } + + public static List Ranges = GetRanges (); } \ No newline at end of file diff --git a/UnitTests/View/Layout/LayoutTests.cs b/UnitTests/View/Layout/LayoutTests.cs index 410f579f81..829aadae45 100644 --- a/UnitTests/View/Layout/LayoutTests.cs +++ b/UnitTests/View/Layout/LayoutTests.cs @@ -539,8 +539,8 @@ public void Excess_Text_Is_Erased_When_The_Width_Is_Reduced () // that was set on the OnAdded method with the text length of 3 // and height 1 because wasn't set and the text has 1 line Assert.Equal (new Rect (0, 0, 3, 1), lbl.Frame); - Assert.Equal (new Rect (0, 0, 3, 1), lbl._needsDisplay); - Assert.Equal (new Rect (0, 0, 0, 0), lbl.SuperView._needsDisplay); + Assert.Equal (new Rect (0, 0, 3, 1), lbl._needsDisplayRect); + Assert.Equal (new Rect (0, 0, 0, 0), lbl.SuperView._needsDisplayRect); Assert.True (lbl.SuperView.LayoutNeeded); lbl.SuperView.Draw (); Assert.Equal ("12 ", GetContents ()); diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index 39301c54e2..a1690e9b1c 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -563,7 +563,7 @@ public void Internal_Tests () // BUGBUG: v2 - _needsDisplay needs debugging - test disabled for now. //Assert.Equal (new Rect (new Point (0, 0), rect.Size), view._needsDisplay); Assert.True (view.LayoutNeeded); - Assert.False (view._subViewNeedsDisplay); + Assert.False (view.SubViewNeedsDisplay); Assert.False (view._addingView); view._addingView = true; Assert.True (view._addingView); @@ -1092,7 +1092,7 @@ A text with some long width Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); view.LayoutStyle = LayoutStyle.Absolute; Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplayRect); top.Draw (); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -1127,7 +1127,7 @@ A text with some long width view.Height = 1; Assert.Equal (new Rect (1, 1, 10, 1), view.Frame); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplayRect); top.Draw (); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -1161,7 +1161,7 @@ A text with some long width Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); view.LayoutStyle = LayoutStyle.Absolute; Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplayRect); view.Draw (); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -1198,7 +1198,7 @@ A text with some long width view.Height = 1; Assert.Equal (new Rect (1, 1, 10, 1), view.Frame); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplayRect); view.Draw (); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -1233,7 +1233,7 @@ A text with some long width Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); view.LayoutStyle = LayoutStyle.Absolute; Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplayRect); top.Draw (); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -1270,7 +1270,7 @@ A text with some long width view.Height = 1; Assert.Equal (new Rect (3, 3, 10, 1), view.Frame); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplayRect); top.Draw (); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -1303,7 +1303,7 @@ A text with some long width view.Frame = new Rect (3, 3, 10, 1); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 10, 1), view._needsDisplayRect); view.Draw (); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -1340,7 +1340,7 @@ A text with some long width view.Height = 1; Assert.Equal (new Rect (3, 3, 10, 1), view.Frame); Assert.Equal (new Rect (0, 0, 10, 1), view.Bounds); - Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 30, 2), view._needsDisplayRect); view.Draw (); TestHelpers.AssertDriverContentsWithFrameAre (@" At 0,0 @@ -1407,19 +1407,19 @@ public void Frame_Set_After_Initialize_Update_NeededDisplay () Application.Begin (top); top.LayoutComplete += (s, e) => { - Assert.Equal (new Rect (0, 0, 80, 25), top._needsDisplay); + Assert.Equal (new Rect (0, 0, 80, 25), top._needsDisplayRect); }; frame.LayoutComplete += (s, e) => { - Assert.Equal (new Rect (0, 0, 40, 8), frame._needsDisplay); + Assert.Equal (new Rect (0, 0, 40, 8), frame._needsDisplayRect); }; label.LayoutComplete += (s, e) => { - Assert.Equal (new Rect (0, 0, 38, 1), label._needsDisplay); + Assert.Equal (new Rect (0, 0, 38, 1), label._needsDisplayRect); }; button.LayoutComplete += (s, e) => { - Assert.Equal (new Rect (0, 0, 13, 1), button._needsDisplay); + Assert.Equal (new Rect (0, 0, 13, 1), button._needsDisplayRect); }; Assert.True (label.AutoSize); diff --git a/UnitTests/Views/ToplevelTests.cs b/UnitTests/Views/ToplevelTests.cs index 689fd122ae..7ea1bb8aea 100644 --- a/UnitTests/Views/ToplevelTests.cs +++ b/UnitTests/Views/ToplevelTests.cs @@ -1012,7 +1012,7 @@ public void IsLoaded_With_Sub_Toplevel_Application_Begin_NeedDisplay () void view_LayoutStarted (object sender, LayoutEventArgs e) { - Assert.Equal (new Rect (0, 0, 20, 10), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 20, 10), view._needsDisplayRect); view.LayoutStarted -= view_LayoutStarted; } @@ -1024,12 +1024,12 @@ void view_LayoutStarted (object sender, LayoutEventArgs e) view.Frame = new Rect (1, 3, 10, 5); Assert.Equal (new Rect (1, 3, 10, 5), view.Frame); - Assert.Equal (new Rect (0, 0, 10, 5), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 10, 5), view._needsDisplayRect); view.OnDrawContent (view.Bounds); view.Frame = new Rect (1, 3, 10, 5); Assert.Equal (new Rect (1, 3, 10, 5), view.Frame); - Assert.Equal (new Rect (0, 0, 10, 5), view._needsDisplay); + Assert.Equal (new Rect (0, 0, 10, 5), view._needsDisplayRect); } // BUGBUG: Broke this test with #2483 - @bdisp I need your help figuring out why From a7b7e3557d860ccb04a642d6ffde16c2a84ac667 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sat, 13 May 2023 12:26:06 +0200 Subject: [PATCH 29/99] Fixing bugs --- Terminal.Gui/Application.cs | 5 ++- Terminal.Gui/View/ViewDrawing.cs | 6 +-- Terminal.Gui/Views/ScrollView.cs | 2 +- Terminal.Gui/Views/TextField.cs | 6 ++- Terminal.Gui/Views/TextView.cs | 6 --- UICatalog/Scenarios/Generic.cs | 40 ++++++++++++++---- UnitTests/TestHelpers.cs | 11 ++--- UnitTests/UnitTests - Backup.csproj | 63 +++++++++++++++++++++++++++++ UnitTests/UnitTests.csproj | 1 + UnitTests/Views/ToplevelTests.cs | 2 +- UnitTests/xunit.runner.json | 3 +- 11 files changed, 114 insertions(+), 31 deletions(-) create mode 100644 UnitTests/UnitTests - Backup.csproj diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index 9a73cd5364..05776e2ed9 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -749,13 +749,14 @@ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool Iteration?.Invoke (); EnsureModalOrVisibleAlwaysOnTop (state.Toplevel); - if ((state.Toplevel != Current && Current?.Modal == true) - || (state.Toplevel != Current && Current?.Modal == false)) { + if (state.Toplevel != Current) { OverlappedTop?.OnDeactivate (state.Toplevel); state.Toplevel = Current; OverlappedTop?.OnActivate (state.Toplevel); Top.SetSubViewNeedsDisplay (); Refresh (); + } else if (Current.SuperView == null && Current?.Modal == true) { + Refresh (); } if (Driver.EnsureCursorVisibility ()) { state.Toplevel.SetNeedsDisplay (); diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs index 013a61c391..fa3c9ad76d 100644 --- a/Terminal.Gui/View/ViewDrawing.cs +++ b/Terminal.Gui/View/ViewDrawing.cs @@ -34,11 +34,11 @@ public virtual ColorScheme ColorScheme { /// If it's overridden can return other values. public virtual Attribute GetNormalColor () { + ColorScheme cs = ColorScheme; if (ColorScheme == null) { - var cs = new ColorScheme (); - return Enabled ? cs.Normal : cs.Disabled; + cs = new ColorScheme (); } - return Enabled ? ColorScheme.Normal : ColorScheme.Disabled; + return Enabled ? cs.Normal : cs.Disabled; } /// diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index c70db78512..10b99c7555 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -368,7 +368,7 @@ public override void OnDrawContent (Rect contentArea) Clear (); if (!ustring.IsNullOrEmpty (Text) || Subviews.Count > 0) { - contentView.Draw (); + _contentView.Draw (); } DrawScrollBars (); diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 875f33872e..fc4c3b2268 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -534,7 +534,11 @@ private void GenerateSuggestions () /// public override Attribute GetNormalColor () { - return Enabled ? ColorScheme.Focus : ColorScheme.Disabled; + ColorScheme cs = ColorScheme; + if (ColorScheme == null) { + cs = new ColorScheme (); + } + return Enabled ? cs.Focus : cs.Disabled; } Attribute GetReadOnlyColor () diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index a9c3bdcdda..ec56ddb5f9 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2699,12 +2699,6 @@ private void GenerateSuggestions () ); } - /// - public override Attribute GetNormalColor () - { - return Enabled ? ColorScheme.Focus : ColorScheme.Disabled; - } - /// public override bool CanFocus { get => base.CanFocus; diff --git a/UICatalog/Scenarios/Generic.cs b/UICatalog/Scenarios/Generic.cs index f2719bccb5..b20a10a6c4 100644 --- a/UICatalog/Scenarios/Generic.cs +++ b/UICatalog/Scenarios/Generic.cs @@ -18,10 +18,10 @@ public override void Init () // A common, alternate, implementation where `this.Win` is not used is below. This code // leverages ConfigurationManager to borrow the color scheme settings from UICatalog: - Application.Init (); - ConfigurationManager.Themes.Theme = Theme; - ConfigurationManager.Apply (); - Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]; + //Application.Init (); + //ConfigurationManager.Themes.Theme = Theme; + //ConfigurationManager.Apply (); + //Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]; } public override void Setup () @@ -31,13 +31,37 @@ public override void Setup () // With a Scenario, after UI Catalog calls `Scenario.Setup` it calls // `Scenario.Run` which calls `Application.Run`. Example: - var button = new Button ("Press me!") { - AutoSize = false, + //var button = new Button ("Press me!") { + // AutoSize = false, + // X = Pos.Center (), + // Y = Pos.Center (), + //}; + //Application.Top.Add (button); + + + var dialog = new Dialog () { Width = 30, Height = 10 }; + dialog.Add (new Label ( + "How should I've to react. Cleaning all chunk trails or setting the 'Cols' and 'Rows' to this dialog length?\n" + + "Cleaning is more easy to fix this.") { X = Pos.Center (), Y = Pos.Center (), - }; - Application.Top.Add (button); + Width = Dim.Fill (), + Height = Dim.Fill (), + TextAlignment = TextAlignment.Centered, + VerticalTextAlignment = VerticalTextAlignment.Middle, + AutoSize = false + }); + + Application.Init (); + Application.Run (dialog); + + + } + + public override void Run () + { + //base.Run (); } } } \ No newline at end of file diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index b0424d24c1..35bb813822 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -36,7 +36,7 @@ public class AutoInitShutdownAttribute : Xunit.Sdk.BeforeAfterTestAttribute { /// Only valid if == and is true. /// Determines what config file locations will /// load from. - public AutoInitShutdownAttribute (bool autoInit = true, bool autoShutdown = true, + public AutoInitShutdownAttribute (bool autoInit = true, Type consoleDriverType = null, bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsNotSupportedException = false, @@ -46,7 +46,6 @@ public AutoInitShutdownAttribute (bool autoInit = true, bool autoShutdown = true //Assert.True (autoInit == false && consoleDriverType == null); AutoInit = autoInit; - AutoShutdown = autoShutdown; _driverType = consoleDriverType ?? typeof (FakeDriver); FakeDriver.FakeBehaviors.UseFakeClipboard = useFakeClipboard; FakeDriver.FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; @@ -56,25 +55,21 @@ public AutoInitShutdownAttribute (bool autoInit = true, bool autoShutdown = true static bool _init = false; bool AutoInit { get; } - bool AutoShutdown { get; } Type _driverType; public override void Before (MethodInfo methodUnderTest) { Debug.WriteLine ($"Before: {methodUnderTest.Name}"); - if (AutoShutdown && _init) { - throw new InvalidOperationException ("After did not run when AutoShutdown was specified."); - } if (AutoInit) { Application.Init ((ConsoleDriver)Activator.CreateInstance (_driverType)); _init = true; } } - + public override void After (MethodInfo methodUnderTest) { Debug.WriteLine ($"After: {methodUnderTest.Name}"); - if (AutoShutdown) { + if (AutoInit) { Application.Shutdown (); _init = false; } diff --git a/UnitTests/UnitTests - Backup.csproj b/UnitTests/UnitTests - Backup.csproj new file mode 100644 index 0000000000..bac4003278 --- /dev/null +++ b/UnitTests/UnitTests - Backup.csproj @@ -0,0 +1,63 @@ + + + net7.0 + + + Preview + false + + + + + 2.0 + 2.0 + 2.0 + 2.0 + + + TRACE + + + TRACE;DEBUG_IDISPOSABLE + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + False + + + [UICatalog]* + + + + + + + + + + False + + C:\Users\charlie\s\gui-cs\Terminal.Gui\UnitTests\bin\Debug\net7.0\fine-code-coverage\coverage-tool-output\UnitTests-fcc-mscodecoverage-generated.runsettings + + \ No newline at end of file diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index b2907d0916..ef120b1c60 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -21,6 +21,7 @@ TRACE;DEBUG_IDISPOSABLE + diff --git a/UnitTests/Views/ToplevelTests.cs b/UnitTests/Views/ToplevelTests.cs index 7ea1bb8aea..b33de39750 100644 --- a/UnitTests/Views/ToplevelTests.cs +++ b/UnitTests/Views/ToplevelTests.cs @@ -1284,7 +1284,7 @@ public void Dialog_Bounds_Bigger_Than_Driver_Cols_And_Rows_Allow_Drag_Beyond_Lef } [Fact, AutoInitShutdown] - public void Single_Smaller_Top_Will_Have_Cleaning_Trails_Chunk_On_Move () + public void Modal_As_Top_Will_Drag_Cleanly () { var dialog = new Dialog () { Width = 30, Height = 10 }; dialog.Add (new Label ( diff --git a/UnitTests/xunit.runner.json b/UnitTests/xunit.runner.json index e810a97254..c108763855 100644 --- a/UnitTests/xunit.runner.json +++ b/UnitTests/xunit.runner.json @@ -1,5 +1,6 @@ { "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", "parallelizeTestCollections": false, - "parallelizeAssembly": false + "parallelizeAssembly": false, + "stopOnFail": true } From c9780ebc5ddae3030874871d4923dcb5f4b1862c Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sat, 13 May 2023 12:55:52 +0200 Subject: [PATCH 30/99] Fixed TabView tests --- Terminal.Gui/Views/TabView.cs | 2 +- UICatalog/Scenarios/Generic.cs | 47 ++++++++------------------------- UnitTests/Views/TabViewTests.cs | 5 +++- 3 files changed, 16 insertions(+), 38 deletions(-) diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs index 7bb2bf3354..f74e3ff028 100644 --- a/Terminal.Gui/Views/TabView.cs +++ b/Terminal.Gui/Views/TabView.cs @@ -171,7 +171,7 @@ public void ApplyStyleChanges () // Should be able to just use 0 but switching between top/bottom tabs repeatedly breaks in ValidatePosDim if just using the absolute value 0 tabsBar.Y = Pos.Percent (0); } - + LayoutSubviews (); SetNeedsDisplay (); } diff --git a/UICatalog/Scenarios/Generic.cs b/UICatalog/Scenarios/Generic.cs index b20a10a6c4..9d4ce4e32b 100644 --- a/UICatalog/Scenarios/Generic.cs +++ b/UICatalog/Scenarios/Generic.cs @@ -12,16 +12,16 @@ public override void Init () // that reads "Press to Quit". Access this Window with `this.Win`. // - Sets the Theme & the ColorScheme property of `this.Win` to `colorScheme`. // To override this, implement an override of `Init`. - + //base.Init (); - + // A common, alternate, implementation where `this.Win` is not used is below. This code // leverages ConfigurationManager to borrow the color scheme settings from UICatalog: - - //Application.Init (); - //ConfigurationManager.Themes.Theme = Theme; - //ConfigurationManager.Apply (); - //Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]; + + Application.Init (); + ConfigurationManager.Themes.Theme = Theme; + ConfigurationManager.Apply (); + Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]; } public override void Setup () @@ -31,37 +31,12 @@ public override void Setup () // With a Scenario, after UI Catalog calls `Scenario.Setup` it calls // `Scenario.Run` which calls `Application.Run`. Example: - //var button = new Button ("Press me!") { - // AutoSize = false, - // X = Pos.Center (), - // Y = Pos.Center (), - //}; - //Application.Top.Add (button); - - - var dialog = new Dialog () { Width = 30, Height = 10 }; - dialog.Add (new Label ( - "How should I've to react. Cleaning all chunk trails or setting the 'Cols' and 'Rows' to this dialog length?\n" + - "Cleaning is more easy to fix this.") { + var button = new Button ("Press me!") { + AutoSize = false, X = Pos.Center (), Y = Pos.Center (), - Width = Dim.Fill (), - Height = Dim.Fill (), - TextAlignment = TextAlignment.Centered, - VerticalTextAlignment = VerticalTextAlignment.Middle, - AutoSize = false - }); - - Application.Init (); - - Application.Run (dialog); - - - } - - public override void Run () - { - //base.Run (); + }; + Application.Top.Add (button); } } } \ No newline at end of file diff --git a/UnitTests/Views/TabViewTests.cs b/UnitTests/Views/TabViewTests.cs index 339f18a59b..d147b254b6 100644 --- a/UnitTests/Views/TabViewTests.cs +++ b/UnitTests/Views/TabViewTests.cs @@ -25,13 +25,16 @@ private TabView GetTabView () private TabView GetTabView (out TabView.Tab tab1, out TabView.Tab tab2, bool initFakeDriver = true) { - if (initFakeDriver) + if (initFakeDriver) { InitFakeDriver (); + } var tv = new TabView (); tv.ColorScheme = new ColorScheme (); tv.AddTab (tab1 = new TabView.Tab ("Tab1", new TextField ("hi")), false); tv.AddTab (tab2 = new TabView.Tab ("Tab2", new Label ("hi2")), false); + tv.BeginInit (); + tv.EndInit (); return tv; } From 9f7b5ac3e9f3a657fc8f52dae173ac5b77ad9cd5 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sat, 13 May 2023 13:15:11 +0200 Subject: [PATCH 31/99] Fixed View.Visible test that was not really working --- UnitTests/View/ViewTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index a1690e9b1c..59e5035348 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -888,7 +888,7 @@ public void Visible_Clear_The_View_Output () win.Add (label); var top = Application.Top; top.Add (win); - Application.Begin (top); + var rs = Application.Begin (top); Assert.True (label.Visible); ((FakeDriver)Application.Driver).SetBufferSize (30, 5); @@ -901,6 +901,9 @@ public void Visible_Clear_The_View_Output () ", output); label.Visible = false; + + bool firstIteration = false; + Application.RunMainLoopIteration (ref rs, true, ref firstIteration); TestHelpers.AssertDriverContentsWithFrameAre (@" ┌────────────────────────────┐ │ │ @@ -1363,6 +1366,8 @@ public void Test_Nested_Views_With_Height_Equal_To_One () bottom.Add (new Label ("222")); v.Add (bottom); + v.BeginInit (); + v.EndInit (); v.LayoutSubviews (); v.Draw (); From 8f39eff1d815686de675dc0fc8c19f0c007b6d44 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sat, 13 May 2023 13:48:36 +0200 Subject: [PATCH 32/99] Fixed unit tests --- Terminal.Gui/Views/TextView.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index ec56ddb5f9..9d5ba23067 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2151,6 +2151,16 @@ void ClearRegion (int left, int top, int right, int bottom) } } + /// + public override Attribute GetNormalColor () + { + ColorScheme cs = ColorScheme; + if (ColorScheme == null) { + cs = new ColorScheme (); + } + return Enabled ? cs.Focus : cs.Disabled; + } + /// /// Sets the driver to the default color for the control where no text is being rendered. Defaults to . /// From 4717a59a075d305815aba022c8fb071cb08f4549 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sun, 21 May 2023 15:02:39 +0200 Subject: [PATCH 33/99] Cleaned up clipboard APIs in attempt to track down unit test failure --- Terminal.Gui/Clipboard/Clipboard.cs | 26 ++++++++--------- Terminal.Gui/Clipboard/ClipboardBase.cs | 16 +++++++---- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 4 +-- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 9 ++++-- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 9 ++---- UnitTests/Clipboard/ClipboardTests.cs | 16 +++++------ UnitTests/Views/TextViewTests.cs | 28 +++++++++---------- 7 files changed, 56 insertions(+), 52 deletions(-) diff --git a/Terminal.Gui/Clipboard/Clipboard.cs b/Terminal.Gui/Clipboard/Clipboard.cs index aa4a8bb5f5..a4810764b2 100644 --- a/Terminal.Gui/Clipboard/Clipboard.cs +++ b/Terminal.Gui/Clipboard/Clipboard.cs @@ -28,21 +28,21 @@ namespace Terminal.Gui; /// /// public static class Clipboard { - static string contents; + static string _contents = string.Empty; /// - /// Gets (copies from) or sets (pastes to) the contents of the OS clipboard. + /// Gets (copies from) or sets (pastes to) the _contents of the OS clipboard. /// public static string Contents { get { try { if (IsSupported) { - return contents = Application.Driver.Clipboard.GetClipboardData (); + return _contents = Application.Driver.Clipboard.GetClipboardData (); } else { - return contents; + return _contents; } } catch (Exception) { - return contents; + return _contents; } } set { @@ -51,13 +51,13 @@ public static string Contents { if (value == null) { value = string.Empty; } - Application.Driver.Clipboard.SetClipboardData (value.ToString ()); + Application.Driver.Clipboard.SetClipboardData (value); } - contents = value; + _contents = value; } catch (NotSupportedException e) { throw e; } catch (Exception) { - contents = value; + _contents = value; } } } @@ -70,15 +70,15 @@ public static string Contents { public static bool IsSupported { get => Application.Driver.Clipboard.IsSupported; } /// - /// Copies the contents of the OS clipboard to if possible. + /// Copies the _contents of the OS clipboard to if possible. /// - /// The contents of the OS clipboard if successful, if not. + /// The _contents of the OS clipboard if successful, if not. /// the OS clipboard was retrieved, otherwise. public static bool TryGetClipboardData (out string result) { if (IsSupported && Application.Driver.Clipboard.TryGetClipboardData (out result)) { - if (contents != result) { - contents = result; + if (_contents != result) { + _contents = result; } return true; } @@ -94,7 +94,7 @@ public static bool TryGetClipboardData (out string result) public static bool TrySetClipboardData (string text) { if (IsSupported && Application.Driver.Clipboard.TrySetClipboardData (text)) { - contents = text; + _contents = text; return true; } return false; diff --git a/Terminal.Gui/Clipboard/ClipboardBase.cs b/Terminal.Gui/Clipboard/ClipboardBase.cs index ee2e437d41..043aad10bd 100644 --- a/Terminal.Gui/Clipboard/ClipboardBase.cs +++ b/Terminal.Gui/Clipboard/ClipboardBase.cs @@ -22,6 +22,10 @@ public abstract class ClipboardBase : IClipboard { public string GetClipboardData () { try { + var result = GetClipboardDataImpl (); + if (result == null) { + return string.Empty; + } return GetClipboardDataImpl (); } catch (NotSupportedException ex) { throw new NotSupportedException ("Failed to copy from the OS clipboard.", ex); @@ -42,6 +46,10 @@ public string GetClipboardData () /// Thrown if it was not possible to paste to the OS clipboard. public void SetClipboardData (string text) { + if (text == null) { + throw new ArgumentNullException (nameof (text)); + } + try { SetClipboardDataImpl (text); } catch (NotSupportedException ex) { @@ -59,25 +67,21 @@ public void SetClipboardData (string text) /// /// Copies the contents of the OS clipboard to if possible. /// - /// The contents of the OS clipboard if successful, if not. + /// The contents of the OS clipboard if successful. /// the OS clipboard was retrieved, otherwise. public bool TryGetClipboardData (out string result) { + result = string.Empty; // Don't even try to read because environment is not set up. if (!IsSupported) { - result = null; return false; } try { result = GetClipboardDataImpl (); - while (result == null) { - result = GetClipboardDataImpl (); - } return true; } catch (NotSupportedException ex) { System.Diagnostics.Debug.WriteLine ($"TryGetClipboardData: {ex.Message}"); - result = null; return false; } } diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index ed2518eff8..b51a98e766 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -179,8 +179,8 @@ public virtual void AddRune (Rune rune) /// String. public void AddStr (string str) { - foreach (var c in str) { - AddRune (new Rune (c)); + foreach (var rune in str.EnumerateRunes()) { + AddRune (rune); } } diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 5b22cde715..8cf8e5055d 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -602,7 +602,7 @@ public override void Suspend () public class FakeClipboard : ClipboardBase { public Exception FakeException = null; - string Contents = string.Empty; + string _contents = string.Empty; bool _isSupportedAlwaysFalse = false; @@ -621,15 +621,18 @@ protected override string GetClipboardDataImpl () if (FakeException != null) { throw FakeException; } - return Contents; + return _contents; } protected override void SetClipboardDataImpl (string text) { + if (text == null) { + throw new ArgumentNullException (nameof (text)); + } if (FakeException != null) { throw FakeException; } - Contents = text; + _contents = text; } } diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 66db66df97..d7c8574989 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1935,17 +1935,14 @@ public WindowsClipboard () protected override string GetClipboardDataImpl () { - //if (!IsClipboardFormatAvailable (cfUnicodeText)) - // return null; - try { if (!OpenClipboard (IntPtr.Zero)) { - return null; + return string.Empty; } IntPtr handle = GetClipboardData (_cfUnicodeText); if (handle == IntPtr.Zero) { - return null; + return string.Empty; } IntPtr pointer = IntPtr.Zero; @@ -1953,7 +1950,7 @@ protected override string GetClipboardDataImpl () try { pointer = GlobalLock (handle); if (pointer == IntPtr.Zero) { - return null; + return string.Empty; } int size = GlobalSize (handle); diff --git a/UnitTests/Clipboard/ClipboardTests.cs b/UnitTests/Clipboard/ClipboardTests.cs index ecfbd41f91..7ea0543452 100644 --- a/UnitTests/Clipboard/ClipboardTests.cs +++ b/UnitTests/Clipboard/ClipboardTests.cs @@ -39,7 +39,7 @@ public void Contents_Fake_Gets_Sets () Application.Iteration += () => Application.RequestStop (); Application.Run (); - Assert.Equal (clipText, Clipboard.Contents.ToString ()); + Assert.Equal (clipText, Clipboard.Contents); } [Fact, AutoInitShutdown (useFakeClipboard: false)] @@ -56,7 +56,7 @@ public void Contents_Gets_Sets () Application.Iteration += () => Application.RequestStop (); Application.Run (); - Assert.Equal (clipText, Clipboard.Contents.ToString ()); + Assert.Equal (clipText, Clipboard.Contents); } [Fact, AutoInitShutdown (useFakeClipboard: false)] @@ -74,7 +74,7 @@ public void Contents_Gets_Sets_When_IsSupportedFalse () Application.Iteration += () => Application.RequestStop (); Application.Run (); - Assert.Equal (clipText, Clipboard.Contents.ToString ()); + Assert.Equal (clipText, Clipboard.Contents); } [Fact, AutoInitShutdown (useFakeClipboard: true)] @@ -92,7 +92,7 @@ public void Contents_Fake_Gets_Sets_When_IsSupportedFalse () Application.Iteration += () => Application.RequestStop (); Application.Run (); - Assert.Equal (clipText, Clipboard.Contents.ToString ()); + Assert.Equal (clipText, Clipboard.Contents); } [Fact, AutoInitShutdown (useFakeClipboard: false)] @@ -158,12 +158,12 @@ public void Contents_Copies_From_OS_Clipboard () if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { (exitCode, result) = ClipboardProcessRunner.Process ("powershell.exe", $"-command \"Set-Clipboard -Value \\\"{clipText}\\\"\""); output.WriteLine ($" Windows: powershell.exe Set-Clipboard: exitCode = {exitCode}, result = {result}"); - getClipText = Clipboard.Contents.ToString (); + getClipText = Clipboard.Contents; } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { (exitCode, result) = ClipboardProcessRunner.Process ("pbcopy", string.Empty, clipText); output.WriteLine ($" OSX: pbcopy: exitCode = {exitCode}, result = {result}"); - getClipText = Clipboard.Contents.ToString (); + getClipText = Clipboard.Contents; } else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) { if (Is_WSL_Platform ()) { @@ -177,7 +177,7 @@ public void Contents_Copies_From_OS_Clipboard () if (!failed) { // If we set the OS clipboard via Powershell, then getting Contents should return the same text. - getClipText = Clipboard.Contents.ToString (); + getClipText = Clipboard.Contents; output.WriteLine ($" WSL: Clipboard.Contents: {getClipText}"); } Application.RequestStop (); @@ -196,7 +196,7 @@ public void Contents_Copies_From_OS_Clipboard () output.WriteLine ($" Linux: bash xclip -sel clip -i: exitCode = {exitCode}, result = {result}"); if (!failed) { - getClipText = Clipboard.Contents.ToString (); + getClipText = Clipboard.Contents; output.WriteLine ($" Linux via xclip: Clipboard.Contents: {getClipText}"); } } diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index 509e75d023..53439c0952 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -971,15 +971,15 @@ public void Kill_To_End_Delete_Forwards_And_Copy_To_The_Clipboard () _textView.ProcessKey (new KeyEvent (Key.K | Key.CtrlMask, new KeyModifiers ())); Assert.Equal (0, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); - Assert.Equal ($"{Environment.NewLine}This is the second line.", _textView.Text.ToString ()); - Assert.Equal ("This is the first line.", Clipboard.Contents.ToString ()); + Assert.Equal ($"{Environment.NewLine}This is the second line.", _textView.Text); + Assert.Equal ("This is the first line.", Clipboard.Contents); break; case 1: _textView.ProcessKey (new KeyEvent (Key.DeleteChar | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ())); Assert.Equal (0, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); - Assert.Equal ("This is the second line.", _textView.Text.ToString ()); - Assert.Equal ($"This is the first line.{Environment.NewLine}", Clipboard.Contents.ToString ()); + Assert.Equal ("This is the second line.", _textView.Text); + Assert.Equal ($"This is the first line.{Environment.NewLine}", Clipboard.Contents); break; case 2: _textView.ProcessKey (new KeyEvent (Key.K | Key.CtrlMask, new KeyModifiers ())); @@ -1015,26 +1015,26 @@ public void Kill_To_Start_Delete_Backwards_And_Copy_To_The_Clipboard () _textView.ProcessKey (new KeyEvent (Key.K | Key.AltMask, new KeyModifiers ())); Assert.Equal (0, _textView.CursorPosition.X); Assert.Equal (1, _textView.CursorPosition.Y); - Assert.Equal ($"This is the first line.{Environment.NewLine}", _textView.Text.ToString ()); - Assert.Equal ($"This is the second line.", Clipboard.Contents.ToString ()); + Assert.Equal ($"This is the first line.{Environment.NewLine}", _textView.Text); + Assert.Equal ($"This is the second line.", Clipboard.Contents); break; case 1: _textView.ProcessKey (new KeyEvent (Key.Backspace | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ())); Assert.Equal (23, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); - Assert.Equal ("This is the first line.", _textView.Text.ToString ()); - Assert.Equal ($"This is the second line.{Environment.NewLine}", Clipboard.Contents.ToString ()); + Assert.Equal ("This is the first line.", _textView.Text); + Assert.Equal ($"This is the second line.{Environment.NewLine}", Clipboard.Contents); break; case 2: _textView.ProcessKey (new KeyEvent (Key.K | Key.AltMask, new KeyModifiers ())); Assert.Equal (0, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); - Assert.Equal ("", _textView.Text.ToString ()); - Assert.Equal ($"This is the second line.{Environment.NewLine}This is the first line.", Clipboard.Contents.ToString ()); + Assert.Equal ("", _textView.Text); + Assert.Equal ($"This is the second line.{Environment.NewLine}This is the first line.", Clipboard.Contents); // Paste inverted _textView.ProcessKey (new KeyEvent (Key.Y | Key.CtrlMask, new KeyModifiers ())); - Assert.Equal ($"This is the second line.{Environment.NewLine}This is the first line.", _textView.Text.ToString ()); + Assert.Equal ($"This is the second line.{Environment.NewLine}This is the first line.", _textView.Text); break; default: iterationsFinished = true; @@ -1450,7 +1450,7 @@ public void TextChanged_Event_NoFires_OnTyping () Assert.Equal (1, eventcount); _textView.ProcessKey (new KeyEvent (Key.Y, new KeyModifiers ())); Assert.Equal (1, eventcount); - Assert.Equal ("Yay", _textView.Text.ToString ()); + Assert.Equal ("Yay", _textView.Text); } [Fact] @@ -2579,7 +2579,7 @@ public void KeyBindings_Command () Assert.True (tv.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ()))); Assert.Equal ($"This is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.", tv.Text); Assert.Equal (new Point (23, 2), tv.CursorPosition); - g.AllSuggestions = Regex.Matches (tv.Text.ToString (), "\\w+") + g.AllSuggestions = Regex.Matches (tv.Text, "\\w+") .Select (s => s.Value) .Distinct ().ToList (); Assert.Equal (7, g.AllSuggestions.Count); @@ -6707,7 +6707,7 @@ public void ContentsChanged_Event_Fires_On_Typing () expectedCol = 1; tv.ProcessKey (new KeyEvent (Key.Y, new KeyModifiers ())); Assert.Equal (3, eventcount); - Assert.Equal ("Yay", tv.Text.ToString ()); + Assert.Equal ("Yay", tv.Text); } [Fact, TextViewTestsAutoInitShutdown] From 6338613e11347b39ab2d18c040c3eb2be0a3b358 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sun, 21 May 2023 15:27:48 +0200 Subject: [PATCH 34/99] Add Cut_Preserves_Selection test --- Terminal.Gui/View/ViewSubViews.cs | 2 +- Terminal.Gui/Views/TextField.cs | 13 ++++++++----- UnitTests/Views/TextFieldTests.cs | 12 ++++++++++++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs index 01226fd809..371119c32d 100644 --- a/Terminal.Gui/View/ViewSubViews.cs +++ b/Terminal.Gui/View/ViewSubViews.cs @@ -128,7 +128,7 @@ public virtual void OnAdded (SuperViewChangedEventArgs e) } /// - /// Gets information if the view was already added to the . + /// Indicates whether the view was added to . /// public bool IsAdded { get; private set; } diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 2faaf3e17e..2a5d98bdba 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -310,8 +310,9 @@ public override Rect Frame { set { var oldText = StringExtensions.ToString (text); - if (oldText == value) + if (oldText == value) { return; + } var newText = OnTextChanging (value.Replace ("\t", "").Split ("\n") [0]); if (newText.Cancel) { @@ -545,9 +546,9 @@ Attribute GetReadOnlyColor () void Adjust () { - if (!IsAdded) + if (!IsAdded) { return; - + } int offB = OffSetBackground (); if (point < first) { first = point; @@ -1152,8 +1153,9 @@ void PrepareSelection (int x, int direction = 0) /// public void ClearAllSelection () { - if (selectedStart == -1 && length == 0 && selectedText == "") + if (selectedStart == -1 && length == 0 && selectedText == "") { return; + } selectedStart = -1; length = 0; @@ -1188,8 +1190,9 @@ public virtual void Copy () /// public virtual void Cut () { - if (ReadOnly || Secret || length == 0) + if (ReadOnly || Secret || length == 0) { return; + } Clipboard.Contents = SelectedText; var newText = DeleteSelectedText (); diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index 7145d10edb..e96585f50c 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -602,6 +602,17 @@ public void Copy_Or_Cut_Not_Null_If_Has_Selection () Assert.Null (_textField.SelectedText); } + [Fact] + [TextFieldTestsAutoInitShutdown] + public void Cut_Preserves_Selection () + { + Assert.Equal ("TAB to jump between text fields.", _textField.Text); + _textField.SelectedStart = 20; + _textField.CursorPosition = 24; + _textField.Cut (); + Assert.Equal (20, _textField.SelectedStart); + } + [Fact] [TextFieldTestsAutoInitShutdown] public void Copy_Or_Cut_And_Paste_With_Selection () @@ -615,6 +626,7 @@ public void Copy_Or_Cut_And_Paste_With_Selection () Assert.Equal ("TAB to jump between text fields.", _textField.Text); _textField.SelectedStart = 20; _textField.Cut (); + Assert.Equal (20, _textField.SelectedStart); _textField.Paste (); Assert.Equal ("TAB to jump between text fields.", _textField.Text); } From be8977ef3e1f0e1530d472c976c0a95fe3d521cf Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 22 May 2023 06:30:54 +0200 Subject: [PATCH 35/99] Removed invalid code --- UnitTests/Views/TextFieldTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index 73b7b2f768..80766a4092 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -25,7 +25,6 @@ public class TextFieldTestsAutoInitShutdown : AutoInitShutdownAttribute { public override void Before (MethodInfo methodUnderTest) { - FakeDriver.FakeBehaviors.UseFakeClipboard = true; base.Before (methodUnderTest); // 1 2 3 From 68d37219226b9b271a362e0268f989834e282e8c Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 22 May 2023 06:46:06 +0200 Subject: [PATCH 36/99] Removed invalid test code; unit tests now pass --- UnitTests/Views/TextFieldTests.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index 80766a4092..2dba686584 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -602,17 +602,6 @@ public void Copy_Or_Cut_Not_Null_If_Has_Selection () Assert.Null (_textField.SelectedText); } - [Fact] - [TextFieldTestsAutoInitShutdown] - public void Cut_Preserves_Selection () - { - Assert.Equal ("TAB to jump between text fields.", _textField.Text); - _textField.SelectedStart = 20; - _textField.CursorPosition = 24; - _textField.Cut (); - Assert.Equal (20, _textField.SelectedStart); - } - [Fact] [TextFieldTestsAutoInitShutdown] public void Copy_Or_Cut_And_Paste_With_Selection () @@ -626,7 +615,6 @@ public void Copy_Or_Cut_And_Paste_With_Selection () Assert.Equal ("TAB to jump between text fields.", _textField.Text); _textField.SelectedStart = 20; _textField.Cut (); - Assert.Equal (20, _textField.SelectedStart); _textField.Paste (); Assert.Equal ("TAB to jump between text fields.", _textField.Text); } From b19735673ea1a68394b7edebc2cd88d48833a19d Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 22 May 2023 22:57:02 +0200 Subject: [PATCH 37/99] EscSeq* - Adjusted naming, added more sequences, made code more consistent, simplified, etc... --- .../CursesDriver/CursesDriver.cs | 12 +- .../ConsoleDrivers/EscSeqUtils/EscSeqReq.cs | 114 ++ .../ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs | 1109 +++++++++++++++++ Terminal.Gui/ConsoleDrivers/NetDriver.cs | 243 ++-- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 23 +- Terminal.Gui/Input/EscSeqUtils/EscSeqReq.cs | 109 -- Terminal.Gui/Input/EscSeqUtils/EscSeqUtils.cs | 907 -------------- UnitTests/Input/EscSeqReqTests.cs | 64 +- UnitTests/Input/EscSeqUtilsTests.cs | 40 +- 9 files changed, 1408 insertions(+), 1213 deletions(-) create mode 100644 Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqReq.cs create mode 100644 Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs delete mode 100644 Terminal.Gui/Input/EscSeqUtils/EscSeqReq.cs delete mode 100644 Terminal.Gui/Input/EscSeqUtils/EscSeqUtils.cs diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 546427c736..27ff5ebd05 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -340,7 +340,7 @@ void ProcessInput () new ConsoleKeyInfo ('<', 0, false, false, false) }; code = 0; - GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); + HandleEscSeqResponse (ref code, ref k, ref wch2, ref key, ref cki); } return; } @@ -399,7 +399,7 @@ void ProcessInput () new ConsoleKeyInfo ((char)Key.Esc, 0, false, false, false), new ConsoleKeyInfo ('[', 0, false, false, false) }; - GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); + HandleEscSeqResponse (ref code, ref k, ref wch2, ref key, ref cki); return; } else { // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa. @@ -455,7 +455,7 @@ void ProcessInput () //} } - void GetEscSeq (ref int code, ref Key k, ref int wch2, ref KeyEvent key, ref ConsoleKeyInfo [] cki) + void HandleEscSeqResponse (ref int code, ref Key k, ref int wch2, ref KeyEvent key, ref ConsoleKeyInfo [] cki) { ConsoleKey ck = 0; ConsoleModifiers mod = 0; @@ -755,12 +755,12 @@ public override void Suspend () public void StartReportingMouseMoves () { - Console.Out.Write (EscSeqUtils.EnableMouseEvents); + Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents); } public void StopReportingMouseMoves () { - Console.Out.Write (EscSeqUtils.DisableMouseEvents); + Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents); } /// @@ -786,7 +786,7 @@ public override bool SetCursorVisibility (CursorVisibility visibility) Curses.curs_set (((int)visibility >> 16) & 0x000000FF); if (visibility != CursorVisibility.Invisible) { - Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF); + Console.Out.Write (EscSeqUtils.CSI_SetCursorStyle ((EscSeqUtils.DECSCUSR_Style)(((int)visibility >> 24) & 0xFF))); } _currentCursorVisibility = visibility; diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqReq.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqReq.cs new file mode 100644 index 0000000000..bce7ba8c8e --- /dev/null +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqReq.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; + +namespace Terminal.Gui { + /// + /// Represents the status of an ANSI escape sequence request made to the terminal using . + /// + /// + /// + public class EscSeqReqStatus { + /// + /// Gets the Escape Sequence Termintor (e.g. ESC[8t ... t is the terminator). + /// + public string Terminator { get; } + /// + /// Gets the number of requests. + /// + public int NumRequests { get; } + /// + /// Gets the number of unfinished requests. + /// + public int NumOutstanding { get; set; } + + /// + /// Creates a new state of escape sequence request. + /// + /// The terminator. + /// The number of requests. + public EscSeqReqStatus (string terminator, int numReq) + { + Terminator = terminator; + NumRequests = NumOutstanding = numReq; + } + } + + // TODO: This class is a singleton. It should use the singleton pattern. + /// + /// Manages ANSI Escape Sequence requests and responses. The list of contains the status of the request. + /// Each request is identified by the terminator (e.g. ESC[8t ... t is the terminator). + /// + public class EscSeqRequests { + /// + /// Gets the list. + /// + public List Statuses { get; } = new List (); + + /// + /// Adds a new request for the ANSI Escape Sequence defined by . Adds a + /// instance to list. + /// + /// The terminator. + /// The number of requests. + public void Add (string terminator, int numReq = 1) + { + lock (Statuses) { + var found = Statuses.Find (x => x.Terminator == terminator); + if (found == null) { + Statuses.Add (new EscSeqReqStatus (terminator, numReq)); + } else if (found != null && found.NumOutstanding < found.NumRequests) { + found.NumOutstanding = Math.Min (found.NumOutstanding + numReq, found.NumRequests); + } + } + } + + /// + /// Removes a request defined by . If a matching is found + /// and the number of outstanding + /// requests is greater than 0, the number of outstanding requests is decremented. If the number of outstanding requests + /// is 0, the is removed from . + /// + /// The terminating string. + public void Remove (string terminator) + { + lock (Statuses) { + var found = Statuses.Find (x => x.Terminator == terminator); + if (found == null) { + return; + } + if (found != null && found.NumOutstanding == 0) { + Statuses.Remove (found); + } else if (found != null && found.NumOutstanding > 0) { + found.NumOutstanding--; + if (found.NumOutstanding == 0) { + Statuses.Remove (found); + } + } + } + } + + /// + /// Indicates if a with the exists + /// in the list. + /// + /// + /// if exist, otherwise. + public bool HasResponse (string terminator) + { + lock (Statuses) { + var found = Statuses.Find (x => x.Terminator == terminator); + if (found == null) { + return false; + } + if (found != null && found.NumOutstanding > 0) { + return true; + } else { + // BUGBUG: Why does an API that returns a bool remove the entry from the list? + // NetDriver and Unit tests never exercise this line of code. Maybe Curses does? + Statuses.Remove (found); + } + return false; + } + } + } +} diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs new file mode 100644 index 0000000000..674412c7b6 --- /dev/null +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs @@ -0,0 +1,1109 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Management; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Terminal.Gui; +/// +/// Provides a platform-independent API for managing ANSI escape sequences. +/// +/// +/// Useful resources: +/// * https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences +/// * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +/// * https://vt100.net/ +/// +public static class EscSeqUtils { + /// + /// Escape key code (ASCII 27/0x1B). + /// + public static readonly char KeyEsc = (char)Key.Esc; + + /// + /// ESC [ - The CSI (Control Sequence Introducer). + /// + public static readonly string CSI = $"{KeyEsc}["; + + /// + /// ESC [ ? 1003 h - Enable mouse event tracking. + /// + public static readonly string CSI_EnableAnyEventMouse = CSI + "?1003h"; + + /// + /// ESC [ ? 1006 h - Enable SGR (Select Graphic Rendition). + /// + public static readonly string CSI_EnableSgrExtModeMouse = CSI + "?1006h"; + + /// + /// ESC [ ? 1015 h - Enable URXVT (Unicode Extended Virtual Terminal). + /// + public static readonly string CSI_EnableUrxvtExtModeMouse = CSI + "?1015h"; + + /// + /// ESC [ ? 1003 l - Disable any mouse event tracking. + /// + public static readonly string CSI_DisableAnyEventMouse = CSI + "?1003l"; + + /// + /// ESC [ ? 1006 l - Disable SGR (Select Graphic Rendition). + /// + public static readonly string CSI_DisableSgrExtModeMouse = CSI + "?1006l"; + + /// + /// ESC [ ? 1015 l - Disable URXVT (Unicode Extended Virtual Terminal). + /// + public static readonly string CSI_DisableUrxvtExtModeMouse = CSI + "?1015l"; + + /// + /// ESC [ ? 1047 h - Activate xterm alternative buffer (no backscroll) + /// + public static readonly string CSI_ActivateAltBufferNoBackscroll = CSI + "?1047h"; + + /// + /// ESC [ ? 1047 l - Restore xterm working buffer (with backscroll) + /// + public static readonly string CSI_RestoreAltBufferWithBackscroll = CSI + "?1047l"; + + /// + /// ESC [ ? 1049 h - Save cursor position and activate xterm alternative buffer (no backscroll) + /// + public static readonly string CSI_SaveCursorAndActivateAltBufferNoBackscroll = CSI + "?1049h"; + + /// + /// ESC [ ? 1049 l - Restore cursor position and restore xterm working buffer (with backscroll) + /// + public static readonly string CSI_RestoreCursorAndActivateAltBufferWithBackscroll = CSI + "?1049l"; + + /// + /// Options for ANSI ESC "[xJ" - Clears part of the screen. + /// + public enum ClearScreenOptions { + /// + /// If n is 0 (or missing), clear from cursor to end of screen. + /// + CursorToEndOfScreen = 0, + /// + /// If n is 1, clear from cursor to beginning of the screen. + /// + CursorToBeginningOfScreen = 1, + /// + /// If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + /// + EntireScreen = 2, + /// + /// If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + /// + EntireScreenAndScrollbackBuffer = 3 + } + + /// + /// ESC [ x J - Clears part of the screen. See . + /// + /// + /// + public static string CSI_ClearScreen (ClearScreenOptions option) => $"{CSI}{(int)option}J"; + + #region Cursor + //ESC [ M - RI Reverse Index – Performs the reverse operation of \n, moves cursor up one line, maintains horizontal position, scrolls buffer if necessary* + + /// + /// ESC [ 7 - Save Cursor Position in Memory** + /// + public static readonly string CSI_SaveCursorPosition = CSI + "7"; + + /// + /// ESC [ 8 - DECSR Restore Cursor Position from Memory** + /// + public static readonly string CSI_RestoreCursorPosition = CSI + "8"; + + /// + /// ESC [ 8 ; height ; width t - Set Terminal Window Size + /// https://terminalguide.namepad.de/seq/csi_st-8/ + /// + public static string CSI_SetTerminalWindowSize (int height, int width) => $"{CSI}8;{height};{width}t"; + + //ESC [ < n > A - CUU - Cursor Up Cursor up by < n > + //ESC [ < n > B - CUD - Cursor Down Cursor down by < n > + //ESC [ < n > C - CUF - Cursor Forward Cursor forward (Right) by < n > + //ESC [ < n > D - CUB - Cursor Backward Cursor backward (Left) by < n > + //ESC [ < n > E - CNL - Cursor Next Line - Cursor down < n > lines from current position + //ESC [ < n > F - CPL - Cursor Previous Line Cursor up < n > lines from current position + //ESC [ < n > G - CHA - Cursor Horizontal Absolute Cursor moves to < n > th position horizontally in the current line + //ESC [ < n > d - VPA - Vertical Line Position Absolute Cursor moves to the < n > th position vertically in the current column + + /// + /// ESC [ y ; x H - CUP Cursor Position - Cursor moves to x ; y coordinate within the viewport, where x is the column of the y line + /// + /// + /// + /// + public static string CSI_SetCursorPosition (int x, int y) => $"{CSI}{y};{x}H"; + + + //ESC [ ; f - HVP Horizontal Vertical Position* Cursor moves to; coordinate within the viewport, where is the column of the line + //ESC [ s - ANSISYSSC Save Cursor – Ansi.sys emulation **With no parameters, performs a save cursor operation like DECSC + //ESC [ u - ANSISYSRC Restore Cursor – Ansi.sys emulation **With no parameters, performs a restore cursor operation like DECRC + //ESC [ ? 12 h - ATT160 Text Cursor Enable Blinking Start the cursor blinking + //ESC [ ? 12 l - ATT160 Text Cursor Disable Blinking Stop blinking the cursor + /// + /// ESC [ ? 25 h - DECTCEM Text Cursor Enable Mode Show Show the cursor + /// + public static readonly string CSI_ShowCursor = CSI + "?25h"; + + /// + /// ESC [ ? 25 l - DECTCEM Text Cursor Enable Mode Hide Hide the cursor + /// + public static readonly string CSI_HideCursor = CSI + "?25l"; + //ESC [ ? 12 h - ATT160 Text Cursor Enable Blinking Start the cursor blinking + //ESC [ ? 12 l - ATT160 Text Cursor Disable Blinking Stop blinking the cursor + //ESC [ ? 25 h - DECTCEM Text Cursor Enable Mode Show Show the cursor + //ESC [ ? 25 l - DECTCEM Text Cursor Enable Mode Hide Hide the cursor + + /// + /// Styles for ANSI ESC "[x q" - Set Cursor Style + /// + public enum DECSCUSR_Style { + /// + /// DECSCUSR - User Shape - Default cursor shape configured by the user + /// + UserShape = 0, + /// + /// DECSCUSR - Blinking Block - Blinking block cursor shape + /// + BlinkingBlock = 1, + /// + /// DECSCUSR - Steady Block - Steady block cursor shape + /// + SteadyBlock = 2, + /// + /// DECSCUSR - Blinking Underline - Blinking underline cursor shape + /// + BlinkingUnderline = 3, + /// + /// DECSCUSR - Steady Underline - Steady underline cursor shape + /// + SteadyUnderline = 4, + /// + /// DECSCUSR - Blinking Bar - Blinking bar cursor shape + /// + BlinkingBar = 5, + /// + /// DECSCUSR - Steady Bar - Steady bar cursor shape + /// + SteadyBar = 6 + } + + /// + /// ESC [ n SP q - Select Cursor Style (DECSCUSR) + /// https://terminalguide.namepad.de/seq/csi_sq_t_space/ + /// + /// + /// + public static string CSI_SetCursorStyle (DECSCUSR_Style style) => $"{CSI}{(int)style} q"; + + #endregion + + /// + /// Control sequence for enabling mouse events. + /// + public static string CSI_EnableMouseEvents { get; set; } = + CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse; + + /// + /// Control sequence for disabling mouse events. + /// + public static string CSI_DisableMouseEvents { get; set; } = + CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse; + + #region Requests + /// + /// ESC [ ? 6 n - Request Cursor Position Report (?) (DECXCPR) + /// https://terminalguide.namepad.de/seq/csi_sn__p-6/ + /// + public static readonly string CSI_RequestCursorPositionReport = CSI + "?6n"; + + /// + /// The terminal reply to . ESC [ ? (y) ; (x) R + /// + public const string CSI_RequestCursorPositionReport_Terminator = "R"; + + /// + /// ESC [ 0 c - DA Device Attributes - Report the terminal identity. + /// + public static readonly string CSI_ReportDeviceAttributes = CSI + "0c"; + + /// + /// The terminal reply to : Windows Terminal Will emit “\x1b[?1;0c”, indicating "VT101 with No Options". + /// + public const string CSI_ReportDeviceAttributes_Terminator = "c"; + + /// + /// CSI 1 8 t | yes | yes | yes | report window size in chars + /// https://terminalguide.namepad.de/seq/csi_st-18/ + /// + public static readonly string CSI_ReportTerminalSizeInChars = CSI + "18t"; + + /// + /// The terminal reply to : ESC [ 8 ; height ; width t + /// + public const string CSI_ReportTerminalSizeInChars_Terminator = "t"; + + /// + /// The value of the response to indicating value 1 and 2 are the terminal size in chars. + /// + public const string CSI_ReportTerminalSizeInChars_ResponseValue = "8"; + #endregion + + /// + /// Ensures a console key is mapped to one that works correctly with ANSI escape sequences. + /// + /// The . + /// The modified. + public static ConsoleKeyInfo MapConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) + { + ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo; + ConsoleKey key; + var keyChar = consoleKeyInfo.KeyChar; + switch ((uint)keyChar) { + case 0: + if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. + newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + } + break; + case uint n when n > 0 && n <= KeyEsc: + if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') { + key = ConsoleKey.Enter; + newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + } else if (consoleKeyInfo.Key == 0) { + key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1); + newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + true); + } + break; + case 127: // DEL + newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + break; + default: + newConsoleKeyInfo = consoleKeyInfo; + break; + } + + return newConsoleKeyInfo; + } + + /// + /// A helper to resize the as needed. + /// + /// The . + /// The array to resize. + /// The resized. + public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, ConsoleKeyInfo [] cki) + { + Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1); + cki [cki.Length - 1] = consoleKeyInfo; + return cki; + } + + /// + /// Decodes an ANSI escape sequence. + /// + /// The which may contain a request. + /// The which may changes. + /// The which may changes. + /// The array. + /// The which may changes. + /// The control returned by the method. + /// The code returned by the method. + /// The values returned by the method. + /// The terminator returned by the method. + /// Indicates if the escape sequence is a mouse event. + /// The button state. + /// The position. + /// Indicates if the escape sequence is a response to a request. + /// The handler that will process the event. + public static void DecodeEscSeq (EscSeqRequests escSeqRequests, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod, out string c1Control, out string code, out string [] values, out string terminator, out bool isMouse, out List buttonState, out Point pos, out bool isResponse, Action continuousButtonPressedHandler) + { + char [] kChars = GetKeyCharArray (cki); + (c1Control, code, values, terminator) = GetEscapeResult (kChars); + isMouse = false; + buttonState = new List () { 0 }; + pos = default; + isResponse = false; + switch (c1Control) { + case "ESC": + if (values == null && string.IsNullOrEmpty (terminator)) { + key = ConsoleKey.Escape; + newConsoleKeyInfo = new ConsoleKeyInfo (cki [0].KeyChar, key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); + } else if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) { + key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1); + newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar, + key, + false, + true, + true); + } else { + if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) { + key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0]; + } else { + key = (ConsoleKey)cki [1].KeyChar; + } + newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, + (ConsoleKey)Math.Min ((uint)key, 255), + false, + true, + false); + } + break; + case "SS3": + key = GetConsoleKey (terminator [0], values [0], ref mod); + newConsoleKeyInfo = new ConsoleKeyInfo ('\0', + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); + break; + case "CSI": + if (!string.IsNullOrEmpty (code) && code == "<") { + GetMouse (cki, out buttonState, out pos, continuousButtonPressedHandler); + isMouse = true; + return; + } else if (escSeqRequests != null && escSeqRequests.HasResponse (terminator)) { + isResponse = true; + escSeqRequests.Remove (terminator); + return; + } + key = GetConsoleKey (terminator [0], values [0], ref mod); + if (key != 0 && values.Length > 1) { + mod |= GetConsoleModifiers (values [1]); + } + newConsoleKeyInfo = new ConsoleKeyInfo ('\0', + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); + break; + } + } + + /// + /// Gets all the needed information about a escape sequence. + /// + /// The array with all chars. + /// + /// The c1Control returned by , code, values and terminating. + /// + public static (string c1Control, string code, string [] values, string terminating) GetEscapeResult (char [] kChar) + { + if (kChar == null || kChar.Length == 0) { + return (null, null, null, null); + } + if (kChar [0] != KeyEsc) { + throw new InvalidOperationException ("Invalid escape character!"); + } + if (kChar.Length == 1) { + return ("ESC", null, null, null); + } + if (kChar.Length == 2) { + return ("ESC", null, null, kChar [1].ToString ()); + } + string c1Control = GetC1ControlChar (kChar [1]); + string code = null; + int nSep = kChar.Count (x => x == ';') + 1; + string [] values = new string [nSep]; + int valueIdx = 0; + string terminating = ""; + for (int i = 2; i < kChar.Length; i++) { + var c = kChar [i]; + if (char.IsDigit (c)) { + values [valueIdx] += c.ToString (); + } else if (c == ';') { + valueIdx++; + } else if (valueIdx == nSep - 1 || i == kChar.Length - 1) { + terminating += c.ToString (); + } else { + code += c.ToString (); + } + } + + return (c1Control, code, values, terminating); + } + + /// + /// Gets the c1Control used in the called escape sequence. + /// + /// The char used. + /// The c1Control. + public static string GetC1ControlChar (char c) + { + // These control characters are used in the vtXXX emulation. + switch (c) { + case 'D': + return "IND"; // Index + case 'E': + return "NEL"; // Next Line + case 'H': + return "HTS"; // Tab Set + case 'M': + return "RI"; // Reverse Index + case 'N': + return "SS2"; // Single Shift Select of G2 Character Set: affects next character only + case 'O': + return "SS3"; // Single Shift Select of G3 Character Set: affects next character only + case 'P': + return "DCS"; // Device Control String + case 'V': + return "SPA"; // Start of Guarded Area + case 'W': + return "EPA"; // End of Guarded Area + case 'X': + return "SOS"; // Start of String + case 'Z': + return "DECID"; // Return Terminal ID Obsolete form of CSI c (DA) + case '[': + return "CSI"; // Control Sequence Introducer + case '\\': + return "ST"; // String Terminator + case ']': + return "OSC"; // Operating System Command + case '^': + return "PM"; // Privacy Message + case '_': + return "APC"; // Application Program Command + default: + return ""; // Not supported + } + } + + /// + /// Gets the from the value. + /// + /// The value. + /// The or zero. + public static ConsoleModifiers GetConsoleModifiers (string value) + { + switch (value) { + case "2": + return ConsoleModifiers.Shift; + case "3": + return ConsoleModifiers.Alt; + case "4": + return ConsoleModifiers.Shift | ConsoleModifiers.Alt; + case "5": + return ConsoleModifiers.Control; + case "6": + return ConsoleModifiers.Shift | ConsoleModifiers.Control; + case "7": + return ConsoleModifiers.Alt | ConsoleModifiers.Control; + case "8": + return ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control; + default: + return 0; + } + } + + /// + /// Gets the depending on terminating and value. + /// + /// The terminating. + /// The value. + /// The which may changes. + /// The and probably the . + public static ConsoleKey GetConsoleKey (char terminating, string value, ref ConsoleModifiers mod) + { + ConsoleKey key; + switch (terminating) { + case 'A': + key = ConsoleKey.UpArrow; + break; + case 'B': + key = ConsoleKey.DownArrow; + break; + case 'C': + key = ConsoleKey.RightArrow; + break; + case 'D': + key = ConsoleKey.LeftArrow; + break; + case 'F': + key = ConsoleKey.End; + break; + case 'H': + key = ConsoleKey.Home; + break; + case 'P': + key = ConsoleKey.F1; + break; + case 'Q': + key = ConsoleKey.F2; + break; + case 'R': + key = ConsoleKey.F3; + break; + case 'S': + key = ConsoleKey.F4; + break; + case 'Z': + key = ConsoleKey.Tab; + mod |= ConsoleModifiers.Shift; + break; + case '~': + switch (value) { + case "2": + key = ConsoleKey.Insert; + break; + case "3": + key = ConsoleKey.Delete; + break; + case "5": + key = ConsoleKey.PageUp; + break; + case "6": + key = ConsoleKey.PageDown; + break; + case "15": + key = ConsoleKey.F5; + break; + case "17": + key = ConsoleKey.F6; + break; + case "18": + key = ConsoleKey.F7; + break; + case "19": + key = ConsoleKey.F8; + break; + case "20": + key = ConsoleKey.F9; + break; + case "21": + key = ConsoleKey.F10; + break; + case "23": + key = ConsoleKey.F11; + break; + case "24": + key = ConsoleKey.F12; + break; + default: + key = 0; + break; + } + break; + default: + key = 0; + break; + } + + return key; + } + + /// + /// A helper to get only the from the array. + /// + /// + /// The char array of the escape sequence. + public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki) + { + char [] kChar = new char [] { }; + var length = 0; + foreach (var kc in cki) { + length++; + Array.Resize (ref kChar, length); + kChar [length - 1] = kc.KeyChar; + } + + return kChar; + } + + private static MouseFlags? lastMouseButtonPressed; + //private static MouseFlags? lastMouseButtonReleased; + private static bool isButtonPressed; + //private static bool isButtonReleased; + private static bool isButtonClicked; + private static bool isButtonDoubleClicked; + private static bool isButtonTripleClicked; + private static Point point; + + /// + /// Gets the mouse button flags and the position. + /// + /// The array. + /// The mouse button flags. + /// The mouse position. + /// The handler that will process the event. + public static void GetMouse (ConsoleKeyInfo [] cki, out List mouseFlags, out Point pos, Action continuousButtonPressedHandler) + { + MouseFlags buttonState = 0; + pos = new Point (); + int buttonCode = 0; + bool foundButtonCode = false; + int foundPoint = 0; + string value = ""; + var kChar = GetKeyCharArray (cki); + //System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}"); + for (int i = 0; i < kChar.Length; i++) { + var c = kChar [i]; + if (c == '<') { + foundButtonCode = true; + } else if (foundButtonCode && c != ';') { + value += c.ToString (); + } else if (c == ';') { + if (foundButtonCode) { + foundButtonCode = false; + buttonCode = int.Parse (value); + } + if (foundPoint == 1) { + pos.X = int.Parse (value) - 1; + } + value = ""; + foundPoint++; + } else if (foundPoint > 0 && c != 'm' && c != 'M') { + value += c.ToString (); + } else if (c == 'm' || c == 'M') { + //pos.Y = int.Parse (value) + Console.WindowTop - 1; + pos.Y = int.Parse (value) - 1; + + switch (buttonCode) { + case 0: + case 8: + case 16: + case 24: + case 32: + case 36: + case 40: + case 48: + case 56: + buttonState = c == 'M' ? MouseFlags.Button1Pressed + : MouseFlags.Button1Released; + break; + case 1: + case 9: + case 17: + case 25: + case 33: + case 37: + case 41: + case 45: + case 49: + case 53: + case 57: + case 61: + buttonState = c == 'M' ? MouseFlags.Button2Pressed + : MouseFlags.Button2Released; + break; + case 2: + case 10: + case 14: + case 18: + case 22: + case 26: + case 30: + case 34: + case 42: + case 46: + case 50: + case 54: + case 58: + case 62: + buttonState = c == 'M' ? MouseFlags.Button3Pressed + : MouseFlags.Button3Released; + break; + case 35: + //// Needed for Windows OS + //if (isButtonPressed && c == 'm' + // && (lastMouseEvent.ButtonState == MouseFlags.Button1Pressed + // || lastMouseEvent.ButtonState == MouseFlags.Button2Pressed + // || lastMouseEvent.ButtonState == MouseFlags.Button3Pressed)) { + + // switch (lastMouseEvent.ButtonState) { + // case MouseFlags.Button1Pressed: + // buttonState = MouseFlags.Button1Released; + // break; + // case MouseFlags.Button2Pressed: + // buttonState = MouseFlags.Button2Released; + // break; + // case MouseFlags.Button3Pressed: + // buttonState = MouseFlags.Button3Released; + // break; + // } + //} else { + // buttonState = MouseFlags.ReportMousePosition; + //} + //break; + case 39: + case 43: + case 47: + case 51: + case 55: + case 59: + case 63: + buttonState = MouseFlags.ReportMousePosition; + break; + case 64: + buttonState = MouseFlags.WheeledUp; + break; + case 65: + buttonState = MouseFlags.WheeledDown; + break; + case 68: + case 72: + case 80: + buttonState = MouseFlags.WheeledLeft; // Shift/Ctrl+WheeledUp + break; + case 69: + case 73: + case 81: + buttonState = MouseFlags.WheeledRight; // Shift/Ctrl+WheeledDown + break; + } + // Modifiers. + switch (buttonCode) { + case 8: + case 9: + case 10: + case 43: + buttonState |= MouseFlags.ButtonAlt; + break; + case 14: + case 47: + buttonState |= MouseFlags.ButtonAlt | MouseFlags.ButtonShift; + break; + case 16: + case 17: + case 18: + case 51: + buttonState |= MouseFlags.ButtonCtrl; + break; + case 22: + case 55: + buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift; + break; + case 24: + case 25: + case 26: + case 59: + buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt; + break; + case 30: + case 63: + buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt; + break; + case 32: + case 33: + case 34: + buttonState |= MouseFlags.ReportMousePosition; + break; + case 36: + case 37: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonShift; + break; + case 39: + case 68: + case 69: + buttonState |= MouseFlags.ButtonShift; + break; + case 40: + case 41: + case 42: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt; + break; + case 45: + case 46: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt | MouseFlags.ButtonShift; + break; + case 48: + case 49: + case 50: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl; + break; + case 53: + case 54: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift; + break; + case 56: + case 57: + case 58: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt; + break; + case 61: + case 62: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt; + break; + } + } + } + + mouseFlags = new List () { MouseFlags.AllEvents }; + + if (lastMouseButtonPressed != null && !isButtonPressed && !buttonState.HasFlag (MouseFlags.ReportMousePosition) + && !buttonState.HasFlag (MouseFlags.Button1Released) + && !buttonState.HasFlag (MouseFlags.Button2Released) + && !buttonState.HasFlag (MouseFlags.Button3Released) + && !buttonState.HasFlag (MouseFlags.Button4Released)) { + + lastMouseButtonPressed = null; + isButtonPressed = false; + } + + if (!isButtonClicked && !isButtonDoubleClicked && ((buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || + buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed) && lastMouseButtonPressed == null) || + isButtonPressed && lastMouseButtonPressed != null && buttonState.HasFlag (MouseFlags.ReportMousePosition)) { + + mouseFlags [0] = buttonState; + lastMouseButtonPressed = buttonState; + isButtonPressed = true; + + if ((mouseFlags [0] & MouseFlags.ReportMousePosition) == 0) { + point = new Point () { + X = pos.X, + Y = pos.Y + }; + + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessContinuousButtonPressedAsync (buttonState, continuousButtonPressedHandler)); + return false; + }); + } else if (mouseFlags [0] == MouseFlags.ReportMousePosition) { + isButtonPressed = false; + } + + } else if (isButtonDoubleClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || + buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) { + + mouseFlags [0] = GetButtonTripleClicked (buttonState); + isButtonDoubleClicked = false; + isButtonTripleClicked = true; + + } else if (isButtonClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || + buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) { + + mouseFlags [0] = GetButtonDoubleClicked (buttonState); + isButtonClicked = false; + isButtonDoubleClicked = true; + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); + return false; + }); + + } + //else if (isButtonReleased && !isButtonClicked && buttonState == MouseFlags.ReportMousePosition) { + // mouseFlag [0] = GetButtonClicked ((MouseFlags)lastMouseButtonReleased); + // lastMouseButtonReleased = null; + // isButtonReleased = false; + // isButtonClicked = true; + // Application.MainLoop.AddIdle (() => { + // Task.Run (async () => await ProcessButtonClickedAsync ()); + // return false; + // }); + + //} + else if (!isButtonClicked && !isButtonDoubleClicked && (buttonState == MouseFlags.Button1Released || buttonState == MouseFlags.Button2Released || + buttonState == MouseFlags.Button3Released || buttonState == MouseFlags.Button4Released)) { + + mouseFlags [0] = buttonState; + isButtonPressed = false; + + if (isButtonTripleClicked) { + isButtonTripleClicked = false; + } else if (pos.X == point.X && pos.Y == point.Y) { + mouseFlags.Add (GetButtonClicked (buttonState)); + isButtonClicked = true; + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessButtonClickedAsync ()); + return false; + }); + } + + point = pos; + + //if ((lastMouseButtonPressed & MouseFlags.ReportMousePosition) == 0) { + // lastMouseButtonReleased = buttonState; + // isButtonPressed = false; + // isButtonReleased = true; + //} else { + // lastMouseButtonPressed = null; + // isButtonPressed = false; + //} + + } else if (buttonState == MouseFlags.WheeledUp) { + + mouseFlags [0] = MouseFlags.WheeledUp; + + } else if (buttonState == MouseFlags.WheeledDown) { + + mouseFlags [0] = MouseFlags.WheeledDown; + + } else if (buttonState == MouseFlags.WheeledLeft) { + + mouseFlags [0] = MouseFlags.WheeledLeft; + + } else if (buttonState == MouseFlags.WheeledRight) { + + mouseFlags [0] = MouseFlags.WheeledRight; + + } else if (buttonState == MouseFlags.ReportMousePosition) { + mouseFlags [0] = MouseFlags.ReportMousePosition; + + } else { + mouseFlags [0] = buttonState; + //foreach (var flag in buttonState.GetUniqueFlags()) { + // mouseFlag [0] |= flag; + //} + } + + mouseFlags [0] = SetControlKeyStates (buttonState, mouseFlags [0]); + //buttonState = mouseFlags; + + //System.Diagnostics.Debug.WriteLine ($"buttonState: {buttonState} X: {pos.X} Y: {pos.Y}"); + //foreach (var mf in mouseFlags) { + // System.Diagnostics.Debug.WriteLine ($"mouseFlags: {mf} X: {pos.X} Y: {pos.Y}"); + //} + } + + private static async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag, Action continuousButtonPressedHandler) + { + while (isButtonPressed) { + await Task.Delay (100); + //var me = new MouseEvent () { + // X = point.X, + // Y = point.Y, + // Flags = mouseFlag + //}; + + var view = Application.WantContinuousButtonPressedView; + if (view == null) + break; + if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { + Application.MainLoop.Invoke (() => continuousButtonPressedHandler (mouseFlag, point)); + } + } + } + + private static async Task ProcessButtonClickedAsync () + { + await Task.Delay (300); + isButtonClicked = false; + } + + private static async Task ProcessButtonDoubleClickedAsync () + { + await Task.Delay (300); + isButtonDoubleClicked = false; + } + + private static MouseFlags GetButtonClicked (MouseFlags mouseFlag) + { + MouseFlags mf = default; + switch (mouseFlag) { + case MouseFlags.Button1Released: + mf = MouseFlags.Button1Clicked; + break; + + case MouseFlags.Button2Released: + mf = MouseFlags.Button2Clicked; + break; + + case MouseFlags.Button3Released: + mf = MouseFlags.Button3Clicked; + break; + } + return mf; + } + + private static MouseFlags GetButtonDoubleClicked (MouseFlags mouseFlag) + { + MouseFlags mf = default; + switch (mouseFlag) { + case MouseFlags.Button1Pressed: + mf = MouseFlags.Button1DoubleClicked; + break; + + case MouseFlags.Button2Pressed: + mf = MouseFlags.Button2DoubleClicked; + break; + + case MouseFlags.Button3Pressed: + mf = MouseFlags.Button3DoubleClicked; + break; + } + return mf; + } + + private static MouseFlags GetButtonTripleClicked (MouseFlags mouseFlag) + { + MouseFlags mf = default; + switch (mouseFlag) { + case MouseFlags.Button1Pressed: + mf = MouseFlags.Button1TripleClicked; + break; + + case MouseFlags.Button2Pressed: + mf = MouseFlags.Button2TripleClicked; + break; + + case MouseFlags.Button3Pressed: + mf = MouseFlags.Button3TripleClicked; + break; + } + return mf; + } + + private static MouseFlags SetControlKeyStates (MouseFlags buttonState, MouseFlags mouseFlag) + { + if ((buttonState & MouseFlags.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0) + mouseFlag |= MouseFlags.ButtonCtrl; + + if ((buttonState & MouseFlags.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0) + mouseFlag |= MouseFlags.ButtonShift; + + if ((buttonState & MouseFlags.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0) + mouseFlag |= MouseFlags.ButtonAlt; + return mouseFlag; + } + + // TODO: Move this out of here and into ConsoleDriver or somewhere else. + /// + /// Get the terminal that holds the console driver. + /// + /// The process. + /// If supported the executable console process, null otherwise. + public static Process GetParentProcess (Process process) + { + if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { + return null; + } + + string query = "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = " + process.Id; + using (ManagementObjectSearcher mos = new ManagementObjectSearcher (query)) { + foreach (ManagementObject mo in mos.Get ()) { + if (mo ["ParentProcessId"] != null) { + try { + var id = Convert.ToInt32 (mo ["ParentProcessId"]); + return Process.GetProcessById (id); + } catch { + } + } + } + } + return null; + } +} diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 001e3f6fd3..a15ba0a0f1 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -119,7 +119,7 @@ internal class NetEvents { bool _neededProcessRequest; #endif public bool IsTerminalWithOptions { get; set; } - public EscSeqReqProc EscSeqReqProc { get; } = new EscSeqReqProc (); + public EscSeqRequests EscSeqRequests { get; } = new EscSeqRequests (); public NetEvents (ConsoleDriver consoleDriver) { @@ -187,17 +187,20 @@ void GetConsoleKey () newConsoleKeyInfo = consoleKeyInfo; _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki); if (!Console.KeyAvailable) { - DecodeEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); + ProcessIncomingEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); _cki = null; _isEscSeq = false; break; } } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) { - DecodeEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); + ProcessIncomingEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); _cki = null; break; } else { - GetConsoleInputType (consoleKeyInfo); + _inputResultQueue.Enqueue (new InputResult { + EventType = EventType.Key, + ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo) + }); _isEscSeq = false; break; } @@ -206,6 +209,42 @@ void GetConsoleKey () void CheckWinChange () { + void WaitWinChange () + { + while (true) { + // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. + Thread.Sleep (10); + if (_stopTasks) { + return; + } + switch (IsTerminalWithOptions) { + case false: + int buffHeight, buffWidth; + if (((NetDriver)_consoleDriver).IsWinPlatform) { + buffHeight = Math.Max (Console.BufferHeight, 0); + buffWidth = Math.Max (Console.BufferWidth, 0); + } else { + buffHeight = _consoleDriver.Rows; + buffWidth = _consoleDriver.Cols; + } + if (RequestWindowSize ( + Math.Max (Console.WindowHeight, 0), + Math.Max (Console.WindowWidth, 0), + buffHeight, + buffWidth)) { + + return; + } + break; + case true: + //Request the size of the text area in characters. + EscSeqRequests.Add (EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator); + Console.Out.Write (EscSeqUtils.CSI_ReportTerminalSizeInChars); + break; + } + } + } + while (true) { if (_stopTasks) { return; @@ -217,49 +256,23 @@ void CheckWinChange () } } - void WaitWinChange () + bool RequestWindowSize (int winHeight, int winWidth, int buffHeight, int buffWidth) { - while (true) { - // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. - Thread.Sleep (10); - if (_stopTasks) { - return; - } - switch (IsTerminalWithOptions) { - case false: - int buffHeight, buffWidth; - if (((NetDriver)_consoleDriver).IsWinPlatform) { - buffHeight = Math.Max (Console.BufferHeight, 0); - buffWidth = Math.Max (Console.BufferWidth, 0); - } else { - buffHeight = _consoleDriver.Rows; - buffWidth = _consoleDriver.Cols; - } - if (IsWinChanged ( - Math.Max (Console.WindowHeight, 0), - Math.Max (Console.WindowWidth, 0), - buffHeight, - buffWidth)) { - - return; + void EnqueueRequestWindowSize (Size size) + { + _inputResultQueue.Enqueue (new InputResult () { + EventType = EventType.WindowSize, + WindowSizeEvent = new WindowSizeEvent () { + Size = size } - break; - case true: - //Request the size of the text area in characters. - EscSeqReqProc.Add ("t"); - Console.Out.Write ("\x1b[18t"); - break; - } + }); } - } - bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth) - { if (!_consoleDriver.EnableConsoleScrolling) { if (winWidth != _consoleDriver.Cols || winHeight != _consoleDriver.Rows) { var w = Math.Max (winWidth, 0); var h = Math.Max (winHeight, 0); - GetWindowSizeEvent (new Size (w, h)); + EnqueueRequestWindowSize (new Size (w, h)); return true; } } else { @@ -267,66 +280,33 @@ bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth) || buffWidth != _consoleDriver.Cols || buffHeight != _consoleDriver.Rows) { _lastWindowHeight = Math.Max (winHeight, 0); - GetWindowSizeEvent (new Size (winWidth, _lastWindowHeight)); + EnqueueRequestWindowSize (new Size (winWidth, _lastWindowHeight)); return true; } } return false; } - void GetWindowSizeEvent (Size size) - { - WindowSizeEvent windowSizeEvent = new WindowSizeEvent () { - Size = size - }; - - _inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.WindowSize, - WindowSizeEvent = windowSizeEvent - }); - } - - void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) + // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event) + void ProcessIncomingEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) { - InputResult inputResult = new InputResult { - EventType = EventType.Key - }; - MouseEvent mouseEvent = new MouseEvent (); - ConsoleKeyInfo newConsoleKeyInfo = EscSeqUtils.GetConsoleInputKey (consoleKeyInfo); - if (inputResult.EventType == EventType.Key) { - inputResult.ConsoleKeyInfo = newConsoleKeyInfo; - } else { - inputResult.MouseEvent = mouseEvent; - } - - _inputResultQueue.Enqueue (inputResult); - } - - void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) - { - // isKeyMouse is true if it's CSI<, false otherwise - EscSeqUtils.DecodeEscSeq (EscSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out var c1Control, out var code, out var values, out var terminating, out var isKeyMouse, out var mouseFlags, out var pos, out var isReq, ProcessContinuousButtonPressed); - - if (isKeyMouse) { + // isMouse is true if it's CSI<, false otherwise + EscSeqUtils.DecodeEscSeq (EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod, + out var c1Control, out var code, out var values, out var terminating, + out var isMouse, out var mouseFlags, + out var pos, out var isReq, + (f, p) => HandleMouseEvent (MapMouseFlags (f), p)); + + if (isMouse) { foreach (var mf in mouseFlags) { - GetMouseEvent (MapMouseFlags (mf), pos); + HandleMouseEvent (MapMouseFlags (mf), pos); } return; } else if (isReq) { - GetRequestEvent (c1Control, code, values, terminating); + HandleRequestEvent (c1Control, code, values, terminating); return; } - InputResult inputResult = new InputResult { - EventType = EventType.Key, - ConsoleKeyInfo = newConsoleKeyInfo - }; - - _inputResultQueue.Enqueue (inputResult); - } - - void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) - { - GetMouseEvent (MapMouseFlags (mouseFlag), pos); + HandleKeyboardEvent (newConsoleKeyInfo); } MouseButtonState MapMouseFlags (MouseFlags mouseFlags) @@ -430,10 +410,11 @@ MouseButtonState MapMouseFlags (MouseFlags mouseFlags) Point _lastCursorPosition; - void GetRequestEvent (string c1Control, string code, string [] values, string terminating) + void HandleRequestEvent (string c1Control, string code, string [] values, string terminating) { switch (terminating) { - case "R": // Reports cursor position as CSI r ; c R + // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed. + case EscSeqUtils.CSI_RequestCursorPositionReport_Terminator: Point point = new Point { X = int.Parse (values [1]) - 1, Y = int.Parse (values [0]) - 1 @@ -452,14 +433,13 @@ void GetRequestEvent (string c1Control, string code, string [] values, string te return; } break; - case "c": + case EscSeqUtils.CSI_ReportDeviceAttributes_Terminator: try { var parent = EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ()); if (parent == null) { Debug.WriteLine ("Not supported!"); } } catch (Exception ex) { Debug.WriteLine (ex.Message); } - if (c1Control == "CSI" && values.Length == 2 && values [0] == "1" && values [1] == "0") { // Reports CSI?1;0c ("VT101 with No Options") @@ -468,10 +448,10 @@ void GetRequestEvent (string c1Control, string code, string [] values, string te IsTerminalWithOptions = true; } break; - case "t": + case EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator: switch (values [0]) { - case "8": - IsWinChanged ( + case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue: + RequestWindowSize ( Math.Max (int.Parse (values [1]), 0), Math.Max (int.Parse (values [2]), 0), Math.Max (int.Parse (values [1]), 0), @@ -502,7 +482,7 @@ void SetRequestedEvent (string c1Control, string code, string [] values, string }); } - void GetMouseEvent (MouseButtonState buttonState, Point pos) + void HandleMouseEvent (MouseButtonState buttonState, Point pos) { MouseEvent mouseEvent = new MouseEvent () { Position = pos, @@ -585,6 +565,16 @@ public struct InputResult { public WindowPositionEvent WindowPositionEvent; public RequestResponseEvent RequestResponseEvent; } + + void HandleKeyboardEvent (ConsoleKeyInfo cki) + { + InputResult inputResult = new InputResult { + EventType = EventType.Key, + ConsoleKeyInfo = cki + }; + + _inputResultQueue.Enqueue (inputResult); + } } internal class NetDriver : ConsoleDriver { @@ -686,10 +676,10 @@ public override void End () Console.ResetColor (); //Disable alternative screen buffer. - Console.Out.Write ("\x1b[?1049l"); + Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndActivateAltBufferWithBackscroll); //Set cursor key to cursor. - Console.Out.Write ("\x1b[?25h"); + Console.Out.Write (EscSeqUtils.CSI_ShowCursor); Console.Out.Close (); } @@ -736,10 +726,10 @@ public override void Init (Action terminalResized) if (NetWinConsole != null) { //Enable alternative screen buffer. - Console.Out.Write ("\x1b[?1049h"); + Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll); //Set cursor key to application. - Console.Out.Write ("\x1b[?25l"); + Console.Out.Write (EscSeqUtils.CSI_HideCursor); Console.TreatControlCAsInput = true; @@ -771,7 +761,7 @@ public virtual void ResizeScreen () if (NetWinConsole == null) { return; } - + if (!EnableConsoleScrolling && Console.WindowHeight > 0) { // Not supported on Unix. if (IsWinPlatform) { @@ -793,7 +783,7 @@ public virtual void ResizeScreen () Clip = new Rect (0, 0, Cols, Rows); } } else { - Console.Out.Write ($"\x1b[8;{Rows};{Cols}t"); + Console.Out.Write (EscSeqUtils.CSI_SetTerminalWindowSize (Rows, Cols)); } } else { if (IsWinPlatform) { @@ -815,7 +805,7 @@ public virtual void ResizeScreen () } } } else { - Console.Out.Write ($"\x1b[30;{Rows};{Cols}t"); + Console.Out.Write (EscSeqUtils.CSI_SetTerminalWindowSize (Rows, Cols)); } } Clip = new Rect (0, 0, Cols, Rows); @@ -896,20 +886,20 @@ public override void UpdateScreen () continue; } - if (lastCol == -1) + if (lastCol == -1) { lastCol = col; + } var attr = Contents [row, col, 1]; if (attr != redrawAttr) { redrawAttr = attr; - output.Append (WriteAttributes (attr)); + output.Append (GetCsiForAttribute (attr)); } outputWidth++; var rune = (Rune)Contents [row, col, 0]; if (rune.Utf16SequenceLength == 1) { output.Append (rune); } else { - // TODO: Not sure we need to do this. I think we can just append the rune. output.Append (rune.ToString ()); } Contents [row, col, 2] = 0; @@ -923,17 +913,11 @@ public override void UpdateScreen () SetCursorPosition (0, 0); } - void SetVirtualCursorPosition (int col, int row) + // TODO: Move this to EscSeqUtils in such a way that it remains platform agnostic. + string GetCsiForAttribute (int attr) { - Console.Out.Write ($"\x1b[{row + 1};{col + 1}H"); - } - - System.Text.StringBuilder WriteAttributes (int attr) - { - const string CSI = "\x1b["; int bg = 0; int fg = 0; - System.Text.StringBuilder sb = new System.Text.StringBuilder (); IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) .OfType () @@ -944,9 +928,7 @@ System.Text.StringBuilder WriteAttributes (int attr) if (values.Contains ((attr >> 16) & 0xffff)) { fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff)); } - sb.Append ($"{CSI}{bg};{fg}m"); - - return sb; + return $"{EscSeqUtils.CSI}{bg};{fg}m"; } int MapColors (ConsoleColor color, bool isForeground = true) @@ -983,7 +965,8 @@ bool SetCursorPosition (int col, int row) return false; } } else { - SetVirtualCursorPosition (col, row); + // TODO: Explain why + 1 is needed (and why we do this for non-Windows). + Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1)); return true; } } @@ -1064,12 +1047,12 @@ public override bool EnsureCursorVisibility () public void StartReportingMouseMoves () { - Console.Out.Write (EscSeqUtils.EnableMouseEvents); + Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents); } public void StopReportingMouseMoves () { - Console.Out.Write (EscSeqUtils.DisableMouseEvents); + Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents); } #region Not Implemented @@ -1233,8 +1216,26 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle mLoop.ProcessInput = (e) => ProcessInput (e); // Check if terminal supports requests - _mainLoop._netEvents.EscSeqReqProc.Add ("c"); - Console.Out.Write ("\x1b[0c"); + //143 63 c * DA - Device Attributes + //* [c = Terminal will identify itself + //* [?1; 2c = Terminal is saying it is a VT100 with AVO + //* [> 0c = Secondary DA request (distinguishes VT240 from VT220) + + //CSI Ps c - Send Device Attributes (Primary DA). + // Ps = 0 or omitted ⇒ request attributes from terminal.The + // response depends on the decTerminalID resource setting. + // ⇒ CSI ? 1 ; 2 c ("VT100 with Advanced Video Option") + // ⇒ CSI ? 1 ; 0 c ("VT101 with No Options") + // ⇒ CSI ? 4 ; 6 c ("VT132 with Advanced Video and Graphics") + // ⇒ CSI ? 6 c ("VT102") + // ⇒ CSI ? 7 c ("VT131") + // ⇒ CSI ? 1 2; Ps c ("VT125") + // ⇒ CSI ? 6 2; Ps c ("VT220") + // ⇒ CSI ? 6 3; Ps c ("VT320") + // ⇒ CSI ? 6 4; Ps c ("VT420") + // Windows: ESC [ 0 c DA Device Attributes Report the terminal identity. Will emit “\x1b[?1;0c”, indicating "VT101 with No Options". + _mainLoop._netEvents.EscSeqRequests.Add (EscSeqUtils.CSI_ReportDeviceAttributes_Terminator); + Console.Out.Write (EscSeqUtils.CSI_ReportDeviceAttributes); } void ProcessInput (NetEvents.InputResult inputEvent) @@ -1266,6 +1267,8 @@ void ProcessInput (NetEvents.InputResult inputEvent) ChangeWin (inputEvent.WindowSizeEvent.Size); break; case NetEvents.EventType.RequestResponse: + // BUGBUG: What is this for? It does not seem to be used anywhere. + // It is also not clear what it does. View.Data is documented as "This property is not used internally" Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; break; } diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index d7c8574989..6e18af99e4 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1528,15 +1528,9 @@ public override void Init (Action terminalResized) try { // Needed for Windows Terminal - // ESC [ ? 1047 h Activate xterm alternative buffer (no backscroll) - // ESC [ ? 1047 l Restore xterm working buffer (with backscroll) - // ESC [ ? 1048 h Save cursor position - // ESC [ ? 1048 l Restore cursor position - // ESC [ ? 1049 h Save cursor position and activate xterm alternative buffer (no backscroll) - // ESC [ ? 1049 l Restore cursor position and restore xterm working buffer (with backscroll) // Per Issue #2264 using the alternative screen buffer is required for Windows Terminal to not // wipe out the backscroll buffer when the application exits. - Console.Out.Write ("\x1b[?1047h"); + Console.Out.Write (EscSeqUtils.CSI_ActivateAltBufferNoBackscroll); var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); Cols = winSize.Width; @@ -1572,14 +1566,7 @@ public virtual void ResizeScreen () }; WinConsole.ForceRefreshCursorVisibility (); if (!EnableConsoleScrolling) { - // ANSI ESC "[xJ" Clears part of the screen. - // If n is 0 (or missing), clear from cursor to end of screen. - // If n is 1, clear from cursor to beginning of the screen. - // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). - // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer - // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer - Console.Out.Write ("\x1b[3J"); - //Console.Out.Write ("\x1b[0J"); + Console.Out.Write (EscSeqUtils.CSI_ClearScreen (EscSeqUtils.ClearScreenOptions.CursorToEndOfScreen)); } } @@ -1752,12 +1739,10 @@ public override void End () // end of the screen. // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE // backbuffer! So we need to use [0J instead. - Console.Out.Write ("\x1b[0J"); + Console.Out.Write (EscSeqUtils.CSI_ClearScreen(0)); // Disable alternative screen buffer. - Console.Out.Write ("\x1b[?1047l"); - - // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 + Console.Out.Write (EscSeqUtils.CSI_RestoreAltBufferWithBackscroll); } #region Not Implemented diff --git a/Terminal.Gui/Input/EscSeqUtils/EscSeqReq.cs b/Terminal.Gui/Input/EscSeqUtils/EscSeqReq.cs deleted file mode 100644 index eaa3084ad9..0000000000 --- a/Terminal.Gui/Input/EscSeqUtils/EscSeqReq.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Terminal.Gui { - /// - /// Represents the state of an ANSI escape sequence request. - /// - /// - /// This is needed because there are some escape sequence requests responses that are equal - /// with some normal escape sequences and thus, will be only considered the responses to the - /// requests that were registered with this object. - /// - public class EscSeqReqStatus { - /// - /// Gets the terminating. - /// - public string Terminating { get; } - /// - /// Gets the number of requests. - /// - public int NumRequests { get; } - /// - /// Gets information about unfinished requests. - /// - public int NumOutstanding { get; set; } - - /// - /// Creates a new state of escape sequence request. - /// - /// The terminating. - /// The number of requests. - public EscSeqReqStatus (string terminating, int numOfReq) - { - Terminating = terminating; - NumRequests = NumOutstanding = numOfReq; - } - } - - /// - /// Manages a list of . - /// - public class EscSeqReqProc { - /// - /// Gets the list. - /// - public List EscSeqReqStats { get; } = new List (); - - /// - /// Adds a new instance to the list. - /// - /// The terminating. - /// The number of requests. - public void Add (string terminating, int numOfReq = 1) - { - lock (EscSeqReqStats) { - var found = EscSeqReqStats.Find (x => x.Terminating == terminating); - if (found == null) { - EscSeqReqStats.Add (new EscSeqReqStatus (terminating, numOfReq)); - } else if (found != null && found.NumOutstanding < found.NumRequests) { - found.NumOutstanding = Math.Min (found.NumOutstanding + numOfReq, found.NumRequests); - } - } - } - - /// - /// Removes a instance from the list. - /// - /// The terminating string. - public void Remove (string terminating) - { - lock (EscSeqReqStats) { - var found = EscSeqReqStats.Find (x => x.Terminating == terminating); - if (found == null) { - return; - } - if (found != null && found.NumOutstanding == 0) { - EscSeqReqStats.Remove (found); - } else if (found != null && found.NumOutstanding > 0) { - found.NumOutstanding--; - if (found.NumOutstanding == 0) { - EscSeqReqStats.Remove (found); - } - } - } - } - - /// - /// Indicates if a with the exist - /// in the list. - /// - /// - /// if exist, otherwise. - public bool Requested (string terminating) - { - lock (EscSeqReqStats) { - var found = EscSeqReqStats.Find (x => x.Terminating == terminating); - if (found == null) { - return false; - } - if (found != null && found.NumOutstanding > 0) { - return true; - } else { - EscSeqReqStats.Remove (found); - } - return false; - } - } - } -} diff --git a/Terminal.Gui/Input/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/Input/EscSeqUtils/EscSeqUtils.cs deleted file mode 100644 index 21a9aac594..0000000000 --- a/Terminal.Gui/Input/EscSeqUtils/EscSeqUtils.cs +++ /dev/null @@ -1,907 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Management; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -namespace Terminal.Gui { - /// - /// Provides a platform-independent API for managing ANSI escape sequence codes. - /// - public static class EscSeqUtils { - /// - /// Represents the escape key. - /// - public static readonly char KeyEsc = (char)Key.Esc; - /// - /// Represents the CSI (Control Sequence Introducer). - /// - public static readonly string KeyCSI = $"{KeyEsc}["; - /// - /// Represents the CSI for enable any mouse event tracking. - /// - public static readonly string CSI_EnableAnyEventMouse = KeyCSI + "?1003h"; - /// - /// Represents the CSI for enable SGR (Select Graphic Rendition). - /// - public static readonly string CSI_EnableSgrExtModeMouse = KeyCSI + "?1006h"; - /// - /// Represents the CSI for enable URXVT (Unicode Extended Virtual Terminal). - /// - public static readonly string CSI_EnableUrxvtExtModeMouse = KeyCSI + "?1015h"; - /// - /// Represents the CSI for disable any mouse event tracking. - /// - public static readonly string CSI_DisableAnyEventMouse = KeyCSI + "?1003l"; - /// - /// Represents the CSI for disable SGR (Select Graphic Rendition). - /// - public static readonly string CSI_DisableSgrExtModeMouse = KeyCSI + "?1006l"; - /// - /// Represents the CSI for disable URXVT (Unicode Extended Virtual Terminal). - /// - public static readonly string CSI_DisableUrxvtExtModeMouse = KeyCSI + "?1015l"; - - /// - /// Control sequence for enable mouse events. - /// - public static string EnableMouseEvents { get; set; } = - CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse; - /// - /// Control sequence for disable mouse events. - /// - public static string DisableMouseEvents { get; set; } = - CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse; - - /// - /// Ensures a console key is mapped to one that works correctly with ANSI escape sequences. - /// - /// The . - /// The modified. - public static ConsoleKeyInfo GetConsoleInputKey (ConsoleKeyInfo consoleKeyInfo) - { - ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo; - ConsoleKey key; - var keyChar = consoleKeyInfo.KeyChar; - switch ((uint)keyChar) { - case 0: - if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. - newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); - } - break; - case uint n when (n >= '\u0001' && n <= '\u001a'): - if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') { - key = ConsoleKey.Enter; - newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); - } else if (consoleKeyInfo.Key == 0) { - key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1); - newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - true); - } - break; - case 127: - newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); - break; - default: - newConsoleKeyInfo = consoleKeyInfo; - break; - } - - return newConsoleKeyInfo; - } - - /// - /// A helper to resize the as needed. - /// - /// The . - /// The array to resize. - /// The resized. - public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, ConsoleKeyInfo [] cki) - { - Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1); - cki [cki.Length - 1] = consoleKeyInfo; - return cki; - } - - /// - /// Decodes a escape sequence to been processed in the appropriate manner. - /// - /// The which may contain a request. - /// The which may changes. - /// The which may changes. - /// The array. - /// The which may changes. - /// The control returned by the method. - /// The code returned by the method. - /// The values returned by the method. - /// The terminating returned by the method. - /// Indicates if the escape sequence is a mouse key. - /// The button state. - /// The position. - /// Indicates if the escape sequence is a response to a request. - /// The handler that will process the event. - public static void DecodeEscSeq (EscSeqReqProc escSeqReqProc, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod, out string c1Control, out string code, out string [] values, out string terminating, out bool isKeyMouse, out List buttonState, out Point pos, out bool isReq, Action continuousButtonPressedHandler) - { - char [] kChars = GetKeyCharArray (cki); - (c1Control, code, values, terminating) = GetEscapeResult (kChars); - isKeyMouse = false; - buttonState = new List () { 0 }; - pos = default; - isReq = false; - switch (c1Control) { - case "ESC": - if (values == null && string.IsNullOrEmpty (terminating)) { - key = ConsoleKey.Escape; - newConsoleKeyInfo = new ConsoleKeyInfo (cki [0].KeyChar, key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - } else if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) { - key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1); - newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar, - key, - false, - true, - true); - } else { - if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) { - key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0]; - } else { - key = (ConsoleKey)cki [1].KeyChar; - } - newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - (ConsoleKey)Math.Min ((uint)key, 255), - false, - true, - false); - } - break; - case "SS3": - key = GetConsoleKey (terminating [0], values [0], ref mod); - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - break; - case "CSI": - if (!string.IsNullOrEmpty (code) && code == "<") { - GetMouse (cki, out buttonState, out pos, continuousButtonPressedHandler); - isKeyMouse = true; - return; - } else if (escSeqReqProc != null && escSeqReqProc.Requested (terminating)) { - isReq = true; - escSeqReqProc.Remove (terminating); - return; - } - key = GetConsoleKey (terminating [0], values [0], ref mod); - if (key != 0 && values.Length > 1) { - mod |= GetConsoleModifiers (values [1]); - } - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - break; - } - } - - /// - /// Gets all the needed information about a escape sequence. - /// - /// The array with all chars. - /// - /// The c1Control returned by , code, values and terminating. - /// - public static (string c1Control, string code, string [] values, string terminating) GetEscapeResult (char [] kChar) - { - if (kChar == null || kChar.Length == 0) { - return (null, null, null, null); - } - if (kChar [0] != '\x1b') { - throw new InvalidOperationException ("Invalid escape character!"); - } - if (kChar.Length == 1) { - return ("ESC", null, null, null); - } - if (kChar.Length == 2) { - return ("ESC", null, null, kChar [1].ToString ()); - } - string c1Control = GetC1ControlChar (kChar [1]); - string code = null; - int nSep = kChar.Count (x => x == ';') + 1; - string [] values = new string [nSep]; - int valueIdx = 0; - string terminating = ""; - for (int i = 2; i < kChar.Length; i++) { - var c = kChar [i]; - if (char.IsDigit (c)) { - values [valueIdx] += c.ToString (); - } else if (c == ';') { - valueIdx++; - } else if (valueIdx == nSep - 1 || i == kChar.Length - 1) { - terminating += c.ToString (); - } else { - code += c.ToString (); - } - } - - return (c1Control, code, values, terminating); - } - - /// - /// Gets the c1Control used in the called escape sequence. - /// - /// The char used. - /// The c1Control. - public static string GetC1ControlChar (char c) - { - // These control characters are used in the vtXXX emulation. - switch (c) { - case 'D': - return "IND"; // Index - case 'E': - return "NEL"; // Next Line - case 'H': - return "HTS"; // Tab Set - case 'M': - return "RI"; // Reverse Index - case 'N': - return "SS2"; // Single Shift Select of G2 Character Set: affects next character only - case 'O': - return "SS3"; // Single Shift Select of G3 Character Set: affects next character only - case 'P': - return "DCS"; // Device Control String - case 'V': - return "SPA"; // Start of Guarded Area - case 'W': - return "EPA"; // End of Guarded Area - case 'X': - return "SOS"; // Start of String - case 'Z': - return "DECID"; // Return Terminal ID Obsolete form of CSI c (DA) - case '[': - return "CSI"; // Control Sequence Introducer - case '\\': - return "ST"; // String Terminator - case ']': - return "OSC"; // Operating System Command - case '^': - return "PM"; // Privacy Message - case '_': - return "APC"; // Application Program Command - default: - return ""; // Not supported - } - } - - /// - /// Gets the from the value. - /// - /// The value. - /// The or zero. - public static ConsoleModifiers GetConsoleModifiers (string value) - { - switch (value) { - case "2": - return ConsoleModifiers.Shift; - case "3": - return ConsoleModifiers.Alt; - case "4": - return ConsoleModifiers.Shift | ConsoleModifiers.Alt; - case "5": - return ConsoleModifiers.Control; - case "6": - return ConsoleModifiers.Shift | ConsoleModifiers.Control; - case "7": - return ConsoleModifiers.Alt | ConsoleModifiers.Control; - case "8": - return ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control; - default: - return 0; - } - } - - /// - /// Gets the depending on terminating and value. - /// - /// The terminating. - /// The value. - /// The which may changes. - /// The and probably the . - public static ConsoleKey GetConsoleKey (char terminating, string value, ref ConsoleModifiers mod) - { - ConsoleKey key; - switch (terminating) { - case 'A': - key = ConsoleKey.UpArrow; - break; - case 'B': - key = ConsoleKey.DownArrow; - break; - case 'C': - key = ConsoleKey.RightArrow; - break; - case 'D': - key = ConsoleKey.LeftArrow; - break; - case 'F': - key = ConsoleKey.End; - break; - case 'H': - key = ConsoleKey.Home; - break; - case 'P': - key = ConsoleKey.F1; - break; - case 'Q': - key = ConsoleKey.F2; - break; - case 'R': - key = ConsoleKey.F3; - break; - case 'S': - key = ConsoleKey.F4; - break; - case 'Z': - key = ConsoleKey.Tab; - mod |= ConsoleModifiers.Shift; - break; - case '~': - switch (value) { - case "2": - key = ConsoleKey.Insert; - break; - case "3": - key = ConsoleKey.Delete; - break; - case "5": - key = ConsoleKey.PageUp; - break; - case "6": - key = ConsoleKey.PageDown; - break; - case "15": - key = ConsoleKey.F5; - break; - case "17": - key = ConsoleKey.F6; - break; - case "18": - key = ConsoleKey.F7; - break; - case "19": - key = ConsoleKey.F8; - break; - case "20": - key = ConsoleKey.F9; - break; - case "21": - key = ConsoleKey.F10; - break; - case "23": - key = ConsoleKey.F11; - break; - case "24": - key = ConsoleKey.F12; - break; - default: - key = 0; - break; - } - break; - default: - key = 0; - break; - } - - return key; - } - - /// - /// A helper to get only the from the array. - /// - /// - /// The char array of the escape sequence. - public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki) - { - char [] kChar = new char [] { }; - var length = 0; - foreach (var kc in cki) { - length++; - Array.Resize (ref kChar, length); - kChar [length - 1] = kc.KeyChar; - } - - return kChar; - } - - private static MouseFlags? lastMouseButtonPressed; - //private static MouseFlags? lastMouseButtonReleased; - private static bool isButtonPressed; - //private static bool isButtonReleased; - private static bool isButtonClicked; - private static bool isButtonDoubleClicked; - private static bool isButtonTripleClicked; - private static Point point; - - /// - /// Gets the mouse button flags and the position. - /// - /// The array. - /// The mouse button flags. - /// The mouse position. - /// The handler that will process the event. - public static void GetMouse (ConsoleKeyInfo [] cki, out List mouseFlags, out Point pos, Action continuousButtonPressedHandler) - { - MouseFlags buttonState = 0; - pos = new Point (); - int buttonCode = 0; - bool foundButtonCode = false; - int foundPoint = 0; - string value = ""; - var kChar = GetKeyCharArray (cki); - //System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}"); - for (int i = 0; i < kChar.Length; i++) { - var c = kChar [i]; - if (c == '<') { - foundButtonCode = true; - } else if (foundButtonCode && c != ';') { - value += c.ToString (); - } else if (c == ';') { - if (foundButtonCode) { - foundButtonCode = false; - buttonCode = int.Parse (value); - } - if (foundPoint == 1) { - pos.X = int.Parse (value) - 1; - } - value = ""; - foundPoint++; - } else if (foundPoint > 0 && c != 'm' && c != 'M') { - value += c.ToString (); - } else if (c == 'm' || c == 'M') { - //pos.Y = int.Parse (value) + Console.WindowTop - 1; - pos.Y = int.Parse (value) - 1; - - switch (buttonCode) { - case 0: - case 8: - case 16: - case 24: - case 32: - case 36: - case 40: - case 48: - case 56: - buttonState = c == 'M' ? MouseFlags.Button1Pressed - : MouseFlags.Button1Released; - break; - case 1: - case 9: - case 17: - case 25: - case 33: - case 37: - case 41: - case 45: - case 49: - case 53: - case 57: - case 61: - buttonState = c == 'M' ? MouseFlags.Button2Pressed - : MouseFlags.Button2Released; - break; - case 2: - case 10: - case 14: - case 18: - case 22: - case 26: - case 30: - case 34: - case 42: - case 46: - case 50: - case 54: - case 58: - case 62: - buttonState = c == 'M' ? MouseFlags.Button3Pressed - : MouseFlags.Button3Released; - break; - case 35: - //// Needed for Windows OS - //if (isButtonPressed && c == 'm' - // && (lastMouseEvent.ButtonState == MouseFlags.Button1Pressed - // || lastMouseEvent.ButtonState == MouseFlags.Button2Pressed - // || lastMouseEvent.ButtonState == MouseFlags.Button3Pressed)) { - - // switch (lastMouseEvent.ButtonState) { - // case MouseFlags.Button1Pressed: - // buttonState = MouseFlags.Button1Released; - // break; - // case MouseFlags.Button2Pressed: - // buttonState = MouseFlags.Button2Released; - // break; - // case MouseFlags.Button3Pressed: - // buttonState = MouseFlags.Button3Released; - // break; - // } - //} else { - // buttonState = MouseFlags.ReportMousePosition; - //} - //break; - case 39: - case 43: - case 47: - case 51: - case 55: - case 59: - case 63: - buttonState = MouseFlags.ReportMousePosition; - break; - case 64: - buttonState = MouseFlags.WheeledUp; - break; - case 65: - buttonState = MouseFlags.WheeledDown; - break; - case 68: - case 72: - case 80: - buttonState = MouseFlags.WheeledLeft; // Shift/Ctrl+WheeledUp - break; - case 69: - case 73: - case 81: - buttonState = MouseFlags.WheeledRight; // Shift/Ctrl+WheeledDown - break; - } - // Modifiers. - switch (buttonCode) { - case 8: - case 9: - case 10: - case 43: - buttonState |= MouseFlags.ButtonAlt; - break; - case 14: - case 47: - buttonState |= MouseFlags.ButtonAlt | MouseFlags.ButtonShift; - break; - case 16: - case 17: - case 18: - case 51: - buttonState |= MouseFlags.ButtonCtrl; - break; - case 22: - case 55: - buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift; - break; - case 24: - case 25: - case 26: - case 59: - buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt; - break; - case 30: - case 63: - buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt; - break; - case 32: - case 33: - case 34: - buttonState |= MouseFlags.ReportMousePosition; - break; - case 36: - case 37: - buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonShift; - break; - case 39: - case 68: - case 69: - buttonState |= MouseFlags.ButtonShift; - break; - case 40: - case 41: - case 42: - buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt; - break; - case 45: - case 46: - buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt | MouseFlags.ButtonShift; - break; - case 48: - case 49: - case 50: - buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl; - break; - case 53: - case 54: - buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift; - break; - case 56: - case 57: - case 58: - buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt; - break; - case 61: - case 62: - buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt; - break; - } - } - } - - mouseFlags = new List () { MouseFlags.AllEvents }; - - if (lastMouseButtonPressed != null && !isButtonPressed && !buttonState.HasFlag (MouseFlags.ReportMousePosition) - && !buttonState.HasFlag (MouseFlags.Button1Released) - && !buttonState.HasFlag (MouseFlags.Button2Released) - && !buttonState.HasFlag (MouseFlags.Button3Released) - && !buttonState.HasFlag (MouseFlags.Button4Released)) { - - lastMouseButtonPressed = null; - isButtonPressed = false; - } - - if (!isButtonClicked && !isButtonDoubleClicked && ((buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || - buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed) && lastMouseButtonPressed == null) || - isButtonPressed && lastMouseButtonPressed != null && buttonState.HasFlag (MouseFlags.ReportMousePosition)) { - - mouseFlags [0] = buttonState; - lastMouseButtonPressed = buttonState; - isButtonPressed = true; - - if ((mouseFlags [0] & MouseFlags.ReportMousePosition) == 0) { - point = new Point () { - X = pos.X, - Y = pos.Y - }; - - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessContinuousButtonPressedAsync (buttonState, continuousButtonPressedHandler)); - return false; - }); - } else if (mouseFlags [0] == MouseFlags.ReportMousePosition) { - isButtonPressed = false; - } - - } else if (isButtonDoubleClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || - buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) { - - mouseFlags [0] = GetButtonTripleClicked (buttonState); - isButtonDoubleClicked = false; - isButtonTripleClicked = true; - - } else if (isButtonClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || - buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) { - - mouseFlags [0] = GetButtonDoubleClicked (buttonState); - isButtonClicked = false; - isButtonDoubleClicked = true; - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); - return false; - }); - - } - //else if (isButtonReleased && !isButtonClicked && buttonState == MouseFlags.ReportMousePosition) { - // mouseFlag [0] = GetButtonClicked ((MouseFlags)lastMouseButtonReleased); - // lastMouseButtonReleased = null; - // isButtonReleased = false; - // isButtonClicked = true; - // Application.MainLoop.AddIdle (() => { - // Task.Run (async () => await ProcessButtonClickedAsync ()); - // return false; - // }); - - //} - else if (!isButtonClicked && !isButtonDoubleClicked && (buttonState == MouseFlags.Button1Released || buttonState == MouseFlags.Button2Released || - buttonState == MouseFlags.Button3Released || buttonState == MouseFlags.Button4Released)) { - - mouseFlags [0] = buttonState; - isButtonPressed = false; - - if (isButtonTripleClicked) { - isButtonTripleClicked = false; - } else if (pos.X == point.X && pos.Y == point.Y) { - mouseFlags.Add (GetButtonClicked (buttonState)); - isButtonClicked = true; - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessButtonClickedAsync ()); - return false; - }); - } - - point = pos; - - //if ((lastMouseButtonPressed & MouseFlags.ReportMousePosition) == 0) { - // lastMouseButtonReleased = buttonState; - // isButtonPressed = false; - // isButtonReleased = true; - //} else { - // lastMouseButtonPressed = null; - // isButtonPressed = false; - //} - - } else if (buttonState == MouseFlags.WheeledUp) { - - mouseFlags [0] = MouseFlags.WheeledUp; - - } else if (buttonState == MouseFlags.WheeledDown) { - - mouseFlags [0] = MouseFlags.WheeledDown; - - } else if (buttonState == MouseFlags.WheeledLeft) { - - mouseFlags [0] = MouseFlags.WheeledLeft; - - } else if (buttonState == MouseFlags.WheeledRight) { - - mouseFlags [0] = MouseFlags.WheeledRight; - - } else if (buttonState == MouseFlags.ReportMousePosition) { - mouseFlags [0] = MouseFlags.ReportMousePosition; - - } else { - mouseFlags [0] = buttonState; - //foreach (var flag in buttonState.GetUniqueFlags()) { - // mouseFlag [0] |= flag; - //} - } - - mouseFlags [0] = SetControlKeyStates (buttonState, mouseFlags [0]); - //buttonState = mouseFlags; - - //System.Diagnostics.Debug.WriteLine ($"buttonState: {buttonState} X: {pos.X} Y: {pos.Y}"); - //foreach (var mf in mouseFlags) { - // System.Diagnostics.Debug.WriteLine ($"mouseFlags: {mf} X: {pos.X} Y: {pos.Y}"); - //} - } - - private static async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag, Action continuousButtonPressedHandler) - { - while (isButtonPressed) { - await Task.Delay (100); - //var me = new MouseEvent () { - // X = point.X, - // Y = point.Y, - // Flags = mouseFlag - //}; - - var view = Application.WantContinuousButtonPressedView; - if (view == null) - break; - if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.Invoke (() => continuousButtonPressedHandler (mouseFlag, point)); - } - } - } - - private static async Task ProcessButtonClickedAsync () - { - await Task.Delay (300); - isButtonClicked = false; - } - - private static async Task ProcessButtonDoubleClickedAsync () - { - await Task.Delay (300); - isButtonDoubleClicked = false; - } - - private static MouseFlags GetButtonClicked (MouseFlags mouseFlag) - { - MouseFlags mf = default; - switch (mouseFlag) { - case MouseFlags.Button1Released: - mf = MouseFlags.Button1Clicked; - break; - - case MouseFlags.Button2Released: - mf = MouseFlags.Button2Clicked; - break; - - case MouseFlags.Button3Released: - mf = MouseFlags.Button3Clicked; - break; - } - return mf; - } - - private static MouseFlags GetButtonDoubleClicked (MouseFlags mouseFlag) - { - MouseFlags mf = default; - switch (mouseFlag) { - case MouseFlags.Button1Pressed: - mf = MouseFlags.Button1DoubleClicked; - break; - - case MouseFlags.Button2Pressed: - mf = MouseFlags.Button2DoubleClicked; - break; - - case MouseFlags.Button3Pressed: - mf = MouseFlags.Button3DoubleClicked; - break; - } - return mf; - } - - private static MouseFlags GetButtonTripleClicked (MouseFlags mouseFlag) - { - MouseFlags mf = default; - switch (mouseFlag) { - case MouseFlags.Button1Pressed: - mf = MouseFlags.Button1TripleClicked; - break; - - case MouseFlags.Button2Pressed: - mf = MouseFlags.Button2TripleClicked; - break; - - case MouseFlags.Button3Pressed: - mf = MouseFlags.Button3TripleClicked; - break; - } - return mf; - } - - private static MouseFlags SetControlKeyStates (MouseFlags buttonState, MouseFlags mouseFlag) - { - if ((buttonState & MouseFlags.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0) - mouseFlag |= MouseFlags.ButtonCtrl; - - if ((buttonState & MouseFlags.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0) - mouseFlag |= MouseFlags.ButtonShift; - - if ((buttonState & MouseFlags.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0) - mouseFlag |= MouseFlags.ButtonAlt; - return mouseFlag; - } - - /// - /// Get the terminal that holds the console driver. - /// - /// The process. - /// If supported the executable console process, null otherwise. - public static Process GetParentProcess (Process process) - { - if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { - return null; - } - - string query = "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = " + process.Id; - using (ManagementObjectSearcher mos = new ManagementObjectSearcher (query)) { - foreach (ManagementObject mo in mos.Get ()) { - if (mo ["ParentProcessId"] != null) { - try { - var id = Convert.ToInt32 (mo ["ParentProcessId"]); - return Process.GetProcessById (id); - } catch { - } - } - } - } - return null; - } - } -} \ No newline at end of file diff --git a/UnitTests/Input/EscSeqReqTests.cs b/UnitTests/Input/EscSeqReqTests.cs index 53032e2667..35a50cc2e9 100644 --- a/UnitTests/Input/EscSeqReqTests.cs +++ b/UnitTests/Input/EscSeqReqTests.cs @@ -10,69 +10,69 @@ public class EscSeqReqTests { [Fact] public void Constructor_Defaults () { - var escSeqReq = new EscSeqReqProc (); - Assert.NotNull (escSeqReq.EscSeqReqStats); - Assert.Empty (escSeqReq.EscSeqReqStats); + var escSeqReq = new EscSeqRequests (); + Assert.NotNull (escSeqReq.Statuses); + Assert.Empty (escSeqReq.Statuses); } [Fact] public void Add_Tests () { - var escSeqReq = new EscSeqReqProc (); + var escSeqReq = new EscSeqRequests (); escSeqReq.Add ("t"); - Assert.Single (escSeqReq.EscSeqReqStats); - Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumRequests); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + Assert.Single (escSeqReq.Statuses); + Assert.Equal ("t", escSeqReq.Statuses [^1].Terminator); + Assert.Equal (1, escSeqReq.Statuses [^1].NumRequests); + Assert.Equal (1, escSeqReq.Statuses [^1].NumOutstanding); escSeqReq.Add ("t", 2); - Assert.Single (escSeqReq.EscSeqReqStats); - Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumRequests); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + Assert.Single (escSeqReq.Statuses); + Assert.Equal ("t", escSeqReq.Statuses [^1].Terminator); + Assert.Equal (1, escSeqReq.Statuses [^1].NumRequests); + Assert.Equal (1, escSeqReq.Statuses [^1].NumOutstanding); - escSeqReq = new EscSeqReqProc (); + escSeqReq = new EscSeqRequests (); escSeqReq.Add ("t", 2); - Assert.Single (escSeqReq.EscSeqReqStats); - Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + Assert.Single (escSeqReq.Statuses); + Assert.Equal ("t", escSeqReq.Statuses [^1].Terminator); + Assert.Equal (2, escSeqReq.Statuses [^1].NumRequests); + Assert.Equal (2, escSeqReq.Statuses [^1].NumOutstanding); escSeqReq.Add ("t", 3); - Assert.Single (escSeqReq.EscSeqReqStats); - Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + Assert.Single (escSeqReq.Statuses); + Assert.Equal ("t", escSeqReq.Statuses [^1].Terminator); + Assert.Equal (2, escSeqReq.Statuses [^1].NumRequests); + Assert.Equal (2, escSeqReq.Statuses [^1].NumOutstanding); } [Fact] public void Remove_Tests () { - var escSeqReq = new EscSeqReqProc (); + var escSeqReq = new EscSeqRequests (); escSeqReq.Add ("t"); escSeqReq.Remove ("t"); - Assert.Empty (escSeqReq.EscSeqReqStats); + Assert.Empty (escSeqReq.Statuses); escSeqReq.Add ("t", 2); escSeqReq.Remove ("t"); - Assert.Single (escSeqReq.EscSeqReqStats); - Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding); + Assert.Single (escSeqReq.Statuses); + Assert.Equal ("t", escSeqReq.Statuses [^1].Terminator); + Assert.Equal (2, escSeqReq.Statuses [^1].NumRequests); + Assert.Equal (1, escSeqReq.Statuses [^1].NumOutstanding); escSeqReq.Remove ("t"); - Assert.Empty (escSeqReq.EscSeqReqStats); + Assert.Empty (escSeqReq.Statuses); } [Fact] public void Requested_Tests () { - var escSeqReq = new EscSeqReqProc (); - Assert.False (escSeqReq.Requested ("t")); + var escSeqReq = new EscSeqRequests (); + Assert.False (escSeqReq.HasResponse ("t")); escSeqReq.Add ("t"); - Assert.False (escSeqReq.Requested ("r")); - Assert.True (escSeqReq.Requested ("t")); + Assert.False (escSeqReq.HasResponse ("r")); + Assert.True (escSeqReq.HasResponse ("t")); } } } diff --git a/UnitTests/Input/EscSeqUtilsTests.cs b/UnitTests/Input/EscSeqUtilsTests.cs index 39c34db622..9afb3d903f 100644 --- a/UnitTests/Input/EscSeqUtilsTests.cs +++ b/UnitTests/Input/EscSeqUtilsTests.cs @@ -10,15 +10,15 @@ public class EscSeqUtilsTests { public void Defaults_Values () { Assert.Equal ('\x1b', EscSeqUtils.KeyEsc); - Assert.Equal ("\x1b[", EscSeqUtils.KeyCSI); + Assert.Equal ("\x1b[", EscSeqUtils.CSI); Assert.Equal ("\x1b[?1003h", EscSeqUtils.CSI_EnableAnyEventMouse); Assert.Equal ("\x1b[?1006h", EscSeqUtils.CSI_EnableSgrExtModeMouse); Assert.Equal ("\x1b[?1015h", EscSeqUtils.CSI_EnableUrxvtExtModeMouse); Assert.Equal ("\x1b[?1003l", EscSeqUtils.CSI_DisableAnyEventMouse); Assert.Equal ("\x1b[?1006l", EscSeqUtils.CSI_DisableSgrExtModeMouse); Assert.Equal ("\x1b[?1015l", EscSeqUtils.CSI_DisableUrxvtExtModeMouse); - Assert.Equal ("\x1b[?1003h\x1b[?1015h\u001b[?1006h", EscSeqUtils.EnableMouseEvents); - Assert.Equal ("\x1b[?1003l\x1b[?1015l\u001b[?1006l", EscSeqUtils.DisableMouseEvents); + Assert.Equal ("\x1b[?1003h\x1b[?1015h\u001b[?1006h", EscSeqUtils.CSI_EnableMouseEvents); + Assert.Equal ("\x1b[?1003l\x1b[?1015l\u001b[?1006l", EscSeqUtils.CSI_DisableMouseEvents); } [Fact] @@ -26,51 +26,51 @@ public void GetConsoleInputKey_ConsoleKeyInfo () { var cki = new ConsoleKeyInfo ('r', 0, false, false, false); var expectedCki = new ConsoleKeyInfo ('r', 0, false, false, false); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('r', 0, true, false, false); expectedCki = new ConsoleKeyInfo ('r', 0, true, false, false); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('r', 0, false, true, false); expectedCki = new ConsoleKeyInfo ('r', 0, false, true, false); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('r', 0, false, false, true); expectedCki = new ConsoleKeyInfo ('r', 0, false, false, true); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('r', 0, true, true, false); expectedCki = new ConsoleKeyInfo ('r', 0, true, true, false); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('r', 0, false, true, true); expectedCki = new ConsoleKeyInfo ('r', 0, false, true, true); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('r', 0, true, true, true); expectedCki = new ConsoleKeyInfo ('r', 0, true, true, true); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('\u0012', 0, false, false, false); expectedCki = new ConsoleKeyInfo ('R', ConsoleKey.R, false, false, true); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('\0', (ConsoleKey)64, false, false, true); expectedCki = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, false, false, true); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('\r', 0, false, false, false); expectedCki = new ConsoleKeyInfo ('\r', ConsoleKey.Enter, false, false, false); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('\u007f', 0, false, false, false); expectedCki = new ConsoleKeyInfo ('\u007f', ConsoleKey.Backspace, false, false, false); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); cki = new ConsoleKeyInfo ('R', 0, false, false, false); expectedCki = new ConsoleKeyInfo ('R', 0, false, false, false); - Assert.Equal (expectedCki, EscSeqUtils.GetConsoleInputKey (cki)); + Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki)); } [Fact] @@ -83,7 +83,7 @@ public void ResizeArray_ConsoleKeyInfo () Assert.Equal (cki, expectedCkInfos [0]); } - private EscSeqReqProc escSeqReqProc; + private EscSeqRequests escSeqReqProc; private ConsoleKeyInfo newConsoleKeyInfo; private ConsoleKey key; private ConsoleKeyInfo [] cki; @@ -617,7 +617,7 @@ public void DecodeEscSeq_Tests () ClearAll (); Assert.Null (escSeqReqProc); - escSeqReqProc = new EscSeqReqProc (); + escSeqReqProc = new EscSeqRequests (); escSeqReqProc.Add ("t"); cki = new ConsoleKeyInfo [] { @@ -633,10 +633,10 @@ public void DecodeEscSeq_Tests () new ConsoleKeyInfo ('t', 0, false, false, false) }; expectedCki = default; - Assert.Single (escSeqReqProc.EscSeqReqStats); - Assert.Equal ("t", escSeqReqProc.EscSeqReqStats [^1].Terminating); + Assert.Single (escSeqReqProc.Statuses); + Assert.Equal ("t", escSeqReqProc.Statuses [^1].Terminator); EscSeqUtils.DecodeEscSeq (escSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); - Assert.Empty (escSeqReqProc.EscSeqReqStats); + Assert.Empty (escSeqReqProc.Statuses); Assert.Equal (expectedCki, newConsoleKeyInfo); Assert.Equal (0, (int)key); Assert.Equal (0, (int)mod); From 4d56839019ef7eac3b56732f3b6fd09e32f2de5a Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 22 May 2023 23:19:34 +0200 Subject: [PATCH 38/99] Added CSI_SetGraphicsRendition --- .../ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs | 30 ++++++++++++------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 2 +- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs index 674412c7b6..a616b912f5 100644 --- a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs @@ -57,6 +57,18 @@ public static class EscSeqUtils { /// public static readonly string CSI_DisableUrxvtExtModeMouse = CSI + "?1015l"; + /// + /// Control sequence for enabling mouse events. + /// + public static string CSI_EnableMouseEvents { get; set; } = + CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse; + + /// + /// Control sequence for disabling mouse events. + /// + public static string CSI_DisableMouseEvents { get; set; } = + CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse; + /// /// ESC [ ? 1047 h - Activate xterm alternative buffer (no backscroll) /// @@ -206,17 +218,15 @@ public enum DECSCUSR_Style { #endregion + #region Colors /// - /// Control sequence for enabling mouse events. - /// - public static string CSI_EnableMouseEvents { get; set; } = - CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse; - - /// - /// Control sequence for disabling mouse events. - /// - public static string CSI_DisableMouseEvents { get; set; } = - CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse; + /// ESC [ (n) m - SGR - Set Graphics Rendition - Set the format of the screen and text as specified by (n) + /// This command is special in that the (n) position can accept between 0 and 16 parameters separated by semicolons. + /// When no parameters are specified, it is treated the same as a single 0 parameter. + /// https://terminalguide.namepad.de/seq/csi_sm/ + /// summary> + public static string CSI_SetGraphicsRendition (params int [] parameters) => $"{CSI}{string.Join (";", parameters)}m"; + #endregion #region Requests /// diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index a15ba0a0f1..697dde5750 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -928,7 +928,7 @@ string GetCsiForAttribute (int attr) if (values.Contains ((attr >> 16) & 0xffff)) { fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff)); } - return $"{EscSeqUtils.CSI}{bg};{fg}m"; + return EscSeqUtils.CSI_SetGraphicsRendition (bg, fg); } int MapColors (ConsoleColor color, bool isForeground = true) From f6d7e626eb4b1fc78af73720d0b35ff2b2ca7f49 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 23 May 2023 00:00:14 +0200 Subject: [PATCH 39/99] NetDriver code cleanup --- .../ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs | 2 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 243 ++++++++---------- 2 files changed, 113 insertions(+), 132 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs index a616b912f5..d105f36085 100644 --- a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs @@ -224,7 +224,7 @@ public enum DECSCUSR_Style { /// This command is special in that the (n) position can accept between 0 and 16 parameters separated by semicolons. /// When no parameters are specified, it is treated the same as a single 0 parameter. /// https://terminalguide.namepad.de/seq/csi_sm/ - /// summary> + /// public static string CSI_SetGraphicsRendition (params int [] parameters) => $"{CSI}{string.Join (";", parameters)}m"; #endregion diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 697dde5750..7f26ce27ad 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -112,7 +112,7 @@ internal class NetEvents { Queue _inputResultQueue = new Queue (); ConsoleDriver _consoleDriver; volatile ConsoleKeyInfo [] _cki = null; - static volatile bool _isEscSeq; + volatile static bool _isEscSeq; int _lastWindowHeight; bool _stopTasks; #if PROCESS_REQUEST @@ -125,7 +125,7 @@ public NetEvents (ConsoleDriver consoleDriver) { _consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver)); Task.Run (ProcessInputResultQueue); - Task.Run (CheckWinChange); + Task.Run (CheckWindowSizeChange); } internal void StopTasks () @@ -162,54 +162,48 @@ void ProcessInputResultQueue () _waitForStart.Reset (); if (_inputResultQueue.Count == 0) { - GetConsoleKey (); + ConsoleKey key = 0; + ConsoleModifiers mod = 0; + ConsoleKeyInfo newConsoleKeyInfo = default; + + while (true) { + ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true); + if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq) + || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) { + if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) { + _cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, + false, false, false), _cki); + } + _isEscSeq = true; + newConsoleKeyInfo = consoleKeyInfo; + _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki); + if (Console.KeyAvailable) continue; + ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; + _isEscSeq = false; + break; + } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) { + ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; + break; + } else { + _inputResultQueue.Enqueue (new InputResult { + EventType = EventType.Key, + ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo) + }); + _isEscSeq = false; + break; + } + } } _inputReady.Set (); } } - void GetConsoleKey () - { - ConsoleKey key = 0; - ConsoleModifiers mod = 0; - ConsoleKeyInfo newConsoleKeyInfo = default; - - while (true) { - ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true); - if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq) - || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) { - if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) { - _cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, - false, false, false), _cki); - } - _isEscSeq = true; - newConsoleKeyInfo = consoleKeyInfo; - _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki); - if (!Console.KeyAvailable) { - ProcessIncomingEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); - _cki = null; - _isEscSeq = false; - break; - } - } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) { - ProcessIncomingEscSeq (ref newConsoleKeyInfo, ref key, _cki, ref mod); - _cki = null; - break; - } else { - _inputResultQueue.Enqueue (new InputResult { - EventType = EventType.Key, - ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo) - }); - _isEscSeq = false; - break; - } - } - } - - void CheckWinChange () + void CheckWindowSizeChange () { - void WaitWinChange () + void RequestWindowSize () { while (true) { // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. @@ -227,7 +221,7 @@ void WaitWinChange () buffHeight = _consoleDriver.Rows; buffWidth = _consoleDriver.Cols; } - if (RequestWindowSize ( + if (EnqueueWindowSizeEvent ( Math.Max (Console.WindowHeight, 0), Math.Max (Console.WindowWidth, 0), buffHeight, @@ -251,44 +245,50 @@ void WaitWinChange () } _winChange.Wait (); _winChange.Reset (); - WaitWinChange (); + RequestWindowSize (); _inputReady.Set (); } } - bool RequestWindowSize (int winHeight, int winWidth, int buffHeight, int buffWidth) + /// + /// Enqueue a window size event if the window size has changed. + /// + /// + /// + /// + /// + /// + bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int buffWidth) { - void EnqueueRequestWindowSize (Size size) - { + if (!_consoleDriver.EnableConsoleScrolling) { + if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) return false; + var w = Math.Max (winWidth, 0); + var h = Math.Max (winHeight, 0); _inputResultQueue.Enqueue (new InputResult () { EventType = EventType.WindowSize, WindowSizeEvent = new WindowSizeEvent () { - Size = size + Size = new Size (w, h) } }); - } - - if (!_consoleDriver.EnableConsoleScrolling) { - if (winWidth != _consoleDriver.Cols || winHeight != _consoleDriver.Rows) { - var w = Math.Max (winWidth, 0); - var h = Math.Max (winHeight, 0); - EnqueueRequestWindowSize (new Size (w, h)); - return true; - } + return true; } else { - if (winWidth != _consoleDriver.Cols || winHeight != _lastWindowHeight - || buffWidth != _consoleDriver.Cols || buffHeight != _consoleDriver.Rows) { - - _lastWindowHeight = Math.Max (winHeight, 0); - EnqueueRequestWindowSize (new Size (winWidth, _lastWindowHeight)); - return true; - } + if (winWidth == _consoleDriver.Cols && + winHeight == _lastWindowHeight && + buffWidth == _consoleDriver.Cols && buffHeight == _consoleDriver.Rows) return false; + _lastWindowHeight = Math.Max (winHeight, 0); + _inputResultQueue.Enqueue (new InputResult () { + EventType = EventType.WindowSize, + WindowSizeEvent = new WindowSizeEvent () { + Size = new Size (winWidth, _lastWindowHeight) + } + }); + return true; } return false; } // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event) - void ProcessIncomingEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) + void ProcessRequestResponse (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) { // isMouse is true if it's CSI<, false otherwise EscSeqUtils.DecodeEscSeq (EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod, @@ -303,7 +303,7 @@ void ProcessIncomingEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey } return; } else if (isReq) { - HandleRequestEvent (c1Control, code, values, terminating); + HandleRequestResponseEvent (c1Control, code, values, terminating); return; } HandleKeyboardEvent (newConsoleKeyInfo); @@ -410,7 +410,7 @@ MouseButtonState MapMouseFlags (MouseFlags mouseFlags) Point _lastCursorPosition; - void HandleRequestEvent (string c1Control, string code, string [] values, string terminating) + void HandleRequestResponseEvent (string c1Control, string code, string [] values, string terminating) { switch (terminating) { // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed. @@ -451,26 +451,26 @@ void HandleRequestEvent (string c1Control, string code, string [] values, string case EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator: switch (values [0]) { case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue: - RequestWindowSize ( + EnqueueWindowSizeEvent ( Math.Max (int.Parse (values [1]), 0), Math.Max (int.Parse (values [2]), 0), Math.Max (int.Parse (values [1]), 0), Math.Max (int.Parse (values [2]), 0)); break; default: - SetRequestedEvent (c1Control, code, values, terminating); + EnqueueRequestResponseEvent (c1Control, code, values, terminating); break; } break; default: - SetRequestedEvent (c1Control, code, values, terminating); + EnqueueRequestResponseEvent (c1Control, code, values, terminating); break; } _inputReady.Set (); } - void SetRequestedEvent (string c1Control, string code, string [] values, string terminating) + void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating) { EventType eventType = EventType.RequestResponse; var requestRespEv = new RequestResponseEvent () { @@ -839,23 +839,23 @@ public override void Refresh () public override void UpdateScreen () { - if (winChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols * 3 + if (_winSizeChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols * 3 || (!EnableConsoleScrolling && Rows != Console.WindowHeight) || (EnableConsoleScrolling && Rows != _largestBufferHeight)) { return; } - int top = 0; - int left = 0; - int rows = Rows; - int cols = Cols; + var top = 0; + var left = 0; + var rows = Rows; + var cols = Cols; System.Text.StringBuilder output = new System.Text.StringBuilder (); - int redrawAttr = -1; + var redrawAttr = -1; var lastCol = -1; Console.CursorVisible = false; - for (int row = top; row < rows; row++) { + for (var row = top; row < rows; row++) { if (Console.WindowHeight < 1) { return; } @@ -867,7 +867,7 @@ public override void UpdateScreen () } _dirtyLine [row] = false; output.Clear (); - for (int col = left; col < cols; col++) { + for (var col = left; col < cols; col++) { lastCol = -1; var outputWidth = 0; for (; col < cols; col++) { @@ -914,6 +914,7 @@ public override void UpdateScreen () } // TODO: Move this to EscSeqUtils in such a way that it remains platform agnostic. + // Also, this code is inefficient (multiple calls to Contains). string GetCsiForAttribute (int attr) { int bg = 0; @@ -1008,7 +1009,7 @@ private bool EnsureBufferSize () return true; } - private CursorVisibility? savedCursorVisibility; + CursorVisibility? _cachedCursorVisibility; public override void UpdateCursor () { @@ -1022,13 +1023,13 @@ public override void UpdateCursor () public override bool GetCursorVisibility (out CursorVisibility visibility) { - visibility = savedCursorVisibility ?? CursorVisibility.Default; + visibility = _cachedCursorVisibility ?? CursorVisibility.Default; return visibility == CursorVisibility.Default; } public override bool SetCursorVisibility (CursorVisibility visibility) { - savedCursorVisibility = visibility; + _cachedCursorVisibility = visibility; return Console.CursorVisible = visibility == CursorVisibility.Default; } @@ -1036,13 +1037,13 @@ public override bool EnsureCursorVisibility () { if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { GetCursorVisibility (out CursorVisibility cursorVisibility); - savedCursorVisibility = cursorVisibility; + _cachedCursorVisibility = cursorVisibility; SetCursorVisibility (CursorVisibility.Invisible); return false; } - SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default); - return savedCursorVisibility == CursorVisibility.Default; + SetCursorVisibility (_cachedCursorVisibility ?? CursorVisibility.Default); + return _cachedCursorVisibility == CursorVisibility.Default; } public void StartReportingMouseMoves () @@ -1062,7 +1063,7 @@ public override void Suspend () } #endregion - public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) + ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) { if (consoleKeyInfo.Key != ConsoleKey.Packet) { return consoleKeyInfo; @@ -1160,7 +1161,7 @@ Key MapKey (ConsoleKeyInfo keyInfo) } return (Key)((uint)keyInfo.KeyChar); } - if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { + if (key is >= ConsoleKey.F1 and <= ConsoleKey.F12) { var delta = key - ConsoleKey.F1; if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); @@ -1212,32 +1213,15 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle var mLoop = _mainLoop = mainLoop.Driver as NetMainLoop; - // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. + // Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. mLoop.ProcessInput = (e) => ProcessInput (e); - // Check if terminal supports requests - //143 63 c * DA - Device Attributes - //* [c = Terminal will identify itself - //* [?1; 2c = Terminal is saying it is a VT100 with AVO - //* [> 0c = Secondary DA request (distinguishes VT240 from VT220) - - //CSI Ps c - Send Device Attributes (Primary DA). - // Ps = 0 or omitted ⇒ request attributes from terminal.The - // response depends on the decTerminalID resource setting. - // ⇒ CSI ? 1 ; 2 c ("VT100 with Advanced Video Option") - // ⇒ CSI ? 1 ; 0 c ("VT101 with No Options") - // ⇒ CSI ? 4 ; 6 c ("VT132 with Advanced Video and Graphics") - // ⇒ CSI ? 6 c ("VT102") - // ⇒ CSI ? 7 c ("VT131") - // ⇒ CSI ? 1 2; Ps c ("VT125") - // ⇒ CSI ? 6 2; Ps c ("VT220") - // ⇒ CSI ? 6 3; Ps c ("VT320") - // ⇒ CSI ? 6 4; Ps c ("VT420") - // Windows: ESC [ 0 c DA Device Attributes Report the terminal identity. Will emit “\x1b[?1;0c”, indicating "VT101 with No Options". _mainLoop._netEvents.EscSeqRequests.Add (EscSeqUtils.CSI_ReportDeviceAttributes_Terminator); Console.Out.Write (EscSeqUtils.CSI_ReportDeviceAttributes); } + volatile bool _winSizeChanging; + void ProcessInput (NetEvents.InputResult inputEvent) { switch (inputEvent.EventType) { @@ -1264,36 +1248,33 @@ void ProcessInput (NetEvents.InputResult inputEvent) _mouseHandler (ToDriverMouse (inputEvent.MouseEvent)); break; case NetEvents.EventType.WindowSize: - ChangeWin (inputEvent.WindowSizeEvent.Size); + _winSizeChanging = true; + if (!EnableConsoleScrolling) { + _largestBufferHeight = Math.Max (inputEvent.WindowSizeEvent.Size.Height, 0); + } else { + _largestBufferHeight = Math.Max (inputEvent.WindowSizeEvent.Size.Height, _largestBufferHeight); + } + Top = 0; + Left = 0; + Cols = inputEvent.WindowSizeEvent.Size.Width; + Rows = _largestBufferHeight; + ResizeScreen (); + UpdateOffScreen (); + _winSizeChanging = false; + TerminalResized?.Invoke (); break; case NetEvents.EventType.RequestResponse: // BUGBUG: What is this for? It does not seem to be used anywhere. // It is also not clear what it does. View.Data is documented as "This property is not used internally" Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; break; + case NetEvents.EventType.WindowPosition: + break; + default: + throw new ArgumentOutOfRangeException (); } } - volatile bool winChanging; - - void ChangeWin (Size size) - { - winChanging = true; - if (!EnableConsoleScrolling) { - _largestBufferHeight = Math.Max (size.Height, 0); - } else { - _largestBufferHeight = Math.Max (size.Height, _largestBufferHeight); - } - Top = 0; - Left = 0; - Cols = size.Width; - Rows = _largestBufferHeight; - ResizeScreen (); - UpdateOffScreen (); - winChanging = false; - TerminalResized?.Invoke (); - } - MouseEvent ToDriverMouse (NetEvents.MouseEvent me) { //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}"); @@ -1407,7 +1388,7 @@ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool al public override bool GetColors (int value, out Color foreground, out Color background) { - bool hasColor = false; + var hasColor = false; foreground = default; background = default; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) @@ -1521,7 +1502,7 @@ bool IMainLoopDriver.EventsPending (bool wait) bool CheckTimers (bool wait, out int waitTimeout) { - long now = DateTime.UtcNow.Ticks; + var now = DateTime.UtcNow.Ticks; if (_mainLoop.timeouts.Count > 0) { waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); From d5ef4192e0f7e97e042aa008ed6859a20f12c417 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 23 May 2023 08:35:27 +0200 Subject: [PATCH 40/99] code cleanup --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 6f27328284..e9a238a3fa 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -852,8 +852,8 @@ public override void UpdateScreen () var redrawAttr = -1; var lastCol = -1; - GetCursorVisibility (out CursorVisibility savedVisibitity); - SetCursorVisibility (CursorVisibility.Invisible); + //GetCursorVisibility (out CursorVisibility savedVisibitity); + //SetCursorVisibility (CursorVisibility.Invisible); for (var row = top; row < rows; row++) { if (Console.WindowHeight < 1) { @@ -911,7 +911,7 @@ public override void UpdateScreen () } } SetCursorPosition (0, 0); - SetCursorVisibility (savedVisibitity); + //SetCursorVisibility (savedVisibitity); } // TODO: Move this to EscSeqUtils in such a way that it remains platform agnostic. From bca2f974bc12eb2413a5a83b9b9f8cb15b7b956f Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 23 May 2023 13:35:14 +0200 Subject: [PATCH 41/99] Cleaned up color handling in NetDriver --- .../CursesDriver/CursesDriver.cs | 23 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 221 +++++++++--------- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 20 +- 3 files changed, 120 insertions(+), 144 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 27ff5ebd05..8f18827e03 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -846,24 +846,13 @@ public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, public override bool GetColors (int value, out Color foreground, out Color background) { - bool hasColor = false; - foreground = default; - background = default; - int back = -1; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains ((value >> 12) & 0xffff)) { - hasColor = true; - back = (value >> 12) & 0xffff; - background = MapCursesColor (back); - } - if (values.Contains ((value - (back << 12)) >> 8)) { - hasColor = true; - foreground = MapCursesColor ((value - (back << 12)) >> 8); - } - return hasColor; + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = MapCursesColor ((value >> 4) & 0xF); + background = MapCursesColor (value & 0xF); + + return true; } + } internal static class Platform { diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index e9a238a3fa..c5df2705ae 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -683,21 +683,6 @@ public override void End () Console.Out.Close (); } - public override Attribute MakeColor (Color foreground, Color background) - { - return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); - } - - static Attribute MakeColor (ConsoleColor f, ConsoleColor b) - { - // Encode the colors into the int value. - return new Attribute ( - value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff), - foreground: (Color)f, - background: (Color)b - ); - } - public override void Init (Action terminalResized) { var p = Environment.OSVersion.Platform; @@ -893,7 +878,8 @@ public override void UpdateScreen () var attr = Contents [row, col, 1]; if (attr != redrawAttr) { redrawAttr = attr; - output.Append (GetCsiForAttribute (attr)); + GetColors (attr, out var fgColor, out var bgColor); + output.Append (EscSeqUtils.CSI_SetGraphicsRendition (MapColors ((ConsoleColor)bgColor, false), MapColors ((ConsoleColor)fgColor, true))); } outputWidth++; var rune = (Rune)Contents [row, col, 0]; @@ -914,48 +900,72 @@ public override void UpdateScreen () //SetCursorVisibility (savedVisibitity); } - // TODO: Move this to EscSeqUtils in such a way that it remains platform agnostic. - // Also, this code is inefficient (multiple calls to Contains). - string GetCsiForAttribute (int attr) + + #region Color Handling + + // Cache the list of ConsoleColor values. + private static HashSet ConsoleColorValues = new HashSet ( + Enum.GetValues (typeof (ConsoleColor)).OfType ().Select (c => (int)c) + ); + + // Dictionary for mapping ConsoleColor values to the values used by System.Net.Console. + private static Dictionary colorMap = new Dictionary { + { ConsoleColor.Black, COLOR_BLACK }, + { ConsoleColor.DarkBlue, COLOR_BLUE }, + { ConsoleColor.DarkGreen, COLOR_GREEN }, + { ConsoleColor.DarkCyan, COLOR_CYAN }, + { ConsoleColor.DarkRed, COLOR_RED }, + { ConsoleColor.DarkMagenta, COLOR_MAGENTA }, + { ConsoleColor.DarkYellow, COLOR_YELLOW }, + { ConsoleColor.Gray, COLOR_WHITE }, + { ConsoleColor.DarkGray, COLOR_BRIGHT_BLACK }, + { ConsoleColor.Blue, COLOR_BRIGHT_BLUE }, + { ConsoleColor.Green, COLOR_BRIGHT_GREEN }, + { ConsoleColor.Cyan, COLOR_BRIGHT_CYAN }, + { ConsoleColor.Red, COLOR_BRIGHT_RED }, + { ConsoleColor.Magenta, COLOR_BRIGHT_MAGENTA }, + { ConsoleColor.Yellow, COLOR_BRIGHT_YELLOW }, + { ConsoleColor.White, COLOR_BRIGHT_WHITE } + }; + + // Map a ConsoleColor to a platform dependent value. + int MapColors (ConsoleColor color, bool isForeground = true) { - int bg = 0; - int fg = 0; + return colorMap.TryGetValue (color, out var colorValue) ? colorValue + (isForeground ? 0 : 10) : 0; + } - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (attr & 0xffff)) { - bg = MapColors ((ConsoleColor)(attr & 0xffff), false); - } - if (values.Contains ((attr >> 16) & 0xffff)) { - fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff)); - } - return EscSeqUtils.CSI_SetGraphicsRendition (bg, fg); + /// + /// In the NetDriver, colors are encoded as an int. + /// Extracts the foreground and background colors from the encoded value. + /// Assumes a 4-bit encoded value for both foreground and background colors. + /// + public override bool GetColors (int value, out Color foreground, out Color background) + { + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = (Color)((value >> 16) & 0xF); + background = (Color)(value & 0xF); + + return true; } - int MapColors (ConsoleColor color, bool isForeground = true) + /// + /// In the NetDriver, colors are encoded as an int. + /// However, the foreground color is stored in the most significant 16 bits, + /// and the background color is stored in the least significant 16 bits. + /// + public override Attribute MakeColor (Color foreground, Color background) { - return color switch { - ConsoleColor.Black => isForeground ? COLOR_BLACK : COLOR_BLACK + 10, - ConsoleColor.DarkBlue => isForeground ? COLOR_BLUE : COLOR_BLUE + 10, - ConsoleColor.DarkGreen => isForeground ? COLOR_GREEN : COLOR_GREEN + 10, - ConsoleColor.DarkCyan => isForeground ? COLOR_CYAN : COLOR_CYAN + 10, - ConsoleColor.DarkRed => isForeground ? COLOR_RED : COLOR_RED + 10, - ConsoleColor.DarkMagenta => isForeground ? COLOR_MAGENTA : COLOR_MAGENTA + 10, - ConsoleColor.DarkYellow => isForeground ? COLOR_YELLOW : COLOR_YELLOW + 10, - ConsoleColor.Gray => isForeground ? COLOR_WHITE : COLOR_WHITE + 10, - ConsoleColor.DarkGray => isForeground ? COLOR_BRIGHT_BLACK : COLOR_BRIGHT_BLACK + 10, - ConsoleColor.Blue => isForeground ? COLOR_BRIGHT_BLUE : COLOR_BRIGHT_BLUE + 10, - ConsoleColor.Green => isForeground ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_GREEN + 10, - ConsoleColor.Cyan => isForeground ? COLOR_BRIGHT_CYAN : COLOR_BRIGHT_CYAN + 10, - ConsoleColor.Red => isForeground ? COLOR_BRIGHT_RED : COLOR_BRIGHT_RED + 10, - ConsoleColor.Magenta => isForeground ? COLOR_BRIGHT_MAGENTA : COLOR_BRIGHT_MAGENTA + 10, - ConsoleColor.Yellow => isForeground ? COLOR_BRIGHT_YELLOW : COLOR_BRIGHT_YELLOW + 10, - ConsoleColor.White => isForeground ? COLOR_BRIGHT_WHITE : COLOR_BRIGHT_WHITE + 10, - var _ => 0 - }; + // Encode the colors into the int value. + return new Attribute ( + value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), + foreground: foreground, + background: background + ); } + #endregion + + #region Cursor Handling bool SetCursorPosition (int col, int row) { if (IsWinPlatform) { @@ -973,43 +983,6 @@ bool SetCursorPosition (int col, int row) } } - private void SetWindowPosition (int col, int row) - { - if (IsWinPlatform && EnableConsoleScrolling) { - var winTop = Math.Max (Rows - Console.WindowHeight - row, 0); - winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1); - winTop = Math.Max (winTop, 0); - if (winTop != Console.WindowTop) { - try { - if (!EnsureBufferSize ()) { - return; - } -#pragma warning disable CA1416 - Console.SetWindowPosition (col, winTop); -#pragma warning restore CA1416 - } catch (System.IO.IOException) { - - } catch (System.ArgumentOutOfRangeException) { } - } - } - Top = Console.WindowTop; - Left = Console.WindowLeft; - } - - private bool EnsureBufferSize () - { -#pragma warning disable CA1416 - if (IsWinPlatform && Console.BufferHeight < Rows) { - try { - Console.SetBufferSize (Console.WindowWidth, Rows); - } catch (Exception) { - return false; - } - } -#pragma warning restore CA1416 - return true; - } - CursorVisibility? _cachedCursorVisibility; public override void UpdateCursor () @@ -1048,6 +1021,48 @@ public override bool EnsureCursorVisibility () SetCursorVisibility (_cachedCursorVisibility ?? CursorVisibility.Default); return _cachedCursorVisibility == CursorVisibility.Default; } + #endregion + + #region Size and Position Handling + + void SetWindowPosition (int col, int row) + { + if (IsWinPlatform && EnableConsoleScrolling) { + var winTop = Math.Max (Rows - Console.WindowHeight - row, 0); + winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1); + winTop = Math.Max (winTop, 0); + if (winTop != Console.WindowTop) { + try { + if (!EnsureBufferSize ()) { + return; + } +#pragma warning disable CA1416 + Console.SetWindowPosition (col, winTop); +#pragma warning restore CA1416 + } catch (System.IO.IOException) { + + } catch (System.ArgumentOutOfRangeException) { } + } + } + Top = Console.WindowTop; + Left = Console.WindowLeft; + } + + private bool EnsureBufferSize () + { +#pragma warning disable CA1416 + if (IsWinPlatform && Console.BufferHeight < Rows) { + try { + Console.SetBufferSize (Console.WindowWidth, Rows); + } catch (Exception) { + return false; + } + } +#pragma warning restore CA1416 + return true; + } + #endregion + public void StartReportingMouseMoves () { @@ -1059,13 +1074,6 @@ public void StopReportingMouseMoves () Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents); } - #region Not Implemented - public override void Suspend () - { - throw new NotImplementedException (); - } - #endregion - ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) { if (consoleKeyInfo.Key != ConsoleKey.Packet) { @@ -1376,7 +1384,6 @@ MouseEvent ToDriverMouse (NetEvents.MouseEvent me) }; } - public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) { NetEvents.InputResult input = new NetEvents.InputResult { @@ -1389,24 +1396,14 @@ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool al } catch (OverflowException) { } } - public override bool GetColors (int value, out Color foreground, out Color background) + + #region Not Implemented + public override void Suspend () { - var hasColor = false; - foreground = default; - background = default; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (value & 0xffff)) { - hasColor = true; - background = (Color)(ConsoleColor)(value & 0xffff); - } - if (values.Contains ((value >> 16) & 0xffff)) { - hasColor = true; - foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff); - } - return hasColor; + throw new NotImplementedException (); } + #endregion + } /// diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 6e18af99e4..917f2b41df 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1504,21 +1504,11 @@ Attribute MakeColor (ConsoleColor f, ConsoleColor b) public override bool GetColors (int value, out Color foreground, out Color background) { - bool hasColor = false; - foreground = default; - background = default; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains ((value >> 4) & 0xffff)) { - hasColor = true; - background = (Color)(ConsoleColor)((value >> 4) & 0xffff); - } - if (values.Contains (value - ((int)background << 4))) { - hasColor = true; - foreground = (Color)(ConsoleColor)(value - ((int)background << 4)); - } - return hasColor; + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = (Color)((value >> 16) & 0xF); + background = (Color)(value & 0xF); + + return true; } From 0a7f9eac43dd3f5ce685af71010dbd5ae404ba51 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 09:09:40 +0200 Subject: [PATCH 42/99] refixed tabview unit test --- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 107 ++++++++---------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 9 +- UnitTests/Views/TabViewTests.cs | 2 + 3 files changed, 52 insertions(+), 66 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 8cf8e5055d..7aaf2742e6 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -44,24 +44,6 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag bool [] _dirtyLine; - //void UpdateOffscreen () - //{ - // int cols = Cols; - // int rows = Rows; - - // contents = new int [rows, cols, 3]; - // for (int r = 0; r < rows; r++) { - // for (int c = 0; c < cols; c++) { - // contents [r, c, 0] = ' '; - // contents [r, c, 1] = MakeColor (ConsoleColor.Gray, ConsoleColor.Black); - // contents [r, c, 2] = 0; - // } - // } - // dirtyLine = new bool [rows]; - // for (int row = 0; row < rows; row++) - // dirtyLine [row] = true; - //} - public FakeDriver () { if (FakeBehaviors.UseFakeClipboard) { @@ -141,14 +123,6 @@ public override void End () FakeConsole.Clear (); } - public override Attribute MakeColor (Color foreground, Color background) - { - return new Attribute ( - value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), - foreground: (Color)foreground, - background: (Color)background - ); - } public override void Init (Action terminalResized) { @@ -166,20 +140,6 @@ public override void Init (Action terminalResized) UpdateOffScreen (); } - int _redrawColor = -1; - void SetColor (int color) - { - _redrawColor = color; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (color & 0xffff)) { - FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff); - } - if (values.Contains ((color >> 16) & 0xffff)) { - FakeConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff); - } - } public override void UpdateScreen () { @@ -206,9 +166,9 @@ public override void UpdateScreen () } var color = Contents [row, col, 1]; - if (color != _redrawColor) { - SetColor (color); - } + // NOTE: In real drivers setting the color can be a performance hit, so we only do it when needed. + // in fakedriver we don't care about perf. + SetColor (color); var rune = (Rune)Contents [row, col, 0]; if (rune.Utf16SequenceLength == 1) { @@ -238,6 +198,48 @@ public override void Refresh () UpdateCursor (); } + + public override Attribute MakeColor (Color foreground, Color background) + { + return new Attribute ( + value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), + foreground: (Color)foreground, + background: (Color)background + ); + } + + void SetColor (int color) + { + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains (color & 0xffff)) { + FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff); + } + if (values.Contains ((color >> 16) & 0xffff)) { + FakeConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff); + } + } + + public override bool GetColors (int value, out Color foreground, out Color background) + { + bool hasColor = false; + foreground = default; + background = default; + IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) + .OfType () + .Select (s => (int)s); + if (values.Contains (value & 0xffff)) { + hasColor = true; + background = (Color)(ConsoleColor)(value & 0xffff); + } + if (values.Contains ((value >> 16) & 0xffff)) { + hasColor = true; + foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff); + } + return hasColor; + } + public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) { if (consoleKeyInfo.Key != ConsoleKey.Packet) { @@ -556,25 +558,6 @@ public override void UpdateOffScreen () Clip = new Rect (0, 0, Cols, Rows); } - public override bool GetColors (int value, out Color foreground, out Color background) - { - bool hasColor = false; - foreground = default; - background = default; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (value & 0xffff)) { - hasColor = true; - background = (Color)(ConsoleColor)(value & 0xffff); - } - if (values.Contains ((value >> 16) & 0xffff)) { - hasColor = true; - foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff); - } - return hasColor; - } - public override void UpdateCursor () { if (!EnsureCursorVisibility ()) { diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index c5df2705ae..a949c7b34c 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -876,6 +876,7 @@ public override void UpdateScreen () } var attr = Contents [row, col, 1]; + // Performance: Only send the escape sequence if the attribute has changed. if (attr != redrawAttr) { redrawAttr = attr; GetColors (attr, out var fgColor, out var bgColor); @@ -934,11 +935,11 @@ int MapColors (ConsoleColor color, bool isForeground = true) return colorMap.TryGetValue (color, out var colorValue) ? colorValue + (isForeground ? 0 : 10) : 0; } - /// + /// /// In the NetDriver, colors are encoded as an int. /// Extracts the foreground and background colors from the encoded value. /// Assumes a 4-bit encoded value for both foreground and background colors. - /// + /// public override bool GetColors (int value, out Color foreground, out Color background) { // Assume a 4-bit encoded value for both foreground and background colors. @@ -948,11 +949,11 @@ public override bool GetColors (int value, out Color foreground, out Color backg return true; } - /// + /// /// In the NetDriver, colors are encoded as an int. /// However, the foreground color is stored in the most significant 16 bits, /// and the background color is stored in the least significant 16 bits. - /// + /// public override Attribute MakeColor (Color foreground, Color background) { // Encode the colors into the int value. diff --git a/UnitTests/Views/TabViewTests.cs b/UnitTests/Views/TabViewTests.cs index af64a83229..571a90d6ad 100644 --- a/UnitTests/Views/TabViewTests.cs +++ b/UnitTests/Views/TabViewTests.cs @@ -30,6 +30,8 @@ private TabView GetTabView (out Tab tab1, out Tab tab2, bool initFakeDriver = tr } var tv = new TabView (); + tv.BeginInit (); + tv.EndInit (); tv.ColorScheme = new ColorScheme (); tv.AddTab (tab1 = new Tab ("Tab1", new TextField ("hi")), false); tv.AddTab (tab2 = new Tab ("Tab2", new Label ("hi2")), false); From 5aeeef75572ec5e26c82cd7af424ef9707fa031b Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 09:47:26 +0200 Subject: [PATCH 43/99] WindowsDriver color code cleanup --- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 63 ++++++++++--------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 3 +- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 59 +++++++++-------- 3 files changed, 70 insertions(+), 55 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 7aaf2742e6..82f75c3b5b 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -13,6 +13,7 @@ // Alias Console to MockConsole so we don't accidentally use Console using Console = Terminal.Gui.FakeConsole; using Unix.Terminal; +using static Terminal.Gui.WindowsConsole; namespace Terminal.Gui; /// @@ -198,48 +199,54 @@ public override void Refresh () UpdateCursor (); } + #region Color Handling - public override Attribute MakeColor (Color foreground, Color background) - { - return new Attribute ( - value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), - foreground: (Color)foreground, - background: (Color)background - ); - } + // Cache the list of ConsoleColor values. + private static readonly HashSet ConsoleColorValues = new HashSet ( + Enum.GetValues (typeof (ConsoleColor)).OfType ().Select (c => (int)c) + ); void SetColor (int color) { - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (color & 0xffff)) { + if (ConsoleColorValues.Contains (color & 0xffff)) { FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff); } - if (values.Contains ((color >> 16) & 0xffff)) { + if (ConsoleColorValues.Contains ((color >> 16) & 0xffff)) { FakeConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff); } } + /// + /// In the FakeDriver, colors are encoded as an int; same as NetDriver + /// Extracts the foreground and background colors from the encoded value. + /// Assumes a 4-bit encoded value for both foreground and background colors. + /// public override bool GetColors (int value, out Color foreground, out Color background) { - bool hasColor = false; - foreground = default; - background = default; - IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); - if (values.Contains (value & 0xffff)) { - hasColor = true; - background = (Color)(ConsoleColor)(value & 0xffff); - } - if (values.Contains ((value >> 16) & 0xffff)) { - hasColor = true; - foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff); - } - return hasColor; + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = (Color)((value >> 16) & 0xF); + background = (Color)(value & 0xF); + + return true; } + /// + /// In the FakeDriver, colors are encoded as an int; same as NetDriver + /// However, the foreground color is stored in the most significant 16 bits, + /// and the background color is stored in the least significant 16 bits. + /// + public override Attribute MakeColor (Color foreground, Color background) + { + // Encode the colors into the int value. + return new Attribute ( + value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), + foreground: foreground, + background: background + ); + } + + #endregion + public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) { if (consoleKeyInfo.Key != ConsoleKey.Packet) { diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index a949c7b34c..92308e5a31 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -901,11 +901,10 @@ public override void UpdateScreen () //SetCursorVisibility (savedVisibitity); } - #region Color Handling // Cache the list of ConsoleColor values. - private static HashSet ConsoleColorValues = new HashSet ( + private static readonly HashSet ConsoleColorValues = new HashSet ( Enum.GetValues (typeof (ConsoleColor)).OfType ().Select (c => (int)c) ); diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 917f2b41df..f48e96174a 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1487,31 +1487,6 @@ public override void AddRune (Rune systemRune) } - public override Attribute MakeColor (Color foreground, Color background) - { - return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); - } - - Attribute MakeColor (ConsoleColor f, ConsoleColor b) - { - // Encode the colors into the int value. - return new Attribute ( - value: ((int)f | (int)b << 4), - foreground: (Color)f, - background: (Color)b - ); - } - - public override bool GetColors (int value, out Color foreground, out Color background) - { - // Assume a 4-bit encoded value for both foreground and background colors. - foreground = (Color)((value >> 16) & 0xF); - background = (Color)(value & 0xF); - - return true; - } - - public override void Init (Action terminalResized) { TerminalResized = terminalResized; @@ -1628,6 +1603,40 @@ public override void Refresh () UpdateCursor (); } + #region Color Handling + + /// + /// In the WindowsDriver, colors are encoded as an int. + /// The background color is stored in the least significant 4 bits, + /// and the foreground color is stored in the next 4 bits. + /// + public override Attribute MakeColor (Color foreground, Color background) + { + // Encode the colors into the int value. + return new Attribute ( + value: (((int)foreground) | ((int)background << 4)), + foreground: foreground, + background: background + ); + } + + /// + /// Extracts the foreground and background colors from the encoded value. + /// Assumes a 4-bit encoded value for both foreground and background colors. + /// + public override bool GetColors (int value, out Color foreground, out Color background) + { + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = (Color)((value >> 16) & 0xF); + background = (Color)(value & 0xF); + + //foreground = (Color)(value & 0xF); + //background = (Color)((value >> 4) & 0xF); + return true; + } + + #endregion + CursorVisibility savedCursorVisibility; public override void UpdateCursor () From cdfc7f541997484ff89a8749be1970aafa02e657 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 09:47:46 +0200 Subject: [PATCH 44/99] WindowsDriver color code cleanup --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index f48e96174a..9925d47847 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1629,9 +1629,6 @@ public override bool GetColors (int value, out Color foreground, out Color backg // Assume a 4-bit encoded value for both foreground and background colors. foreground = (Color)((value >> 16) & 0xF); background = (Color)(value & 0xF); - - //foreground = (Color)(value & 0xF); - //background = (Color)((value >> 4) & 0xF); return true; } From 80007053ddb963cb262cbff91123e06c4ff45dae Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 10:31:36 +0200 Subject: [PATCH 45/99] CursesDriver color code cleanup --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 5 + .../CursesDriver/CursesDriver.cs | 264 ++++++++---------- .../ConsoleDrivers/CursesDriver/binding.cs | 28 ++ 3 files changed, 155 insertions(+), 142 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index b51a98e766..468fd81687 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -248,6 +248,8 @@ public Rect Clip { /// public abstract void UpdateScreen (); + #region Color Handling + Attribute _currentAttribute; /// @@ -325,6 +327,9 @@ public void InitializeColorSchemes () s.Value.Initialize (); } } + + #endregion + /// /// Enables diagnostic functions /// diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 8f18827e03..8159589f90 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -177,45 +177,146 @@ private void ProcessWinChange () } } - public override void UpdateCursor () => Refresh (); - - public override void End () - { - StopReportingMouseMoves (); - SetCursorVisibility (CursorVisibility.Default); - - Curses.endwin (); - } - - public override void UpdateScreen () => _window.redrawwin (); - - public Curses.Window _window; + #region Color Handling /// - /// Creates a curses color from the provided foreground and background colors + /// Creates an Attribute from the provided curses-based foreground and background color numbers /// - /// Contains the curses attributes for the foreground (color, plus any attributes) - /// Contains the curses attributes for the background (color, plus any attributes) + /// Contains the curses color number for the foreground (color, plus any attributes) + /// Contains the curses color number for the background (color, plus any attributes) /// static Attribute MakeColor (short foreground, short background) { var v = (short)((int)foreground | background << 4); - //Curses.InitColorPair (++last_color_pair, foreground, background); + // TODO: for TrueColor - Use InitExtendedPair Curses.InitColorPair (v, foreground, background); return new Attribute ( - //value: Curses.ColorPair (last_color_pair), value: Curses.ColorPair (v), - //foreground: (Color)foreground, foreground: MapCursesColor (foreground), - //background: (Color)background); background: MapCursesColor (background)); } + /// + /// In the CursesDriver, colors are encoded as an int. + /// The foreground color is stored in the most significant 4 bits, + /// and the background color is stored in the least significant 4 bits. + /// The Terminal.GUi Color values are converted to curses color encoding before being encoded. + /// public override Attribute MakeColor (Color fore, Color back) { - return MakeColor ((short)MapColor (fore), (short)MapColor (back)); + return MakeColor ((short)(ColorToCursesColorNumber (fore) & 0xffff), (short)ColorToCursesColorNumber (back)); + } + + static int ColorToCursesColorNumber (Color color) + { + switch (color) { + case Color.Black: + return Curses.COLOR_BLACK; + case Color.Blue: + return Curses.COLOR_BLUE; + case Color.Green: + return Curses.COLOR_GREEN; + case Color.Cyan: + return Curses.COLOR_CYAN; + case Color.Red: + return Curses.COLOR_RED; + case Color.Magenta: + return Curses.COLOR_MAGENTA; + case Color.Brown: + return Curses.COLOR_YELLOW; + case Color.Gray: + return Curses.COLOR_WHITE; + case Color.DarkGray: + //return Curses.COLOR_BLACK | Curses.A_BOLD; + return Curses.COLOR_GRAY; + case Color.BrightBlue: + return Curses.COLOR_BLUE | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightGreen: + return Curses.COLOR_GREEN | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightCyan: + return Curses.COLOR_CYAN | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightRed: + return Curses.COLOR_RED | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightMagenta: + return Curses.COLOR_MAGENTA | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.BrightYellow: + return Curses.COLOR_YELLOW | Curses.A_BOLD | Curses.COLOR_GRAY; + case Color.White: + return Curses.COLOR_WHITE | Curses.A_BOLD | Curses.COLOR_GRAY; + } + throw new ArgumentException ("Invalid color code"); + } + + static Color MapCursesColor (int color) + { + switch (color) { + case Curses.COLOR_BLACK: + return Color.Black; + case Curses.COLOR_BLUE: + return Color.Blue; + case Curses.COLOR_GREEN: + return Color.Green; + case Curses.COLOR_CYAN: + return Color.Cyan; + case Curses.COLOR_RED: + return Color.Red; + case Curses.COLOR_MAGENTA: + return Color.Magenta; + case Curses.COLOR_YELLOW: + return Color.Brown; + case Curses.COLOR_WHITE: + return Color.Gray; + case Curses.COLOR_GRAY: + return Color.DarkGray; + case Curses.COLOR_BLUE | Curses.COLOR_GRAY: + return Color.BrightBlue; + case Curses.COLOR_GREEN | Curses.COLOR_GRAY: + return Color.BrightGreen; + case Curses.COLOR_CYAN | Curses.COLOR_GRAY: + return Color.BrightCyan; + case Curses.COLOR_RED | Curses.COLOR_GRAY: + return Color.BrightRed; + case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY: + return Color.BrightMagenta; + case Curses.COLOR_YELLOW | Curses.COLOR_GRAY: + return Color.BrightYellow; + case Curses.COLOR_WHITE | Curses.COLOR_GRAY: + return Color.White; + } + throw new ArgumentException ("Invalid curses color code"); + } + + /// + /// In the CursesDriver, colors are encoded as an int. + /// The foreground color is stored in the most significant 4 bits, + /// and the background color is stored in the least significant 4 bits. + /// The Terminal.GUI Color values are converted to curses color encoding before being encoded. + /// + public override bool GetColors (int value, out Color foreground, out Color background) + { + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = MapCursesColor ((value >> 4) & 0xF); + background = MapCursesColor (value & 0xF); + + return true; + } + + #endregion + + public override void UpdateCursor () => Refresh (); + + public override void End () + { + StopReportingMouseMoves (); + SetCursorVisibility (CursorVisibility.Default); + + Curses.endwin (); } + public override void UpdateScreen () => _window.redrawwin (); + + public Curses.Window _window; + static Key MapCursesKey (int cursesKey) { switch (cursesKey) { @@ -592,35 +693,6 @@ public override void Init (Action terminalResized) InitializeColorSchemes (); } else { throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does."); - //InitializeColorSchemes (false); - - //// BUGBUG: This is a hack to make the colors work on the Mac? - //// The new Theme support overwrites these colors, so this is not needed? - //Colors.TopLevel.Normal = Curses.COLOR_GREEN; - //Colors.TopLevel.Focus = Curses.COLOR_WHITE; - //Colors.TopLevel.HotNormal = Curses.COLOR_YELLOW; - //Colors.TopLevel.HotFocus = Curses.COLOR_YELLOW; - //Colors.TopLevel.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - //Colors.Base.Normal = Curses.A_NORMAL; - //Colors.Base.Focus = Curses.A_REVERSE; - //Colors.Base.HotNormal = Curses.A_BOLD; - //Colors.Base.HotFocus = Curses.A_BOLD | Curses.A_REVERSE; - //Colors.Base.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - //Colors.Menu.Normal = Curses.A_REVERSE; - //Colors.Menu.Focus = Curses.A_NORMAL; - //Colors.Menu.HotNormal = Curses.A_BOLD; - //Colors.Menu.HotFocus = Curses.A_NORMAL; - //Colors.Menu.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - //Colors.Dialog.Normal = Curses.A_REVERSE; - //Colors.Dialog.Focus = Curses.A_NORMAL; - //Colors.Dialog.HotNormal = Curses.A_BOLD; - //Colors.Dialog.HotFocus = Curses.A_NORMAL; - //Colors.Dialog.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; - //Colors.Error.Normal = Curses.A_BOLD; - //Colors.Error.Focus = Curses.A_BOLD | Curses.A_REVERSE; - //Colors.Error.HotNormal = Curses.A_BOLD | Curses.A_REVERSE; - //Colors.Error.HotFocus = Curses.A_REVERSE; - //Colors.Error.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; } ResizeScreen (); @@ -660,90 +732,6 @@ public static bool Is_WSL_Platform () return false; } - static int MapColor (Color color) - { - switch (color) { - case Color.Black: - return Curses.COLOR_BLACK; - case Color.Blue: - return Curses.COLOR_BLUE; - case Color.Green: - return Curses.COLOR_GREEN; - case Color.Cyan: - return Curses.COLOR_CYAN; - case Color.Red: - return Curses.COLOR_RED; - case Color.Magenta: - return Curses.COLOR_MAGENTA; - case Color.Brown: - return Curses.COLOR_YELLOW; - case Color.Gray: - return Curses.COLOR_WHITE; - case Color.DarkGray: - //return Curses.COLOR_BLACK | Curses.A_BOLD; - return Curses.COLOR_GRAY; - case Color.BrightBlue: - return Curses.COLOR_BLUE | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightGreen: - return Curses.COLOR_GREEN | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightCyan: - return Curses.COLOR_CYAN | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightRed: - return Curses.COLOR_RED | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightMagenta: - return Curses.COLOR_MAGENTA | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.BrightYellow: - return Curses.COLOR_YELLOW | Curses.A_BOLD | Curses.COLOR_GRAY; - case Color.White: - return Curses.COLOR_WHITE | Curses.A_BOLD | Curses.COLOR_GRAY; - } - throw new ArgumentException ("Invalid color code"); - } - - static Color MapCursesColor (int color) - { - switch (color) { - case Curses.COLOR_BLACK: - return Color.Black; - case Curses.COLOR_BLUE: - return Color.Blue; - case Curses.COLOR_GREEN: - return Color.Green; - case Curses.COLOR_CYAN: - return Color.Cyan; - case Curses.COLOR_RED: - return Color.Red; - case Curses.COLOR_MAGENTA: - return Color.Magenta; - case Curses.COLOR_YELLOW: - return Color.Brown; - case Curses.COLOR_WHITE: - return Color.Gray; - case Curses.COLOR_GRAY: - return Color.DarkGray; - case Curses.COLOR_BLUE | Curses.COLOR_GRAY: - return Color.BrightBlue; - case Curses.COLOR_GREEN | Curses.COLOR_GRAY: - return Color.BrightGreen; - case Curses.COLOR_CYAN | Curses.COLOR_GRAY: - return Color.BrightCyan; - case Curses.COLOR_RED | Curses.COLOR_GRAY: - return Color.BrightRed; - case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY: - return Color.BrightMagenta; - case Curses.COLOR_YELLOW | Curses.COLOR_GRAY: - return Color.BrightYellow; - case Curses.COLOR_WHITE | Curses.COLOR_GRAY: - return Color.White; - } - throw new ArgumentException ("Invalid curses color code"); - } - - public override Attribute MakeAttribute (Color fore, Color back) - { - return MakeColor ((short)(MapColor (fore) & 0xffff), (short)MapColor (back)); - } - public override void Suspend () { StopReportingMouseMoves (); @@ -844,14 +832,6 @@ public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, _keyUpHandler (new KeyEvent (key, km)); } - public override bool GetColors (int value, out Color foreground, out Color background) - { - // Assume a 4-bit encoded value for both foreground and background colors. - foreground = MapCursesColor ((value >> 4) & 0xF); - background = MapCursesColor (value & 0xF); - - return true; - } } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs index 8a0ed3da78..7f87c9ef5c 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs @@ -250,7 +250,35 @@ static public int IsAlt (int key) public static int StartColor () => methods.start_color (); public static bool HasColors => methods.has_colors (); + /// + /// The init_pair routine changes the definition of a color-pair.It takes + /// three arguments: the number of the color-pair to be changed, the fore- + /// ground color number, and the background color number.For portable ap- + /// plications: + /// + /// o The first argument must be a legal color pair value.If default + /// colors are used (see use_default_colors(3x)) the upper limit is ad- + /// justed to allow for extra pairs which use a default color in fore- + /// ground and/or background. + /// + /// o The second and third arguments must be legal color values. + /// + /// If the color-pair was previously initialized, the screen is refreshed + /// and all occurrences of that color-pair are changed to the new defini- + /// tion. + /// + /// As an extension, ncurses allows you to set color pair 0 via the as- + /// sume_default_colors (3x) routine, or to specify the use of default col- + /// ors (color number -1) if you first invoke the use_default_colors (3x) + /// routine. + /// + /// + /// + /// + /// public static int InitColorPair (short pair, short foreground, short background) => methods.init_pair (pair, foreground, background); + // TODO: Upgrade to ncurses 6.1 and use the extended version + //public static int InitExtendedPair (int pair, int foreground, int background) => methods.init_extended_pair (pair, foreground, background); public static int UseDefaultColors () => methods.use_default_colors (); public static int ColorPairs => methods.COLOR_PAIRS (); From 3c5c7b114e6d8ae9f88e6313b4e0ed87cc7d84a8 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 10:32:58 +0200 Subject: [PATCH 46/99] CursesDriver - Adding _BOLD has no effect. Further up the stack we cast the return of ColorToCursesColor from int to short and the _BOLD values don't fit in a short. --- .../ConsoleDrivers/CursesDriver/CursesDriver.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 8159589f90..3f9dc6d83f 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -207,7 +207,7 @@ public override Attribute MakeColor (Color fore, Color back) return MakeColor ((short)(ColorToCursesColorNumber (fore) & 0xffff), (short)ColorToCursesColorNumber (back)); } - static int ColorToCursesColorNumber (Color color) + static short ColorToCursesColorNumber (Color color) { switch (color) { case Color.Black: @@ -230,19 +230,19 @@ static int ColorToCursesColorNumber (Color color) //return Curses.COLOR_BLACK | Curses.A_BOLD; return Curses.COLOR_GRAY; case Color.BrightBlue: - return Curses.COLOR_BLUE | Curses.A_BOLD | Curses.COLOR_GRAY; + return Curses.COLOR_BLUE | Curses.COLOR_GRAY; case Color.BrightGreen: - return Curses.COLOR_GREEN | Curses.A_BOLD | Curses.COLOR_GRAY; + return Curses.COLOR_GREEN | Curses.COLOR_GRAY; case Color.BrightCyan: - return Curses.COLOR_CYAN | Curses.A_BOLD | Curses.COLOR_GRAY; + return Curses.COLOR_CYAN | Curses.COLOR_GRAY; case Color.BrightRed: - return Curses.COLOR_RED | Curses.A_BOLD | Curses.COLOR_GRAY; + return Curses.COLOR_RED | Curses.COLOR_GRAY; case Color.BrightMagenta: - return Curses.COLOR_MAGENTA | Curses.A_BOLD | Curses.COLOR_GRAY; + return Curses.COLOR_MAGENTA | Curses.COLOR_GRAY; case Color.BrightYellow: - return Curses.COLOR_YELLOW | Curses.A_BOLD | Curses.COLOR_GRAY; + return Curses.COLOR_YELLOW | Curses.COLOR_GRAY; case Color.White: - return Curses.COLOR_WHITE | Curses.A_BOLD | Curses.COLOR_GRAY; + return Curses.COLOR_WHITE | Curses.COLOR_GRAY; } throw new ArgumentException ("Invalid color code"); } From 59c730e4d30cca346ea103643f2016ee9a9d1beb Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 10:37:07 +0200 Subject: [PATCH 47/99] CursesDriver color code - make code more accurate --- .../ConsoleDrivers/CursesDriver/CursesDriver.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 3f9dc6d83f..a81c77147d 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -192,8 +192,8 @@ static Attribute MakeColor (short foreground, short background) Curses.InitColorPair (v, foreground, background); return new Attribute ( value: Curses.ColorPair (v), - foreground: MapCursesColor (foreground), - background: MapCursesColor (background)); + foreground: CursesColorNumberToColor (foreground), + background: CursesColorNumberToColor (background)); } /// @@ -204,7 +204,7 @@ static Attribute MakeColor (short foreground, short background) /// public override Attribute MakeColor (Color fore, Color back) { - return MakeColor ((short)(ColorToCursesColorNumber (fore) & 0xffff), (short)ColorToCursesColorNumber (back)); + return MakeColor (ColorToCursesColorNumber (fore), ColorToCursesColorNumber (back)); } static short ColorToCursesColorNumber (Color color) @@ -247,7 +247,7 @@ static short ColorToCursesColorNumber (Color color) throw new ArgumentException ("Invalid color code"); } - static Color MapCursesColor (int color) + static Color CursesColorNumberToColor (short color) { switch (color) { case Curses.COLOR_BLACK: @@ -295,8 +295,8 @@ static Color MapCursesColor (int color) public override bool GetColors (int value, out Color foreground, out Color background) { // Assume a 4-bit encoded value for both foreground and background colors. - foreground = MapCursesColor ((value >> 4) & 0xF); - background = MapCursesColor (value & 0xF); + foreground = CursesColorNumberToColor ((short)((value >> 4) & 0xF)); + background = CursesColorNumberToColor ((short)(value & 0xF)); return true; } From 95f4517cbba61baa59ba69401e456546ddb812d5 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 11:02:29 +0200 Subject: [PATCH 48/99] CursesDriver color code - make code more accurate --- Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index a81c77147d..992f4364a8 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -227,7 +227,6 @@ static short ColorToCursesColorNumber (Color color) case Color.Gray: return Curses.COLOR_WHITE; case Color.DarkGray: - //return Curses.COLOR_BLACK | Curses.A_BOLD; return Curses.COLOR_GRAY; case Color.BrightBlue: return Curses.COLOR_BLUE | Curses.COLOR_GRAY; From afb5bd7586c8326353ad36f2ada5f8eaf17985e9 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 11:20:52 +0200 Subject: [PATCH 49/99] Simplified ConsoleDriver.GetColors API --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 2 +- .../ConsoleDrivers/CursesDriver/CursesDriver.cs | 4 +--- Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs | 4 +--- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 11 ++++------- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 3 +-- UICatalog/Scenarios/BasicColors.cs | 4 ++-- 6 files changed, 10 insertions(+), 18 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 468fd81687..fe2f825459 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -300,7 +300,7 @@ public virtual Attribute MakeAttribute (Color fore, Color back) /// The foreground. /// The background. /// - public abstract bool GetColors (int value, out Color foreground, out Color background); + internal abstract void GetColors (int value, out Color foreground, out Color background); /// /// Gets the current . diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 992f4364a8..e7554491c6 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -291,13 +291,11 @@ static Color CursesColorNumberToColor (short color) /// and the background color is stored in the least significant 4 bits. /// The Terminal.GUI Color values are converted to curses color encoding before being encoded. /// - public override bool GetColors (int value, out Color foreground, out Color background) + internal override void GetColors (int value, out Color foreground, out Color background) { // Assume a 4-bit encoded value for both foreground and background colors. foreground = CursesColorNumberToColor ((short)((value >> 4) & 0xF)); background = CursesColorNumberToColor ((short)(value & 0xF)); - - return true; } #endregion diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 82f75c3b5b..bc506e00e5 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -221,13 +221,11 @@ void SetColor (int color) /// Extracts the foreground and background colors from the encoded value. /// Assumes a 4-bit encoded value for both foreground and background colors. /// - public override bool GetColors (int value, out Color foreground, out Color background) + internal override void GetColors (int value, out Color foreground, out Color background) { // Assume a 4-bit encoded value for both foreground and background colors. foreground = (Color)((value >> 16) & 0xF); background = (Color)(value & 0xF); - - return true; } /// diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 92308e5a31..b9c2260926 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -834,7 +834,7 @@ public override void UpdateScreen () var rows = Rows; var cols = Cols; System.Text.StringBuilder output = new System.Text.StringBuilder (); - var redrawAttr = -1; + Attribute redrawAttr = -1; var lastCol = -1; //GetCursorVisibility (out CursorVisibility savedVisibitity); @@ -875,12 +875,11 @@ public override void UpdateScreen () lastCol = col; } - var attr = Contents [row, col, 1]; + Attribute attr = Contents [row, col, 1]; // Performance: Only send the escape sequence if the attribute has changed. if (attr != redrawAttr) { redrawAttr = attr; - GetColors (attr, out var fgColor, out var bgColor); - output.Append (EscSeqUtils.CSI_SetGraphicsRendition (MapColors ((ConsoleColor)bgColor, false), MapColors ((ConsoleColor)fgColor, true))); + output.Append (EscSeqUtils.CSI_SetGraphicsRendition (MapColors ((ConsoleColor)attr.Background, false), MapColors ((ConsoleColor)attr.Foreground, true))); } outputWidth++; var rune = (Rune)Contents [row, col, 0]; @@ -939,13 +938,11 @@ int MapColors (ConsoleColor color, bool isForeground = true) /// Extracts the foreground and background colors from the encoded value. /// Assumes a 4-bit encoded value for both foreground and background colors. /// - public override bool GetColors (int value, out Color foreground, out Color background) + internal override void GetColors (int value, out Color foreground, out Color background) { // Assume a 4-bit encoded value for both foreground and background colors. foreground = (Color)((value >> 16) & 0xF); background = (Color)(value & 0xF); - - return true; } /// diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 9925d47847..f1b4ce9a59 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1624,12 +1624,11 @@ public override Attribute MakeColor (Color foreground, Color background) /// Extracts the foreground and background colors from the encoded value. /// Assumes a 4-bit encoded value for both foreground and background colors. /// - public override bool GetColors (int value, out Color foreground, out Color background) + internal override void GetColors (int value, out Color foreground, out Color background) { // Assume a 4-bit encoded value for both foreground and background colors. foreground = (Color)((value >> 16) & 0xF); background = (Color)(value & 0xF); - return true; } #endregion diff --git a/UICatalog/Scenarios/BasicColors.cs b/UICatalog/Scenarios/BasicColors.cs index a8fe04e4c2..9284659f74 100644 --- a/UICatalog/Scenarios/BasicColors.cs +++ b/UICatalog/Scenarios/BasicColors.cs @@ -87,8 +87,8 @@ public override void Setup () Application.RootMouseEvent = (e) => { if (e.View != null) { - var colorValue = e.View.GetNormalColor ().Value; - Application.Driver.GetColors (colorValue, out Color fore, out Color back); + var fore = e.View.GetNormalColor ().Foreground; + var back = e.View.GetNormalColor ().Background; lblForeground.Text = fore.ToString (); viewForeground.ColorScheme.Normal = new Attribute (fore, fore); lblBackground.Text = back.ToString (); From 32e674c920e296090110631070545736b435b2a8 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 11:36:18 +0200 Subject: [PATCH 50/99] Simplified ConsoleDriver.GetColors API further --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 1 - Terminal.Gui/ConsoleDrivers/NetDriver.cs | 3 +- Terminal.Gui/Drawing/Color.cs | 5 +-- .../Configuration/ConfigurationMangerTests.cs | 45 +++++++++---------- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index fe2f825459..c690abc131 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -299,7 +299,6 @@ public virtual Attribute MakeAttribute (Color fore, Color back) /// The platform-dependent color value. /// The foreground. /// The background. - /// internal abstract void GetColors (int value, out Color foreground, out Color background); /// diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index b9c2260926..dd598e1de2 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -879,7 +879,8 @@ public override void UpdateScreen () // Performance: Only send the escape sequence if the attribute has changed. if (attr != redrawAttr) { redrawAttr = attr; - output.Append (EscSeqUtils.CSI_SetGraphicsRendition (MapColors ((ConsoleColor)attr.Background, false), MapColors ((ConsoleColor)attr.Foreground, true))); + output.Append (EscSeqUtils.CSI_SetGraphicsRendition ( + MapColors ((ConsoleColor)attr.Background, false), MapColors ((ConsoleColor)attr.Foreground, true))); } outputWidth++; var rune = (Rune)Contents [row, col, 0]; diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs index 6c6d78fa55..3d6425fdc9 100644 --- a/Terminal.Gui/Drawing/Color.cs +++ b/Terminal.Gui/Drawing/Color.cs @@ -188,11 +188,10 @@ public struct Attribute { public Color Background { get; } /// - /// Initializes a new instance of the struct with only the value passed to - /// and trying to get the colors if defined. + /// Initializes a new instance with a platform-specific color value. /// /// Value. - public Attribute (int value) + internal Attribute (int value) { Color foreground = default; Color background = default; diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index 3a2689d026..08b447337d 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -13,7 +13,7 @@ namespace Terminal.Gui.ConfigurationTests { public class ConfigurationManagerTests { - public static readonly JsonSerializerOptions _jsonOptions = new() { + public static readonly JsonSerializerOptions _jsonOptions = new () { Converters = { new AttributeJsonConverter (), new ColorJsonConverter (), @@ -65,44 +65,43 @@ public void DeepMemberwiseCopyTest () Assert.Equal (boolSrc, boolCopy); // Structs - var attrDest = new Attribute (1); - var attrSrc = new Attribute (2); + var attrDest = new Attribute (Color.Black); + var attrSrc = new Attribute (Color.White); var attrCopy = DeepMemberwiseCopy (attrSrc, attrDest); Assert.Equal (attrSrc, attrCopy); // Classes - var colorschemeDest = new ColorScheme () { Disabled = new Attribute (1) }; - var colorschemeSrc = new ColorScheme () { Disabled = new Attribute (2) }; + var colorschemeDest = new ColorScheme () { Disabled = new Attribute (Color.Black) }; + var colorschemeSrc = new ColorScheme () { Disabled = new Attribute (Color.White) }; var colorschemeCopy = DeepMemberwiseCopy (colorschemeSrc, colorschemeDest); Assert.Equal (colorschemeSrc, colorschemeCopy); // Dictionaries - var dictDest = new Dictionary () { { "Disabled", new Attribute (1) } }; - var dictSrc = new Dictionary () { { "Disabled", new Attribute (2) } }; + var dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) } }; + var dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) } }; var dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); Assert.Equal (dictSrc, dictCopy); - dictDest = new Dictionary () { { "Disabled", new Attribute (1) } }; - dictSrc = new Dictionary () { { "Disabled", new Attribute (2) }, { "Normal", new Attribute (3) } }; + dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) } }; + dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) } }; dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); Assert.Equal (dictSrc, dictCopy); // src adds an item - dictDest = new Dictionary () { { "Disabled", new Attribute (1) } }; - dictSrc = new Dictionary () { { "Disabled", new Attribute (2) }, { "Normal", new Attribute (3) } }; + dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) } }; + dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) } }; dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); Assert.Equal (2, dictCopy.Count); Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]); Assert.Equal (dictSrc ["Normal"], dictCopy ["Normal"]); // src updates only one item - dictDest = new Dictionary () { { "Disabled", new Attribute (1) }, { "Normal", new Attribute (2) } }; - dictSrc = new Dictionary () { { "Disabled", new Attribute (3) } }; + dictDest = new Dictionary () { { "Disabled", new Attribute (Color.Black) }, { "Normal", new Attribute (Color.White) } }; + dictSrc = new Dictionary () { { "Disabled", new Attribute (Color.White) } }; dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); Assert.Equal (2, dictCopy.Count); Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]); Assert.Equal (dictDest ["Normal"], dictCopy ["Normal"]); - } //[Fact ()] @@ -207,7 +206,7 @@ public void UseWithoutResetAsserts () } [Fact] - public void Reset_Resets() + public void Reset_Resets () { ConfigurationManager.Locations = ConfigLocations.DefaultOnly; ConfigurationManager.Reset (); @@ -312,7 +311,7 @@ public void TestConfigurationManagerToJson () ConfigurationManager.Reset (); ConfigurationManager.GetHardCodedDefaults (); var stream = ConfigurationManager.ToStream (); - + ConfigurationManager.Settings.Update (stream, "TestConfigurationManagerToJson"); } @@ -320,7 +319,7 @@ public void TestConfigurationManagerToJson () public void TestConfigurationManagerInitDriver_NoLocations () { - + } [Fact, AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)] @@ -336,7 +335,7 @@ public void TestConfigurationManagerInitDriver () // Change Base var json = ConfigurationManager.ToStream (); - + ConfigurationManager.Settings.Update (json, "TestConfigurationManagerInitDriver"); var colorSchemes = ((Dictionary)ConfigurationManager.Themes [ConfigurationManager.Themes.Theme] ["ColorSchemes"].PropertyValue); @@ -507,7 +506,7 @@ public void TestConfigurationManagerUpdateFromJson () ConfigurationManager.Reset (); ConfigurationManager.ThrowOnJsonErrors = true; - + ConfigurationManager.Settings.Update (json, "TestConfigurationManagerUpdateFromJson"); Assert.Equal (Key.Q | Key.CtrlMask, Application.QuitKey); @@ -518,7 +517,7 @@ public void TestConfigurationManagerUpdateFromJson () Assert.Equal (Color.White, Colors.ColorSchemes ["Base"].Normal.Foreground); Assert.Equal (Color.Blue, Colors.ColorSchemes ["Base"].Normal.Background); - var colorSchemes = (Dictionary)Themes.First().Value ["ColorSchemes"].PropertyValue; + var colorSchemes = (Dictionary)Themes.First ().Value ["ColorSchemes"].PropertyValue; Assert.Equal (Color.White, colorSchemes ["Base"].Normal.Foreground); Assert.Equal (Color.Blue, colorSchemes ["Base"].Normal.Background); @@ -615,7 +614,7 @@ public void TestConfigurationManagerInvalidJsonThrows () jsonException = Assert.Throws (() => ConfigurationManager.Settings.Update (json, "test")); Assert.StartsWith ("Unknown property", jsonException.Message); - + Assert.Equal (0, ConfigurationManager.jsonErrors.Length); ConfigurationManager.ThrowOnJsonErrors = false; @@ -696,7 +695,7 @@ public void TestConfigurationManagerInvalidJsonLogs () ConfigurationManager.Settings.Update (json, "test"); ConfigurationManager.Settings.Update ("{}}", "test"); - + Assert.NotEqual (0, ConfigurationManager.jsonErrors.Length); Application.Shutdown (); @@ -743,7 +742,7 @@ public void LoadConfigurationFromAllSources_ShouldLoadSettingsFromAllSources () public void Load_FiresUpdated () { ConfigurationManager.Reset (); - + ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q; ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; From 3e42a44928a74a4a58d2d8b57a7054e501822233 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 24 May 2023 14:57:29 +0200 Subject: [PATCH 51/99] Improved encapslation of Attribute; prep for TrueColor & other attributes like blink --- .../CursesDriver/CursesDriver.cs | 16 ++--- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 6 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 16 ++--- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 4 +- Terminal.Gui/Drawing/Color.cs | 65 ++++++++++++------- Terminal.Gui/View/ViewDrawing.cs | 4 +- Terminal.Gui/Views/HexView.cs | 4 +- UICatalog/Scenarios/LineDrawing.cs | 2 +- UnitTests/ConsoleDrivers/AttributeTests.cs | 51 +++------------ UnitTests/TestHelpers.cs | 27 ++++---- 10 files changed, 89 insertions(+), 106 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index e7554491c6..b5617706ce 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -69,12 +69,12 @@ public override void AddRune (Rune systemRune) var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; Contents [Row, Col - 1, 0] = normalized [0]; - Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 1] = CurrentAttribute.Value; Contents [Row, Col - 1, 2] = 1; Curses.attrset (Contents [Row, Col - 1, 1]); Curses.mvaddch (Row, Col - 1, normalized [0]); } else { - Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 1] = CurrentAttribute.Value; Contents [Row, Col, 2] = 1; if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumns () > 1) { @@ -84,7 +84,7 @@ public override void AddRune (Rune systemRune) Curses.mvaddch (Row, Col - 1, Rune.ReplacementChar.Value); Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; Curses.move (Row, Col); - Curses.attrset (curAttr); + Curses.attrset (curAttr.Value); } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumns () > 1) { // This is a single-width character, and we are not at the end of the line. @@ -93,7 +93,7 @@ public override void AddRune (Rune systemRune) Curses.mvaddch (Row, Col + 1, Rune.ReplacementChar.Value); Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; Curses.move (Row, Col); - Curses.attrset (curAttr); + Curses.attrset (curAttr.Value); } if (runeWidth > 1 && Col == Clip.Right - 1) { @@ -120,7 +120,7 @@ public override void AddRune (Rune systemRune) result = Rune.ReplacementChar; } Contents [Row, column, 0] = result.Value; - Contents [Row, column, 1] = CurrentAttribute; + Contents [Row, column, 1] = CurrentAttribute.Value; Curses.attrset (Contents [Row, column, 1]); // BUGBUG: workaround curses not supporting non BMP? # @@ -133,7 +133,7 @@ public override void AddRune (Rune systemRune) } Curses.move (Row, Col); } - Curses.attrset (curAttr); + Curses.attrset (curAttr.Value); } } } else { @@ -147,7 +147,7 @@ public override void AddRune (Rune systemRune) if (runeWidth > 1) { // This is a double-width character, and we are not at the end of the line. if (validLocation && Col < Clip.Right) { - Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 1] = CurrentAttribute.Value; Contents [Row, Col, 2] = 0; //if (rune.IsBmp) { @@ -709,7 +709,7 @@ public override void UpdateOffScreen () for (int row = 0; row < Rows; row++) { for (int col = 0; col < Cols; col++) { Contents [row, col, 0] = ' '; - Contents [row, col, 1] = Colors.TopLevel.Normal; + Contents [row, col, 1] = Colors.TopLevel.Normal.Value; Contents [row, col, 2] = 0; } } diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index bc506e00e5..c0aad4cc6e 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -93,12 +93,12 @@ public override void AddRune (Rune rune) var combined = newCombo.ToString (); var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; Contents [Row, Col - 1, 0] = normalized [0];// BUGBUG: This is wrong, we need to handle the case where the rune is more than one char - Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 1] = CurrentAttribute.Value; Contents [Row, Col - 1, 2] = 1; Col--; } else { Contents [Row, Col, 0] = rune.Value; - Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 1] = CurrentAttribute.Value; if (Col > 0) { var left = new Rune (Contents [Row, Col - 1, 0]); @@ -110,7 +110,7 @@ public override void AddRune (Rune rune) if (runeWidth > 1) { Col++; Contents [Row, Col, 0] = Rune.ReplacementChar.Value; - Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 1] = CurrentAttribute.Value; Contents [Row, Col, 2] = 1; } } diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index dd598e1de2..ebfc0f5d69 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -273,7 +273,7 @@ bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int bu return true; } else { if (winWidth == _consoleDriver.Cols && - winHeight == _lastWindowHeight && + winHeight == _lastWindowHeight && buffWidth == _consoleDriver.Cols && buffHeight == _consoleDriver.Rows) return false; _lastWindowHeight = Math.Max (winHeight, 0); _inputResultQueue.Enqueue (new InputResult () { @@ -623,10 +623,10 @@ public override void AddRune (Rune systemRune) var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; Contents [Row, Col - 1, 0] = normalized [0]; - Contents [Row, Col - 1, 1] = CurrentAttribute; + Contents [Row, Col - 1, 1] = CurrentAttribute.Value; Contents [Row, Col - 1, 2] = 1; } else { - Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 1] = CurrentAttribute.Value; Contents [Row, Col, 2] = 1; if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumns () > 1) { @@ -655,7 +655,7 @@ public override void AddRune (Rune systemRune) if (runeWidth > 1) { // This is a double-width character, and we are not at the end of the line. if (validLocation && Col < Clip.Right) { - Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 1] = CurrentAttribute.Value; Contents [Row, Col, 2] = 0; } Col++; @@ -834,12 +834,12 @@ public override void UpdateScreen () var rows = Rows; var cols = Cols; System.Text.StringBuilder output = new System.Text.StringBuilder (); - Attribute redrawAttr = -1; + Attribute redrawAttr = new Attribute (); var lastCol = -1; //GetCursorVisibility (out CursorVisibility savedVisibitity); //SetCursorVisibility (CursorVisibility.Invisible); - + for (var row = top; row < rows; row++) { if (Console.WindowHeight < 1) { return; @@ -875,7 +875,7 @@ public override void UpdateScreen () lastCol = col; } - Attribute attr = Contents [row, col, 1]; + Attribute attr = new Attribute (Contents [row, col, 1]); // Performance: Only send the escape sequence if the attribute has changed. if (attr != redrawAttr) { redrawAttr = attr; @@ -1022,7 +1022,7 @@ public override bool EnsureCursorVisibility () #endregion #region Size and Position Handling - + void SetWindowPosition (int col, int row) { if (IsWinPlatform && EnableConsoleScrolling) { diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index f1b4ce9a59..1ecc338282 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1365,7 +1365,7 @@ public override void AddRune (Rune systemRune) rune = Rune.ReplacementChar; } Contents [Row, Col, 0] = rune.Value; - Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 1] = CurrentAttribute.Value; if (Col > 0) { var left = new Rune (Contents [Row, Col - 1, 0]); @@ -1377,7 +1377,7 @@ public override void AddRune (Rune systemRune) if (rune.GetColumns () > 1 && Col == Clip.Right - 1) { // This is a double-width character, and we are at the end of the line. Contents [Row, Col, 0] = Rune.ReplacementChar.Value; - Contents [Row, Col, 1] = CurrentAttribute; + Contents [Row, Col, 1] = CurrentAttribute.Value; Contents [Row, Col, 2] = 1; Col++; } diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs index 3d6425fdc9..e661dfa33d 100644 --- a/Terminal.Gui/Drawing/Color.cs +++ b/Terminal.Gui/Drawing/Color.cs @@ -158,34 +158,34 @@ private float CalculateDistance (TrueColor color1, TrueColor color2) } /// - /// Attributes are used as elements that contain both a foreground and a background or platform specific features. + /// Attributes represent how text is styled when displayed in the terminal. /// /// - /// s are needed to map colors to terminal capabilities that might lack colors. + /// provides a platform independent representation of colors (and someday other forms of text styling). /// They encode both the foreground and the background color and are used in the /// class to define color schemes that can be used in an application. /// [JsonConverter (typeof (AttributeJsonConverter))] public struct Attribute { /// - /// The -specific color attribute value. If is + /// The -specific color value. If is /// the value of this property is invalid (typically because the Attribute was created before a driver was loaded) /// and the attribute should be re-made (see ) before it is used. /// [JsonIgnore (Condition = JsonIgnoreCondition.Always)] - public int Value { get; } + internal int Value { get; } /// /// The foreground color. /// [JsonConverter (typeof (ColorJsonConverter))] - public Color Foreground { get; } + public Color Foreground { get; private init; } /// /// The background color. /// [JsonConverter (typeof (ColorJsonConverter))] - public Color Background { get; } + public Color Background { get; private init; } /// /// Initializes a new instance with a platform-specific color value. @@ -209,7 +209,7 @@ internal Attribute (int value) /// /// Initializes a new instance of the struct. /// - /// Value. + /// platform-dependent color value. /// Foreground /// Background public Attribute (int value, Color foreground, Color background) @@ -240,25 +240,35 @@ public Attribute (int value, Color foreground, Color background) /// /// The color. public Attribute (Color color) : this (color, color) { } + + /// + public override bool Equals (object obj) + { + if (obj is Attribute other) { + return this.Value == other.Value; + } + + return false; + } /// - /// Implicit conversion from an to the underlying, driver-specific, Int32 representation - /// of the color. + /// Compares two attributes for equality. /// - /// The driver-specific color value stored in the attribute. - /// The attribute to convert - public static implicit operator int (Attribute c) - { - if (!c.Initialized) throw new InvalidOperationException ("Attribute: Attributes must be initialized by a driver before use."); - return c.Value; - } + /// + /// + /// + public static bool operator == (Attribute left, Attribute right) => left.Equals (right); /// - /// Implicitly convert an driver-specific color value into an + /// Compares two attributes for inequality. /// - /// An attribute with the specified driver-specific color value. - /// value - public static implicit operator Attribute (int v) => new Attribute (v); + /// + /// + /// + public static bool operator != (Attribute left, Attribute right) => !(left == right); + + /// + public override int GetHashCode () => this.Value.GetHashCode (); /// /// Creates an from the specified foreground and background colors. @@ -274,8 +284,10 @@ public static Attribute Make (Color foreground, Color background) { if (Application.Driver == null) { // Create the attribute, but show it's not been initialized - return new Attribute (-1, foreground, background) { - Initialized = false + return new Attribute () { + Initialized = false, + Foreground = foreground, + Background = background }; } return Application.Driver.MakeAttribute (foreground, background); @@ -310,7 +322,14 @@ public static Attribute Get () /// /// [JsonIgnore] - public bool HasValidColors { get => (int)Foreground > -1 && (int)Background > -1; } + public bool HasValidColors => (int)Foreground > -1 && (int)Background > -1; + + /// + public override string ToString () + { + // Note, Unit tests are dependent on this format + return $"{Foreground},{Background}"; + } } /// diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs index c0d60fc4d9..83e9017c21 100644 --- a/Terminal.Gui/View/ViewDrawing.cs +++ b/Terminal.Gui/View/ViewDrawing.cs @@ -386,7 +386,7 @@ public virtual bool OnRenderLineCanvas () // If we have a SuperView, it'll render our frames. if (!SuperViewRendersLineCanvas && LineCanvas.Bounds != Rect.Empty) { foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map - Driver.SetAttribute (p.Value.Attribute?.Value ?? ColorScheme.Normal); + Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal); Driver.Move (p.Key.X, p.Key.Y); Driver.AddRune (p.Value.Rune!.Value); } @@ -401,7 +401,7 @@ public virtual bool OnRenderLineCanvas () } foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map - Driver.SetAttribute (p.Value.Attribute?.Value ?? ColorScheme.Normal); + Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal); Driver.Move (p.Key.X, p.Key.Y); Driver.AddRune (p.Value.Rune.Value); } diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs index e01a41fd52..b425e5f073 100644 --- a/Terminal.Gui/Views/HexView.cs +++ b/Terminal.Gui/Views/HexView.cs @@ -213,8 +213,8 @@ public override void OnDrawContent (Rect contentArea) Source.Position = displayStart; var n = source.Read (data, 0, data.Length); - int activeColor = ColorScheme.HotNormal; - int trackingColor = ColorScheme.HotFocus; + var activeColor = ColorScheme.HotNormal; + var trackingColor = ColorScheme.HotFocus; for (int line = 0; line < frame.Height; line++) { var lineRect = new Rect (0, line, frame.Width, 1); diff --git a/UICatalog/Scenarios/LineDrawing.cs b/UICatalog/Scenarios/LineDrawing.cs index 1b8d9e37dc..5585e2992f 100644 --- a/UICatalog/Scenarios/LineDrawing.cs +++ b/UICatalog/Scenarios/LineDrawing.cs @@ -119,7 +119,7 @@ public override void OnDrawContent (Rect contentArea) foreach (var canvas in _layers) { foreach (var c in canvas.GetCellMap ()) { - Driver.SetAttribute (c.Value.Attribute?.Value ?? ColorScheme.Normal); + Driver.SetAttribute (c.Value.Attribute ?? ColorScheme.Normal); this.AddRune (c.Key.X, c.Key.Y, c.Value.Rune.Value); } } diff --git a/UnitTests/ConsoleDrivers/AttributeTests.cs b/UnitTests/ConsoleDrivers/AttributeTests.cs index c94cabd233..f9331c0090 100644 --- a/UnitTests/ConsoleDrivers/AttributeTests.cs +++ b/UnitTests/ConsoleDrivers/AttributeTests.cs @@ -23,31 +23,29 @@ public void Constuctors_Constuct () Assert.Equal (default (Color), attr.Foreground); Assert.Equal (default (Color), attr.Background); - // Test value, foreground, background - var value = 42; + // Test foreground, background var fg = new Color (); fg = Color.Red; var bg = new Color (); bg = Color.Blue; - attr = new Attribute (value, fg, bg); - - Assert.Equal (value, attr.Value); - Assert.Equal (fg, attr.Foreground); - Assert.Equal (bg, attr.Background); - - // value, foreground, background attr = new Attribute (fg, bg); + Assert.True (attr.Initialized); + Assert.True (attr.HasValidColors); Assert.Equal (fg, attr.Foreground); Assert.Equal (bg, attr.Background); attr = new Attribute (fg); + Assert.True (attr.Initialized); + Assert.True (attr.HasValidColors); Assert.Equal (fg, attr.Foreground); Assert.Equal (fg, attr.Background); attr = new Attribute (bg); + Assert.True (attr.Initialized); + Assert.True (attr.HasValidColors); Assert.Equal (bg, attr.Foreground); Assert.Equal (bg, attr.Background); @@ -73,44 +71,15 @@ public void Implicit_Assign () // Test conversion to int attr = new Attribute (value, fg, bg); - int value_implicit = (int)attr.Value; + int value_implicit = attr.Value; Assert.Equal (value, value_implicit); - // Test conversion from int - attr = value; Assert.Equal (value, attr.Value); driver.End (); Application.Shutdown (); } - [Fact] - public void Implicit_Assign_NoDriver () - { - - var attr = new Attribute (); - - var fg = new Color (); - fg = Color.Red; - - var bg = new Color (); - bg = Color.Blue; - - // Test conversion to int - attr = new Attribute (fg, bg); - int value_implicit = (int)attr.Value; - Assert.False (attr.Initialized); - - Assert.Equal (-1, value_implicit); - Assert.False (attr.Initialized); - - // Test conversion from int - attr = -1; - Assert.Equal (-1, attr.Value); - Assert.False (attr.Initialized); - - } - [Fact] public void Make_SetsNotInitialized_NoDriver () { @@ -185,8 +154,8 @@ public void Get_Gets () var attr = new Attribute (value, fg, bg); - driver.SetAttribute(attr); - + driver.SetAttribute (attr); + var ret_attr = Attribute.Get (); Assert.Equal (value, ret_attr.Value); diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index 043034f432..ee4ba4b599 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -267,12 +267,12 @@ public static void AssertDriverColorsAre (string expectedLook, params Attribute for (var c = 0; c < line.Length; c++) { - var val = contents [r, c, 1]; + Attribute val = new Attribute( contents [r, c, 1]); - var match = expectedColors.Where (e => e.Value == val).ToList (); + var match = expectedColors.Where (e => e == val).ToList (); switch (match.Count) { case 0: - throw new Exception ($"Unexpected color {DescribeColor (val)} was used at row {r} and col {c} (indexes start at 0). Color value was {val} (expected colors were {string.Join (",", expectedColors.Select (c => DescribeColor (c.Value)))})"); + throw new Exception ($"Unexpected color {val} was used at row {r} and col {c} (indexes start at 0). Color value was {val} (expected colors were {string.Join (",", expectedColors.Select (c => c.Value.ToString()))})"); case > 1: throw new ArgumentException ($"Bad value for expectedColors, {match.Count} Attributes had the same Value"); } @@ -280,7 +280,7 @@ public static void AssertDriverColorsAre (string expectedLook, params Attribute var colorUsed = Array.IndexOf (expectedColors, match [0]).ToString () [0]; var userExpected = line [c]; - if (colorUsed != userExpected) throw new Exception ($"Colors used did not match expected at row {r} and col {c} (indexes start at 0). Color index used was {colorUsed} ({DescribeColor (val)}) but test expected {userExpected} ({DescribeColor (expectedColors [int.Parse (userExpected.ToString ())].Value)}) (these are indexes into the expectedColors array)"); + if (colorUsed != userExpected) throw new Exception ($"Colors used did not match expected at row {r} and col {c} (indexes start at 0). Color index used was {colorUsed} ({val}) but test expected {userExpected} ({expectedColors [int.Parse (userExpected.ToString ())].Value}) (these are indexes into the expectedColors array)"); } r++; @@ -300,18 +300,19 @@ internal static void AssertDriverUsedColors (params Attribute [] expectedColors) var toFind = expectedColors.ToList (); - var colorsUsed = new HashSet (); + // Contents 3rd column is an Attribute + var colorsUsed = new HashSet (); for (var r = 0; r < driver.Rows; r++) { for (var c = 0; c < driver.Cols; c++) { - var val = contents [r, c, 1]; + var val = new Attribute(contents [r, c, 1]); colorsUsed.Add (val); - var match = toFind.FirstOrDefault (e => e.Value == val); + var match = toFind.FirstOrDefault (e => e == val); // need to check twice because Attribute is a struct and therefore cannot be null - if (toFind.Any (e => e.Value == val)) { + if (toFind.Any (e => e == val)) { toFind.Remove (match); } }} @@ -320,16 +321,10 @@ internal static void AssertDriverUsedColors (params Attribute [] expectedColors) return; } var sb = new StringBuilder (); - sb.AppendLine ("The following colors were not used:" + string.Join ("; ", toFind.Select (a => DescribeColor (a)))); - sb.AppendLine ("Colors used were:" + string.Join ("; ", colorsUsed.Select (DescribeColor))); + sb.AppendLine ("The following colors were not used:" + string.Join ("; ", toFind.Select (a => a.ToString()))); + sb.AppendLine ("Colors used were:" + string.Join ("; ", colorsUsed.Select (a => a.ToString()))); throw new Exception (sb.ToString ()); } - - private static object DescribeColor (int userExpected) - { - var a = new Attribute (userExpected); - return $"{a.Foreground},{a.Background}"; - } #pragma warning disable xUnit1013 // Public method should be marked as test /// From 577db4237b3ec0730650dbf35924349db305831e Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 26 May 2023 00:20:54 +0100 Subject: [PATCH 52/99] Fixes #2249. CharacterMap isn't refreshing well non-BMP code points on scroll. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 17 ++++++++++------- Terminal.Gui/Text/RuneExtensions.cs | 4 ++-- UICatalog/Scenarios/CharacterMap.cs | 2 +- UnitTests/Text/RuneTests.cs | 4 ++-- UnitTests/View/DrawTests.cs | 12 ++++++------ 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 6d9fa4930a..7558b285bd 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -690,12 +690,12 @@ public override void AddRune (Rune rune) } else { if (runeWidth < 2 && ccol > 0 - && ((Rune)(char)contents [crow, ccol - 1, 0]).GetColumns () > 1) { + && ((Rune)contents [crow, ccol - 1, 0]).GetColumns () > 1) { contents [crow, ccol - 1, 0] = (int)(uint)' '; } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && ((Rune)(char)contents [crow, ccol, 0]).GetColumns () > 1) { + && ((Rune)contents [crow, ccol, 0]).GetColumns () > 1) { contents [crow, ccol + 1, 0] = (int)(uint)' '; contents [crow, ccol + 1, 2] = 1; @@ -953,11 +953,14 @@ public override void UpdateScreen () } outputWidth++; var rune = (Rune)contents [row, col, 0]; - char [] spair; - if (rune.DecodeSurrogatePair (out spair)) { - output.Append (spair); - } else { - output.Append ((char)rune.Value); + output.Append (rune.ToString ()); + if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { + SetCursorPosition (lastCol, row); + Console.Write (output); + output.Clear (); + lastCol += outputWidth; + outputWidth = 0; + Console.CursorLeft--; } contents [row, col, 2] = 0; } diff --git a/Terminal.Gui/Text/RuneExtensions.cs b/Terminal.Gui/Text/RuneExtensions.cs index d27e6ff822..f273678749 100644 --- a/Terminal.Gui/Text/RuneExtensions.cs +++ b/Terminal.Gui/Text/RuneExtensions.cs @@ -42,7 +42,7 @@ public static int GetColumns (this Rune rune) /* if we arrive here, ucs is not a combining or C0/C1 control character */ return 1 + (BiSearch (codePoint, _combiningWideChars, _combiningWideChars.GetLength (0) - 1) != 0 ? 1 : 0); } - + /// /// Returns if the rune is a combining character. /// @@ -266,7 +266,7 @@ public static bool CanBeEncodedAsRune (byte [] buffer) { 0xff01, 0xff60 }, { 0xffe0, 0xffe6 }, { 0x16fe0, 0x16fe4 }, { 0x16ff0, 0x16ff1 }, { 0x17000, 0x187f7 }, { 0x18800, 0x18cd5 }, { 0x18d00, 0x18d08 }, { 0x1aff0, 0x1affc }, - { 0x1b000, 0x1b122 }, { 0x1b150, 0x1b152 }, { 0x1b164, 0x1b167 }, { 0x1b170, 0x1b2fb }, { 0x1d538, 0x1d550 }, + { 0x1b000, 0x1b122 }, { 0x1b150, 0x1b152 }, { 0x1b164, 0x1b167 }, //{ 0x1b170, 0x1b2fb }, { 0x1d538, 0x1d550 }, { 0x1f004, 0x1f004 }, { 0x1f0cf, 0x1f0cf }, /*{ 0x1f100, 0x1f10a },*/ //{ 0x1f110, 0x1f12d }, { 0x1f130, 0x1f169 }, { 0x1f170, 0x1f1ac }, { 0x1f18f, 0x1f199 }, diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index bb8c8a4d1c..dff90193b2 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -404,7 +404,7 @@ public override void OnDrawContentComplete (Rect contentArea) Driver.SetAttribute (GetFocusColor ()); } - if (char.IsSurrogate ((char)(val + col))) { + if (val + col < 0xffff && char.IsSurrogate ((char)(val + col))) { Driver.AddRune (Rune.ReplacementChar); } else { Driver.AddRune (new Rune (val + col)); diff --git a/UnitTests/Text/RuneTests.cs b/UnitTests/Text/RuneTests.cs index 9c0d686bf9..8d783fd8ef 100644 --- a/UnitTests/Text/RuneTests.cs +++ b/UnitTests/Text/RuneTests.cs @@ -539,7 +539,7 @@ public void Test_All_Surrogate_Pairs_Range () [Theory] [InlineData ("Hello, 世界", 13, 11, 9)] // Without Surrogate Pairs - [InlineData ("Hello, 𝔹𝕆𝔹", 19, 13, 13)] // With Surrogate Pairs + [InlineData ("Hello, 𝔹𝕆𝔹", 19, 10, 13)] // With Surrogate Pairs public void Test_DecodeRune_Extension (string text, int bytesLength, int colsLength, int textLength) { List runes = new List (); @@ -558,7 +558,7 @@ public void Test_DecodeRune_Extension (string text, int bytesLength, int colsLen [Theory] [InlineData ("Hello, 世界", 13, 11, 9, "界世 ,olleH")] // Without Surrogate Pairs - [InlineData ("Hello, 𝔹𝕆𝔹", 19, 13, 13, "𝔹𝕆𝔹 ,olleH")] // With Surrogate Pairs + [InlineData ("Hello, 𝔹𝕆𝔹", 19, 10, 13, "𝔹𝕆𝔹 ,olleH")] // With Surrogate Pairs public void Test_DecodeLastRune_Extension (string text, int bytesLength, int colsLength, int textLength, string encoded) { List runes = new List (); diff --git a/UnitTests/View/DrawTests.cs b/UnitTests/View/DrawTests.cs index 3f700c2d7f..6a5ea02432 100644 --- a/UnitTests/View/DrawTests.cs +++ b/UnitTests/View/DrawTests.cs @@ -23,8 +23,8 @@ public void Non_Bmp_ConsoleWidth_ColumnWidth_Equal_Two () Assert.Equal ("𝔹", r.ToString ()); Assert.Equal (us, r.ToString ()); - Assert.Equal (2, us.GetColumns ()); - Assert.Equal (2, r.GetColumns ()); + Assert.Equal (1, us.GetColumns ()); + Assert.Equal (1, r.GetColumns ()); var win = new Window () { Title = us }; var label = new Label (r.ToString ()); @@ -37,9 +37,9 @@ public void Non_Bmp_ConsoleWidth_ColumnWidth_Equal_Two () ((FakeDriver)Application.Driver).SetBufferSize (10, 4); var expected = @" -┌┤𝔹├────┐ -│𝔹 │ -│𝔹 │ +┌┤𝔹├─────┐ +│𝔹 │ +│𝔹 │ └────────┘"; TestHelpers.AssertDriverContentsWithFrameAre (expected, output); @@ -55,7 +55,7 @@ public void Non_Bmp_ConsoleWidth_ColumnWidth_Equal_Two () }; TestHelpers.AssertDriverColorsAre (@" -0022000000 +0020000000 0000000000 0111000000 0000000000", expectedColors); From f7977318ac2e27850026080578a51306f90060ae Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 26 May 2023 00:28:28 +0100 Subject: [PATCH 53/99] Use GetRange to take some of the runes before convert to string. --- Terminal.Gui/Views/TextField.cs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index fff8af2cd5..0e9d3f382c 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -453,7 +453,7 @@ public override void OnDrawContent (Rect contentArea) var roc = GetReadOnlyColor (); for (int idx = p; idx < tcount; idx++) { var rune = _text [idx]; - var cols = ((Rune)rune).GetColumns (); + var cols = rune.GetColumns (); if (idx == _point && HasFocus && !Used && _length == 0 && !ReadOnly) { Driver.SetAttribute (selColor); } else if (ReadOnly) { @@ -466,7 +466,7 @@ public override void OnDrawContent (Rect contentArea) Driver.SetAttribute (idx >= _start && _length > 0 && idx < _start + _length ? selColor : ColorScheme.Focus); } if (col + cols <= width) { - Driver.AddRune ((Rune)(Secret ? CM.Glyphs.Dot : rune)); + Driver.AddRune ((Secret ? CM.Glyphs.Dot : rune)); } if (!TextModel.SetCol (ref col, width, cols)) { break; @@ -1198,14 +1198,11 @@ public virtual void Cut () List DeleteSelectedText () { - string actualText = Text; SetSelectedStartSelectedLength (); int selStart = SelectedStart > -1 ? _start : _point; - (var size, var _) = TextModel.DisplaySize (_text, 0, selStart, false); - (var size2, var _) = TextModel.DisplaySize (_text, selStart, selStart + _length, false); - (var size3, var _) = TextModel.DisplaySize (_text, selStart + _length, actualText.GetRuneCount (), false); - var newText = actualText [..size] + - actualText.Substring (size + size2, size3); + var newText = StringExtensions.ToString (_text.GetRange (0, selStart)) + + StringExtensions.ToString (_text.GetRange (selStart + _length, _text.Count - (selStart + _length))); + ClearAllSelection (); _point = selStart >= newText.GetRuneCount () ? newText.GetRuneCount () : selStart; return newText.ToRuneList (); @@ -1222,14 +1219,11 @@ public virtual void Paste () SetSelectedStartSelectedLength (); int selStart = _start == -1 ? CursorPosition : _start; - string actualText = Text; - (int size, int _) = TextModel.DisplaySize (_text, 0, selStart, false); - (var size2, var _) = TextModel.DisplaySize (_text, selStart, selStart + _length, false); - (var size3, var _) = TextModel.DisplaySize (_text, selStart + _length, actualText.GetRuneCount (), false); string cbTxt = Clipboard.Contents.Split ("\n") [0] ?? ""; - Text = actualText [..size] + + Text = StringExtensions.ToString (_text.GetRange (0, selStart)) + cbTxt + - actualText.Substring (size + size2, size3); + StringExtensions.ToString (_text.GetRange (selStart + _length, _text.Count - (selStart + _length))); + _point = selStart + cbTxt.GetRuneCount (); ClearAllSelection (); SetNeedsDisplay (); From 4148eed2fa642f351499b0d31ea0d32d7a2aafd6 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Fri, 26 May 2023 12:23:47 +0200 Subject: [PATCH 54/99] Attempting to fix unit tests not being cleaned up --- Terminal.Gui/Application.cs | 7 ++- .../CursesDriver/CursesDriver.cs | 2 +- .../CursesDriver/UnixMainLoop.cs | 4 ++ .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 2 +- .../ConsoleDrivers/FakeDriver/FakeMainLoop.cs | 58 +++++++++++-------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 6 +- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 8 ++- Terminal.Gui/MainLoop.cs | 22 ++++--- UICatalog/Scenarios/ProgressBarStyles.cs | 4 +- UnitTests/Application/ApplicationTests.cs | 32 ++++++---- UnitTests/Application/MainLoopTests.cs | 6 +- 11 files changed, 100 insertions(+), 51 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index 540d054f85..4fd20cef85 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -163,7 +163,9 @@ private static List GetSupportedCultures () // calledViaRunT: If false (default) all state will be reset. If true the state will not be reset. internal static void InternalInit (Func topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null, bool calledViaRunT = false) { - if (_initialized && driver == null) return; + if (_initialized && driver == null) { + return; + } if (_initialized) { throw new InvalidOperationException ("Init has already been called and must be bracketed by Shutdown."); @@ -276,6 +278,7 @@ static void ResetState () // BUGBUG: OverlappedTop is not cleared here, but it should be? + MainLoop?.Stop(); MainLoop = null; Driver?.End (); Driver = null; @@ -734,7 +737,7 @@ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool /// public static void DoEvents () { - MainLoop.Driver.Wakeup (); + MainLoop.MainLoopDriver.Wakeup (); } /// diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index b5617706ce..069fa6cc50 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -613,7 +613,7 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle this._keyUpHandler = keyUpHandler; this._mouseHandler = mouseHandler; - var mLoop = mainLoop.Driver as UnixMainLoop; + var mLoop = mainLoop.MainLoopDriver as UnixMainLoop; mLoop.AddWatch (0, UnixMainLoop.Condition.PollIn, x => { ProcessInput (); diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs index 7448e5340e..031f5edb1a 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs @@ -212,5 +212,9 @@ void IMainLoopDriver.Iteration () } } } + public void TearDown () + { + //throw new NotImplementedException (); + } } } diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index c0aad4cc6e..59719b1aac 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -403,7 +403,7 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle _keyUpHandler = keyUpHandler; // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called - (mainLoop.Driver as FakeMainLoop).KeyPressed += (consoleKey) => ProcessInput (consoleKey); + (mainLoop.MainLoopDriver as FakeMainLoop).KeyPressed += (consoleKey) => ProcessInput (consoleKey); } void ProcessInput (ConsoleKeyInfo consoleKey) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs index 06a08a0698..97c9c0146b 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs @@ -11,10 +11,11 @@ namespace Terminal.Gui { /// This implementation is used for FakeDriver. /// public class FakeMainLoop : IMainLoopDriver { - AutoResetEvent keyReady = new AutoResetEvent (false); - AutoResetEvent waitForProbe = new AutoResetEvent (false); - ConsoleKeyInfo? keyResult = null; - MainLoop mainLoop; + AutoResetEvent _keyReady = new AutoResetEvent (false); + AutoResetEvent _waitForProbe = new AutoResetEvent (false); + ConsoleKeyInfo? _keyResult = null; + MainLoop _mainLoop; + Thread _readThread; //Func consoleKeyReaderFn = () => ; /// @@ -33,18 +34,19 @@ public FakeMainLoop (ConsoleDriver consoleDriver = null) void MockKeyReader () { - while (true) { - waitForProbe.WaitOne (); - keyResult = FakeConsole.ReadKey (true); - keyReady.Set (); + while (_mainLoop != null && _waitForProbe != null) { + _waitForProbe?.WaitOne (); + _keyResult = FakeConsole.ReadKey (true); + _keyReady?.Set (); } } void IMainLoopDriver.Setup (MainLoop mainLoop) { - this.mainLoop = mainLoop; - Thread readThread = new Thread (MockKeyReader); - readThread.Start (); + this._mainLoop = mainLoop; + _readThread = new Thread (MockKeyReader); + // BUGBUG: This thread never gets cleaned up. This causes unit tests to never exit. + _readThread.Start (); } void IMainLoopDriver.Wakeup () @@ -53,35 +55,36 @@ void IMainLoopDriver.Wakeup () bool IMainLoopDriver.EventsPending (bool wait) { - keyResult = null; - waitForProbe.Set (); + _keyResult = null; + _waitForProbe.Set (); if (CheckTimers (wait, out var waitTimeout)) { return true; } - keyReady.WaitOne (waitTimeout); - return keyResult.HasValue; + _keyReady.WaitOne (waitTimeout); + return _keyResult.HasValue; } bool CheckTimers (bool wait, out int waitTimeout) { long now = DateTime.UtcNow.Ticks; - if (mainLoop.timeouts.Count > 0) { - waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (_mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); if (waitTimeout < 0) return true; } else { waitTimeout = -1; } - if (!wait) + if (!wait) { waitTimeout = 0; + } int ic; - lock (mainLoop.idleHandlers) { - ic = mainLoop.idleHandlers.Count; + lock (_mainLoop.idleHandlers) { + ic = _mainLoop.idleHandlers.Count; } return ic > 0; @@ -89,10 +92,19 @@ bool CheckTimers (bool wait, out int waitTimeout) void IMainLoopDriver.Iteration () { - if (keyResult.HasValue) { - KeyPressed?.Invoke (keyResult.Value); - keyResult = null; + if (_keyResult.HasValue) { + KeyPressed?.Invoke (_keyResult.Value); + _keyResult = null; } } + public void TearDown () + { + _waitForProbe.Close (); + _waitForProbe.Dispose(); + _waitForProbe = null; + _keyReady.Dispose (); + _keyReady = null; + _mainLoop = null; + } } } \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index ebfc0f5d69..7909c5dae9 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1220,7 +1220,7 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle _keyUpHandler = keyUpHandler; _mouseHandler = mouseHandler; - var mLoop = _mainLoop = mainLoop.Driver as NetMainLoop; + var mLoop = _mainLoop = mainLoop.MainLoopDriver as NetMainLoop; // Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. mLoop.ProcessInput = (e) => ProcessInput (e); @@ -1529,4 +1529,8 @@ void IMainLoopDriver.Iteration () ProcessInput?.Invoke (_inputResult.Dequeue ().Value); } } + public void TearDown () + { + //throw new NotImplementedException (); + } } \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 1ecc338282..e7a53e6b37 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -711,7 +711,7 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle _keyUpHandler = keyUpHandler; _mouseHandler = mouseHandler; - var mLoop = mainLoop.Driver as WindowsMainLoop; + var mLoop = mainLoop.MainLoopDriver as WindowsMainLoop; mLoop.ProcessInput = (e) => ProcessInput (e); @@ -1726,7 +1726,7 @@ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool al public override void End () { - WinConsole.Cleanup (); + WinConsole?.Cleanup (); WinConsole = null; // Needed for Windows Terminal @@ -1903,6 +1903,10 @@ void IMainLoopDriver.Iteration () WinChanged?.Invoke (this, new SizeChangedEventArgs (_windowSize)); } } + public void TearDown () + { + //throw new NotImplementedException (); + } } class WindowsClipboard : ClipboardBase { diff --git a/Terminal.Gui/MainLoop.cs b/Terminal.Gui/MainLoop.cs index 05179fbfbb..6703d670b7 100644 --- a/Terminal.Gui/MainLoop.cs +++ b/Terminal.Gui/MainLoop.cs @@ -35,6 +35,11 @@ public interface IMainLoopDriver { /// The iteration function. /// void Iteration (); + + /// + /// Ensures that the main loop is terminated and any resources created are freed. + /// + void TearDown (); } /// @@ -77,8 +82,8 @@ public ReadOnlyCollection> IdleHandlers { /// /// The current in use. /// - /// The driver. - public IMainLoopDriver Driver { get; } + /// The main loop driver. + public IMainLoopDriver MainLoopDriver { get; } /// /// Invoked when a new timeout is added. To be used in the case @@ -93,7 +98,7 @@ public ReadOnlyCollection> IdleHandlers { /// (one of the implementations FakeMainLoop, UnixMainLoop, NetMainLoop or WindowsMainLoop). public MainLoop (IMainLoopDriver driver) { - Driver = driver; + MainLoopDriver = driver; driver.Setup (this); } @@ -128,7 +133,7 @@ public Func AddIdle (Func idleHandler) idleHandlers.Add (idleHandler); } - Driver.Wakeup (); + MainLoopDriver.Wakeup (); return idleHandler; } @@ -257,13 +262,16 @@ void RunIdle () bool _running; + // BUGBUG: Stop is only called from MainLoopUnitTests.cs. As a result, the mainloop + // will never exit during other unit tests or normal execution. /// /// Stops the mainloop. /// public void Stop () { _running = false; - Driver.Wakeup (); + MainLoopDriver.Wakeup (); + MainLoopDriver.TearDown(); } /// @@ -276,7 +284,7 @@ public void Stop () /// public bool EventsPending (bool wait = false) { - return Driver.EventsPending (wait); + return MainLoopDriver.EventsPending (wait); } /// @@ -294,7 +302,7 @@ public void RunIteration () if (timeouts.Count > 0) RunTimers (); - Driver.Iteration (); + MainLoopDriver.Iteration (); bool runIdle = false; lock (_idleHandlersLock) { diff --git a/UICatalog/Scenarios/ProgressBarStyles.cs b/UICatalog/Scenarios/ProgressBarStyles.cs index 9ff533fa83..022d0adba5 100644 --- a/UICatalog/Scenarios/ProgressBarStyles.cs +++ b/UICatalog/Scenarios/ProgressBarStyles.cs @@ -79,7 +79,7 @@ public override void Setup () _fractionTimer = null; button.Enabled = true; } - Application.MainLoop.Driver.Wakeup (); + Application.MainLoop.MainLoopDriver.Wakeup (); }, null, 0, _timerTick); } }; @@ -128,7 +128,7 @@ public override void Setup () marqueesBlocksPB.Text = marqueesContinuousPB.Text = DateTime.Now.TimeOfDay.ToString (); marqueesBlocksPB.Pulse (); marqueesContinuousPB.Pulse (); - Application.MainLoop.Driver.Wakeup (); + Application.MainLoop.MainLoopDriver.Wakeup (); }, null, 0, 300); Application.Top.Unloaded += Top_Unloaded; diff --git a/UnitTests/Application/ApplicationTests.cs b/UnitTests/Application/ApplicationTests.cs index e44534723e..96951dbef2 100644 --- a/UnitTests/Application/ApplicationTests.cs +++ b/UnitTests/Application/ApplicationTests.cs @@ -41,6 +41,10 @@ void Post_Init_State () Assert.Null (Application.Iteration); Assert.Null (Application.RootMouseEvent); Assert.Null (Application.TerminalResized); + // FakeDriver is always 80x25 + Assert.Equal (80, Application.Driver.Cols); + Assert.Equal (25, Application.Driver.Rows); + } void Init () @@ -60,28 +64,24 @@ void Shutdown () public void Init_Shutdown_Cleans_Up () { // Verify initial state is per spec - Pre_Init_State (); + //Pre_Init_State (); Application.Init (new FakeDriver ()); // Verify post-Init state is correct - Post_Init_State (); - - // MockDriver is always 80x25 - Assert.Equal (80, Application.Driver.Cols); - Assert.Equal (25, Application.Driver.Rows); + //Post_Init_State (); Application.Shutdown (); // Verify state is back to initial - Pre_Init_State (); + //Pre_Init_State (); #if DEBUG_IDISPOSABLE // Validate there are no outstanding Responder-based instances // after a scenario was selected to run. This proves the main UI Catalog // 'app' closed cleanly. - foreach (var inst in Responder.Instances) { - Assert.True (inst.WasDisposed); - } + //foreach (var inst in Responder.Instances) { + //Assert.True (inst.WasDisposed); + //} #endif } @@ -100,7 +100,7 @@ public void Init_Shutdown_Toplevel_Not_Disposed () } [Fact] - public void Init_Unbalanced_Throwss () + public void Init_Unbalanced_Throws () { Application.Init (new FakeDriver ()); @@ -131,6 +131,16 @@ public TestToplevel () } } + [Fact] + public void Init_Null_Driver_Should_Pick_A_Driver () + { + Application.Init (null); + + Assert.NotNull (Application.Driver); + + Shutdown (); + } + [Fact] public void Init_Begin_End_Cleans_Up () { diff --git a/UnitTests/Application/MainLoopTests.cs b/UnitTests/Application/MainLoopTests.cs index e5b694f870..abff9e0674 100644 --- a/UnitTests/Application/MainLoopTests.cs +++ b/UnitTests/Application/MainLoopTests.cs @@ -25,7 +25,7 @@ public class MainLoopTests { public void Constructor_Setups_Driver () { var ml = new MainLoop (new FakeMainLoop ()); - Assert.NotNull (ml.Driver); + Assert.NotNull (ml.MainLoopDriver); } // Idle Handler tests @@ -525,6 +525,10 @@ public void Iteration () { throw new NotImplementedException (); } + public void TearDown () + { + throw new NotImplementedException (); + } public void Setup (MainLoop mainLoop) { From 47a75b3b5ea5a18e27da800780066d5242ea68aa Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Fri, 26 May 2023 15:42:29 +0200 Subject: [PATCH 55/99] Fixes #2658 - ConsoleDriver.IsRuneSupported --- Terminal.Gui/Application.cs | 2 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 5 +--- .../CursesDriver/CursesDriver.cs | 4 --- Terminal.Gui/MainLoop.cs | 25 ++++++++++++------- UnitTests/Application/MainLoopTests.cs | 4 ++- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index 4fd20cef85..8da140f391 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -278,7 +278,7 @@ static void ResetState () // BUGBUG: OverlappedTop is not cleared here, but it should be? - MainLoop?.Stop(); + //MainLoop?.Stop(); MainLoop = null; Driver?.End (); Driver = null; diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index c690abc131..3392f77e79 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -152,10 +152,7 @@ public virtual bool IsRuneSupported (Rune rune) /// /// /// Rune to add. - public virtual void AddRune (Rune rune) - { - - } + public abstract void AddRune (Rune rune); /// /// Adds the specified to the display at the current cursor position. This method diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 069fa6cc50..62a02f17ee 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -49,10 +49,6 @@ public override bool IsRuneSupported (Rune rune) public override void AddRune (Rune systemRune) { - if (!IsRuneSupported (systemRune)) { - systemRune = Rune.ReplacementChar; - } - var rune = systemRune.MakePrintable (); var runeWidth = rune.GetColumns (); var validLocation = IsValidLocation (Col, Row); diff --git a/Terminal.Gui/MainLoop.cs b/Terminal.Gui/MainLoop.cs index 6703d670b7..4454b76753 100644 --- a/Terminal.Gui/MainLoop.cs +++ b/Terminal.Gui/MainLoop.cs @@ -145,8 +145,9 @@ public Func AddIdle (Func idleHandler) /// This method also returns false if the idle handler is not found. public bool RemoveIdle (Func token) { - lock (_idleHandlersLock) + lock (_idleHandlersLock) { return idleHandlers.Remove (token); + } } void AddTimeout (TimeSpan time, Timeout timeout) @@ -154,7 +155,7 @@ void AddTimeout (TimeSpan time, Timeout timeout) lock (_timeoutsLockToken) { var k = (DateTime.UtcNow + time).Ticks; timeouts.Add (NudgeToUniqueKey (k), timeout); - TimeoutAdded?.Invoke (this, new TimeoutEventArgs(timeout, k)); + TimeoutAdded?.Invoke (this, new TimeoutEventArgs (timeout, k)); } } @@ -171,8 +172,9 @@ void AddTimeout (TimeSpan time, Timeout timeout) /// public object AddTimeout (TimeSpan time, Func callback) { - if (callback == null) + if (callback == null) { throw new ArgumentNullException (nameof (callback)); + } var timeout = new Timeout () { Span = time, Callback = callback @@ -193,8 +195,9 @@ public bool RemoveTimeout (object token) { lock (_timeoutsLockToken) { var idx = timeouts.IndexOfValue (token as Timeout); - if (idx == -1) + if (idx == -1) { return false; + } timeouts.RemoveAt (idx); } return true; @@ -218,8 +221,9 @@ void RunTimers () var k = t.Key; var timeout = t.Value; if (k < now) { - if (timeout.Callback (this)) + if (timeout.Callback (this)) { AddTimeout (timeout.Span, timeout); + } } else { lock (_timeoutsLockToken) { timeouts.Add (NudgeToUniqueKey (k), timeout); @@ -254,9 +258,11 @@ void RunIdle () } foreach (var idle in iterate) { - if (idle ()) - lock (_idleHandlersLock) + if (idle ()) { + lock (_idleHandlersLock) { idleHandlers.Add (idle); + } + } } } @@ -271,7 +277,7 @@ public void Stop () { _running = false; MainLoopDriver.Wakeup (); - MainLoopDriver.TearDown(); + //MainLoopDriver.TearDown(); } /// @@ -299,8 +305,9 @@ public bool EventsPending (bool wait = false) /// public void RunIteration () { - if (timeouts.Count > 0) + if (timeouts.Count > 0) { RunTimers (); + } MainLoopDriver.Iteration (); diff --git a/UnitTests/Application/MainLoopTests.cs b/UnitTests/Application/MainLoopTests.cs index abff9e0674..4633833258 100644 --- a/UnitTests/Application/MainLoopTests.cs +++ b/UnitTests/Application/MainLoopTests.cs @@ -663,7 +663,9 @@ public void Mainloop_Invoke_Or_AddIdle_Can_Be_Used_For_Events_Or_Actions (Action Assert.True (btn.ProcessKey (new KeyEvent (Key.Enter, null))); Assert.Equal (cancel, btn.Text); Assert.Equal (one, total); - } else if (taskCompleted) Application.RequestStop (); + } else if (taskCompleted) { + Application.RequestStop (); + } }; Application.Run (); From 279deaac78f334a31eff39dcfa595c935c1d8ded Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Fri, 26 May 2023 15:45:28 +0200 Subject: [PATCH 56/99] Fixes #2658 - ConsoleDriver.IsRuneSupported (for WindowsDriver) --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index e7a53e6b37..92011d734e 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1356,6 +1356,11 @@ int GetOutputBufferPosition () return Row * Cols + Col; } + public override bool IsRuneSupported (Rune rune) + { + return base.IsRuneSupported (rune) && rune.IsBmp; + } + public override void AddRune (Rune systemRune) { if (IsValidLocation (Col, Row)) { From 6cd53f2f53cc996fff852148c82839bca38d4929 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 26 May 2023 15:11:40 +0100 Subject: [PATCH 57/99] Check all the range values and not only the max value. --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 3392f77e79..f1952bccb7 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -132,10 +132,7 @@ public virtual void Move (int col, int row) /// does not support displaying this rune. public virtual bool IsRuneSupported (Rune rune) { - if (rune.Value > RuneExtensions.MaxUnicodeCodePoint) { - return false; - } - return true; + return Rune.IsValid (rune.Value); } /// @@ -152,7 +149,7 @@ public virtual bool IsRuneSupported (Rune rune) /// /// /// Rune to add. - public abstract void AddRune (Rune rune); + public abstract void AddRune (Rune rune); /// /// Adds the specified to the display at the current cursor position. This method @@ -176,7 +173,7 @@ public virtual bool IsRuneSupported (Rune rune) /// String. public void AddStr (string str) { - foreach (var rune in str.EnumerateRunes()) { + foreach (var rune in str.EnumerateRunes ()) { AddRune (rune); } } @@ -246,7 +243,7 @@ public Rect Clip { public abstract void UpdateScreen (); #region Color Handling - + Attribute _currentAttribute; /// From 203e46e7af9b5bdd828984aa9e9d45d77cada1f9 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 26 May 2023 15:22:45 +0100 Subject: [PATCH 58/99] Reducing code. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 33 ++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 1e8ea39bb4..920ede62bf 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -858,11 +858,7 @@ public override void UpdateScreen () for (; col < cols; col++) { if (Contents [row, col, 2] == 0) { if (output.Length > 0) { - SetCursorPosition (lastCol, row); - Console.Write (output); - output.Clear (); - lastCol += outputWidth; - outputWidth = 0; + WriteToConsole (output, ref lastCol, row, ref outputWidth); } else if (lastCol == -1) { lastCol = col; } @@ -884,17 +880,12 @@ public override void UpdateScreen () } outputWidth++; var rune = (Rune)Contents [row, col, 0]; - output.Append(rune.ToString()); - if (rune.IsSurrogatePair() && rune.GetColumns() < 2) - { - SetCursorPosition(lastCol, row); - Console.Write(output); - output.Clear(); - lastCol += outputWidth; - outputWidth = 0; - Console.CursorLeft--; - } - Contents[row, col, 2] = 0; + output.Append (rune.ToString ()); + if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { + WriteToConsole (output, ref lastCol, row, ref outputWidth); + Console.CursorLeft--; + } + Contents [row, col, 2] = 0; } } if (output.Length > 0) { @@ -903,7 +894,17 @@ public override void UpdateScreen () } } SetCursorPosition (0, 0); + //SetCursorVisibility (savedVisibitity); + + void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth) + { + SetCursorPosition (lastCol, row); + Console.Write (output); + output.Clear (); + lastCol += outputWidth; + outputWidth = 0; + } } #region Color Handling From 547642d6cb35f4a18107c67707b3cc38836fe027 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sat, 27 May 2023 09:39:07 +0200 Subject: [PATCH 59/99] Fixes #2674 - Unit test process doesn't exit --- Terminal.Gui/Application.cs | 2 +- .../ConsoleDrivers/FakeDriver/FakeConsole.cs | 16 +- .../ConsoleDrivers/FakeDriver/FakeMainLoop.cs | 142 ++++++------------ Terminal.Gui/MainLoop.cs | 6 - UnitTests/View/KeyboardTests.cs | 2 +- 5 files changed, 57 insertions(+), 111 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index 8da140f391..4fd20cef85 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -278,7 +278,7 @@ static void ResetState () // BUGBUG: OverlappedTop is not cleared here, but it should be? - //MainLoop?.Stop(); + MainLoop?.Stop(); MainLoop = null; Driver?.End (); Driver = null; diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs index 79008ccbbf..45b7469f0d 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs @@ -810,14 +810,14 @@ public static int Read () /// /// /// - public static ConsoleKeyInfo ReadKey (bool intercept) - { - if (MockKeyPresses.Count > 0) { - return MockKeyPresses.Pop (); - } else { - return new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', false, false, false); - } - } + //public static ConsoleKeyInfo ReadKey (bool intercept) + //{ + // if (MockKeyPresses.Count > 0) { + // return MockKeyPresses.Pop (); + // } else { + // return new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', false, false, false); + // } + //} /// /// A stack of keypresses to return when ReadKey is called. diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs index 97c9c0146b..7cbe1d7d63 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs @@ -1,110 +1,62 @@ using System; using System.Threading; +using System.Threading.Tasks; -namespace Terminal.Gui { - /// - /// Mainloop intended to be used with the .NET System.Console API, and can - /// be used on Windows and Unix, it is cross platform but lacks things like - /// file descriptor monitoring. - /// - /// - /// This implementation is used for FakeDriver. - /// - public class FakeMainLoop : IMainLoopDriver { - AutoResetEvent _keyReady = new AutoResetEvent (false); - AutoResetEvent _waitForProbe = new AutoResetEvent (false); - ConsoleKeyInfo? _keyResult = null; - MainLoop _mainLoop; - Thread _readThread; - //Func consoleKeyReaderFn = () => ; +namespace Terminal.Gui; +public class FakeMainLoop : IMainLoopDriver { + private MainLoop _mainLoop; - /// - /// Invoked when a Key is pressed. - /// - public Action KeyPressed; + public Action KeyPressed; - /// - /// Creates an instance of the FakeMainLoop. is not used. - /// - /// - public FakeMainLoop (ConsoleDriver consoleDriver = null) - { - // consoleDriver is not needed/used in FakeConsole - } - - void MockKeyReader () - { - while (_mainLoop != null && _waitForProbe != null) { - _waitForProbe?.WaitOne (); - _keyResult = FakeConsole.ReadKey (true); - _keyReady?.Set (); - } - } - - void IMainLoopDriver.Setup (MainLoop mainLoop) - { - this._mainLoop = mainLoop; - _readThread = new Thread (MockKeyReader); - // BUGBUG: This thread never gets cleaned up. This causes unit tests to never exit. - _readThread.Start (); - } - - void IMainLoopDriver.Wakeup () - { - } - - bool IMainLoopDriver.EventsPending (bool wait) - { - _keyResult = null; - _waitForProbe.Set (); + public FakeMainLoop (ConsoleDriver consoleDriver = null) + { + // consoleDriver is not needed/used in FakeConsole + } + + public void Setup (MainLoop mainLoop) + { + _mainLoop = mainLoop; + } - if (CheckTimers (wait, out var waitTimeout)) { - return true; - } + public void Wakeup () + { + // No implementation needed for FakeMainLoop + } - _keyReady.WaitOne (waitTimeout); - return _keyResult.HasValue; - } + public bool EventsPending (bool wait) + { + //if (CheckTimers (wait, out var waitTimeout)) { + // return true; + //} - bool CheckTimers (bool wait, out int waitTimeout) - { - long now = DateTime.UtcNow.Ticks; + // Always return true for FakeMainLoop + return true; + } - if (_mainLoop.timeouts.Count > 0) { - waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); - if (waitTimeout < 0) - return true; - } else { - waitTimeout = -1; - } + //private bool CheckTimers (bool wait, out int waitTimeout) + //{ + // long now = DateTime.UtcNow.Ticks; - if (!wait) { - waitTimeout = 0; - } + // if (_mainLoop.timeouts.Count > 0) { + // waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + // if (waitTimeout < 0) + // return true; + // } else { + // waitTimeout = -1; + // } - int ic; - lock (_mainLoop.idleHandlers) { - ic = _mainLoop.idleHandlers.Count; - } + // if (!wait) { + // waitTimeout = 0; + // } - return ic > 0; - } + // return _mainLoop.idleHandlers.Count > 0; + //} - void IMainLoopDriver.Iteration () - { - if (_keyResult.HasValue) { - KeyPressed?.Invoke (_keyResult.Value); - _keyResult = null; - } - } - public void TearDown () - { - _waitForProbe.Close (); - _waitForProbe.Dispose(); - _waitForProbe = null; - _keyReady.Dispose (); - _keyReady = null; - _mainLoop = null; + public void Iteration () + { + if (FakeConsole.MockKeyPresses.Count > 0) { + KeyPressed?.Invoke (FakeConsole.MockKeyPresses.Pop ()); } } -} \ No newline at end of file +} + diff --git a/Terminal.Gui/MainLoop.cs b/Terminal.Gui/MainLoop.cs index 4454b76753..0934e15c49 100644 --- a/Terminal.Gui/MainLoop.cs +++ b/Terminal.Gui/MainLoop.cs @@ -35,11 +35,6 @@ public interface IMainLoopDriver { /// The iteration function. /// void Iteration (); - - /// - /// Ensures that the main loop is terminated and any resources created are freed. - /// - void TearDown (); } /// @@ -277,7 +272,6 @@ public void Stop () { _running = false; MainLoopDriver.Wakeup (); - //MainLoopDriver.TearDown(); } /// diff --git a/UnitTests/View/KeyboardTests.cs b/UnitTests/View/KeyboardTests.cs index 6ddd755aff..7b35d9c80a 100644 --- a/UnitTests/View/KeyboardTests.cs +++ b/UnitTests/View/KeyboardTests.cs @@ -160,7 +160,7 @@ public void KeyDown_And_KeyUp_Events_With_Only_Key_Modifiers (bool shift, bool a Application.Top.Add (view); - Console.MockKeyPresses.Push (new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', shift, alt, control)); + Console.MockKeyPresses.Push (new ConsoleKeyInfo ('\0', 0, shift, alt, control)); Application.Iteration += () => Application.RequestStop (); From 8c489cddf41a3c845e2f74007583e82aafa4cac4 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sun, 28 May 2023 11:19:03 +0200 Subject: [PATCH 60/99] Changed Cell to support IsDirty and list of Runes --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 10 ++++++++++ Terminal.Gui/Drawing/Cell.cs | 19 ++++++++++++++----- Terminal.Gui/Drawing/LineCanvas.cs | 7 +++++-- Terminal.Gui/View/ViewDrawing.cs | 6 ++++-- UICatalog/Scenarios/LineDrawing.cs | 3 ++- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 3392f77e79..0ad0ce48eb 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -7,6 +7,7 @@ namespace Terminal.Gui; + /// /// Base class for Terminal.Gui ConsoleDriver implementations. /// @@ -85,6 +86,15 @@ public abstract class ConsoleDriver { /// public int [,,] Contents { get; internal set; } + ///// + ///// The contents of the application output. The driver outputs this buffer to the terminal when + ///// is called. + ///// + ///// The format of the array is rows, columns. The first index is the row, the second index is the column. + ///// + ///// + //public Cell [,] Contents { get; internal set; } + /// /// Initializes the driver /// diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index 8d511b5ded..f6094b337f 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -1,20 +1,29 @@ -using System.Text; +using System.Collections.Generic; +using System.Text; namespace Terminal.Gui; /// -/// Represents a single row/column within the . Includes the glyph and the foreground/background colors. +/// Represents a single row/column in a Terminal.Gui rendering surface +/// (e.g. and ). /// public class Cell { /// - /// The glyph to draw. + /// The list of Runes to draw in this cell. If the list is empty, the cell is blank. If the list contains + /// more than one Rune, the cell is a combining sequence. + /// (See #2616 - Support combining sequences that don't normalize) /// - public Rune? Rune { get; set; } + public List Runes { get; set; } = new List (); /// - /// The foreground color to draw the glyph with. + /// The attributes to use when drawing the Glyph. /// public Attribute? Attribute { get; set; } + /// + /// Gets or sets a value indicating whether this has + /// been modified since the last time it was drawn. + /// + public bool IsDirty { get; set; } } diff --git a/Terminal.Gui/Drawing/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas.cs index 5b09af2333..0c43ec72ed 100644 --- a/Terminal.Gui/Drawing/LineCanvas.cs +++ b/Terminal.Gui/Drawing/LineCanvas.cs @@ -528,14 +528,17 @@ public override void SetGlyphs () } - private Cell GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects) + private Cell? GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects) { if (!intersects.Any ()) { return null; } var cell = new Cell (); - cell.Rune = GetRuneForIntersects (driver, intersects); + var rune = GetRuneForIntersects (driver, intersects); + if (rune.HasValue) { + cell.Runes.Add (rune.Value); + } cell.Attribute = GetAttributeForIntersects (intersects); return cell; } diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs index 83e9017c21..17a725e16b 100644 --- a/Terminal.Gui/View/ViewDrawing.cs +++ b/Terminal.Gui/View/ViewDrawing.cs @@ -388,7 +388,8 @@ public virtual bool OnRenderLineCanvas () foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal); Driver.Move (p.Key.X, p.Key.Y); - Driver.AddRune (p.Value.Rune!.Value); + // TODO: #2616 - Support combining sequences that don't normalize + Driver.AddRune (p.Value.Runes [0]); } LineCanvas.Clear (); } @@ -403,7 +404,8 @@ public virtual bool OnRenderLineCanvas () foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal); Driver.Move (p.Key.X, p.Key.Y); - Driver.AddRune (p.Value.Rune.Value); + // TODO: #2616 - Support combining sequences that don't normalize + Driver.AddRune (p.Value.Runes [0]); } LineCanvas.Clear (); } diff --git a/UICatalog/Scenarios/LineDrawing.cs b/UICatalog/Scenarios/LineDrawing.cs index 5585e2992f..35e349ff74 100644 --- a/UICatalog/Scenarios/LineDrawing.cs +++ b/UICatalog/Scenarios/LineDrawing.cs @@ -120,7 +120,8 @@ public override void OnDrawContent (Rect contentArea) foreach (var c in canvas.GetCellMap ()) { Driver.SetAttribute (c.Value.Attribute ?? ColorScheme.Normal); - this.AddRune (c.Key.X, c.Key.Y, c.Value.Rune.Value); + // TODO: #2616 - Support combining sequences that don't normalize + this.AddRune (c.Key.X, c.Key.Y, c.Value.Runes [0]); } } } From cc18db1e5d6882c83167fb1ad09adfb0004a6af9 Mon Sep 17 00:00:00 2001 From: adstep Date: Mon, 26 Jun 2023 16:34:06 -0700 Subject: [PATCH 61/99] add support for rendering TrueColor output on Windows merging veeman & tznind code --- .../Configuration/AttributeJsonConverter.cs | 8 +- .../Configuration/ColorJsonConverter.cs | 2 +- .../Configuration/TrueColorJsonConverter.cs | 47 +++ Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 19 ++ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 126 ++++++-- Terminal.Gui/Drawing/Color.cs | 296 ++++++++++++++---- UICatalog/Scenarios/Images.cs | 136 ++++++++ UICatalog/Scenarios/TrueColors.cs | 116 +++++++ UnitTests/Configuration/JsonConverterTests.cs | 61 +++- 9 files changed, 714 insertions(+), 97 deletions(-) create mode 100644 Terminal.Gui/Configuration/TrueColorJsonConverter.cs create mode 100644 UICatalog/Scenarios/Images.cs create mode 100644 UICatalog/Scenarios/TrueColors.cs diff --git a/Terminal.Gui/Configuration/AttributeJsonConverter.cs b/Terminal.Gui/Configuration/AttributeJsonConverter.cs index 6add9b8dfe..2bdd9c5ccf 100644 --- a/Terminal.Gui/Configuration/AttributeJsonConverter.cs +++ b/Terminal.Gui/Configuration/AttributeJsonConverter.cs @@ -76,10 +76,14 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J public override void Write (Utf8JsonWriter writer, Attribute value, JsonSerializerOptions options) { writer.WriteStartObject (); - writer.WritePropertyName ("Foreground"); + writer.WritePropertyName (nameof(Attribute.Foreground)); ColorJsonConverter.Instance.Write (writer, value.Foreground, options); - writer.WritePropertyName ("Background"); + writer.WritePropertyName (nameof (Attribute.Background)); ColorJsonConverter.Instance.Write (writer, value.Background, options); + writer.WritePropertyName (nameof (Attribute.TrueColorForeground)); + TrueColorJsonConverter.Instance.Write (writer, value.TrueColorForeground, options); + writer.WritePropertyName (nameof (Attribute.TrueColorBackground)); + TrueColorJsonConverter.Instance.Write (writer, value.TrueColorBackground, options); writer.WriteEndObject (); } } diff --git a/Terminal.Gui/Configuration/ColorJsonConverter.cs b/Terminal.Gui/Configuration/ColorJsonConverter.cs index 0a417ac423..7dfafe03d6 100644 --- a/Terminal.Gui/Configuration/ColorJsonConverter.cs +++ b/Terminal.Gui/Configuration/ColorJsonConverter.cs @@ -41,7 +41,7 @@ public override Color Read (ref Utf8JsonReader reader, Type typeToConvert, JsonS var r = int.Parse (match.Groups [1].Value); var g = int.Parse (match.Groups [2].Value); var b = int.Parse (match.Groups [3].Value); - return new TrueColor (r, g, b).ToConsoleColor (); + return TrueColor.ToConsoleColor(new TrueColor (r, g, b)); } else { throw new JsonException ($"Invalid Color: '{colorString}'"); } diff --git a/Terminal.Gui/Configuration/TrueColorJsonConverter.cs b/Terminal.Gui/Configuration/TrueColorJsonConverter.cs new file mode 100644 index 0000000000..bb1bd741cb --- /dev/null +++ b/Terminal.Gui/Configuration/TrueColorJsonConverter.cs @@ -0,0 +1,47 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Terminal.Gui { + /// + /// for . + /// + internal class TrueColorJsonConverter : JsonConverter { + private static TrueColorJsonConverter instance; + + /// + /// Singleton + /// + public static TrueColorJsonConverter Instance { + get { + if (instance == null) { + instance = new TrueColorJsonConverter (); + } + + return instance; + } + } + + public override TrueColor Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Check if the value is a string + if (reader.TokenType == JsonTokenType.String) { + // Get the color string + var colorString = reader.GetString (); + + if (!TrueColor.TryParse (colorString, out TrueColor? trueColor)) { + throw new JsonException ($"Invalid TrueColor: '{colorString}'"); + } + + return trueColor.Value; + } else { + throw new JsonException ($"Unexpected token when parsing TrueColor: {reader.TokenType}"); + } + } + + public override void Write (Utf8JsonWriter writer, TrueColor value, JsonSerializerOptions options) + { + writer.WriteStringValue (value.ToString ()); + } + } +} diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 3392f77e79..2eed1b2ebe 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -18,6 +18,8 @@ namespace Terminal.Gui; /// - for unit testing. /// public abstract class ConsoleDriver { + private bool useTrueColor = false; + /// /// Prepare the driver and set the key and mouse events handlers. /// @@ -103,6 +105,23 @@ public abstract class ConsoleDriver { /// public int Row { get; internal set; } + /// + /// Gets whether the supports TrueColor output. + /// + public virtual bool SupportsTrueColorOutput { get => false; } + + /// + /// Gets or sets whether the should use TrueColor output. + /// + /// + /// Can only be enabled if is true, indicating + /// that the supports it. + /// + public bool UseTrueColor { + get => useTrueColor; + set => this.useTrueColor = (value && SupportsTrueColorOutput); + } + /// /// Updates and to the specified column and row in . /// Used by and to determine where to add content. diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 92011d734e..0081a282f4 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -17,12 +17,21 @@ internal class WindowsConsole { public const int STD_INPUT_HANDLE = -10; public const int STD_ERROR_HANDLE = -12; + readonly static char [] SafeCursor = new [] { '\x1b', '7', '\x1b', '[', '0', ';', '0', 'H' }; + readonly static char [] RestoreCursor = new [] { '\x1b', '8' }; + readonly static char [] SendTrueColorFg = new [] { '\x1b', '[', '3', '8', ';', '2', ';' }; + readonly static char [] SendTrueColorBg = new [] { ';', '4', '8', ';', '2', ';' }; + readonly static char [] SendColorFg = new [] { '\x1b', '[', '3', '8', ';', '5', ';' }; + readonly static char [] SendColorBg = new [] { ';', '4', '8', ';', '5', ';' }; + IntPtr _inputHandle, _outputHandle; IntPtr _screenBuffer; readonly uint _originalConsoleMode; CursorVisibility? _initialCursorVisibility = null; CursorVisibility? _currentCursorVisibility = null; CursorVisibility? _pendingCursorVisibility = null; + readonly StringBuilder _stringBuilder = new StringBuilder (256 * 1024); + public WindowsConsole () { @@ -38,13 +47,64 @@ public WindowsConsole () CharInfo [] _originalStdOutChars; - public bool WriteToConsole (Size size, CharInfo [] charInfoBuffer, Coord coords, SmallRect window) + public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord coords, SmallRect window, bool useTrueColor) { if (_screenBuffer == IntPtr.Zero) { ReadFromConsoleOutput (size, coords, ref window); } - return WriteConsoleOutput (_screenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window); + if (!useTrueColor) { + int i = 0; + CharInfo [] ci = new CharInfo [charInfoBuffer.Length]; + foreach (ExtendedCharInfo info in charInfoBuffer) { + ci [i++] = new CharInfo () { + Char = new CharUnion () { UnicodeChar = info.Char }, + Attributes = (ushort)info.Attribute.Value + }; + } + return WriteConsoleOutput (_screenBuffer, ci, coords, new Coord () { X = window.Left, Y = window.Top }, ref window); + } + + + return WriteConsoleTrueColorOutput (charInfoBuffer); + } + + private bool WriteConsoleTrueColorOutput (ExtendedCharInfo [] charInfoBuffer) + { + _stringBuilder.Clear (); + + _stringBuilder.Append (SafeCursor); + + Attribute? prev = null; + foreach (var info in charInfoBuffer) { + var attr = info.Attribute; + + if (attr != prev) { + prev = attr; + + _stringBuilder.Append (SendTrueColorFg); + _stringBuilder.Append (attr.TrueColorForeground.Red); + _stringBuilder.Append (';'); + _stringBuilder.Append (attr.TrueColorForeground.Green); + _stringBuilder.Append (';'); + _stringBuilder.Append (attr.TrueColorForeground.Blue); + _stringBuilder.Append (SendTrueColorBg); + _stringBuilder.Append (attr.TrueColorBackground.Red); + _stringBuilder.Append (';'); + _stringBuilder.Append (attr.TrueColorBackground.Green); + _stringBuilder.Append (';'); + _stringBuilder.Append (attr.TrueColorBackground.Blue); + _stringBuilder.Append ('m'); + } + + _stringBuilder.Append (info.Char != '\x1b' ? info.Char : ' '); + } + + _stringBuilder.Append (RestoreCursor); + + string s = _stringBuilder.ToString (); + + return WriteConsole (_screenBuffer, s, (uint)(s.Length), out uint _, null); } public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window) @@ -461,6 +521,17 @@ public struct CharInfo { [FieldOffset (2)] public ushort Attributes; } + public struct ExtendedCharInfo { + public char Char { get; set; } + public Attribute Attribute { get; set; } + + public ExtendedCharInfo(char character, Attribute attribute) + { + Char = character; + Attribute = attribute; + } + } + [StructLayout (LayoutKind.Sequential)] public struct SmallRect { public short Left; @@ -551,6 +622,15 @@ static extern bool WriteConsoleOutput ( ref SmallRect lpWriteRegion ); + [DllImport ("kernel32.dll", EntryPoint = "WriteConsole", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool WriteConsole ( + IntPtr hConsoleOutput, + String lpbufer, + UInt32 NumberOfCharsToWriten, + out UInt32 lpNumberOfCharsWritten, + object lpReserved + ); + [DllImport ("kernel32.dll")] static extern bool SetConsoleCursorPosition (IntPtr hConsoleOutput, Coord dwCursorPosition); @@ -689,7 +769,7 @@ static extern Coord GetLargestConsoleWindowSize ( } internal class WindowsDriver : ConsoleDriver { - WindowsConsole.CharInfo [] _outputBuffer; + WindowsConsole.ExtendedCharInfo [] _outputBuffer; WindowsConsole.SmallRect _damageRegion; Action _keyHandler; Action _keyDownHandler; @@ -698,6 +778,8 @@ internal class WindowsDriver : ConsoleDriver { public WindowsConsole WinConsole { get; private set; } + public override bool SupportsTrueColorOutput => Environment.OSVersion.Version.Build >= 14931; + public WindowsDriver () { WinConsole = new WindowsConsole (); @@ -1371,11 +1453,15 @@ public override void AddRune (Rune systemRune) } Contents [Row, Col, 0] = rune.Value; Contents [Row, Col, 1] = CurrentAttribute.Value; + _outputBuffer [GetOutputBufferPosition()] = new WindowsConsole.ExtendedCharInfo ((char)rune.Value, CurrentAttribute); + WindowsConsole.SmallRect.Update (ref _damageRegion, (short)Col, (short)Row); if (Col > 0) { var left = new Rune (Contents [Row, Col - 1, 0]); if (left.GetColumns () > 1) { + int prevPosition = Row * Cols + (Col - 1); Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; + _outputBuffer [prevPosition].Char = (char)Rune.ReplacementChar.Value; } } @@ -1384,6 +1470,7 @@ public override void AddRune (Rune systemRune) Contents [Row, Col, 0] = Rune.ReplacementChar.Value; Contents [Row, Col, 1] = CurrentAttribute.Value; Contents [Row, Col, 2] = 1; + _outputBuffer [GetOutputBufferPosition ()] = new WindowsConsole.ExtendedCharInfo ((char)Rune.ReplacementChar.Value, CurrentAttribute); Col++; } @@ -1526,7 +1613,7 @@ public virtual void ResizeScreen () return; } - _outputBuffer = new WindowsConsole.CharInfo [Rows * Cols]; + _outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols]; Clip = new Rect (0, 0, Cols, Rows); _damageRegion = new WindowsConsole.SmallRect () { Top = 0, @@ -1548,10 +1635,10 @@ public override void UpdateOffScreen () for (int row = 0; row < Rows; row++) { for (int col = 0; col < Cols; col++) { int position = row * Cols + col; - _outputBuffer [position].Attributes = (ushort)new Attribute (Color.White, Color.Black).Value; ; - _outputBuffer [position].Char.UnicodeChar = ' '; - Contents [row, col, 0] = _outputBuffer [position].Char.UnicodeChar; - Contents [row, col, 1] = _outputBuffer [position].Attributes; + _outputBuffer [position].Attribute = Colors.TopLevel.Normal; + _outputBuffer [position].Char = ' '; + Contents [row, col, 0] = _outputBuffer [position].Char; + Contents [row, col, 1] = _outputBuffer [position].Attribute.Value; Contents [row, col, 2] = 0; WindowsConsole.SmallRect.Update (ref _damageRegion, (short)col, (short)row); } @@ -1576,28 +1663,7 @@ public override void UpdateScreen () Y = (short)Clip.Height }; - // BUGBUG: Temp hack to simplify - copy output buffer here instead of AddRune - WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - for (var row = 0; row < Rows; row++) { - for (var col = 0; col < Cols; col++) { - var rune = new Rune (Contents [row, col, 0]); - var position = row * Cols + col; - _outputBuffer [position].Char.UnicodeChar = (char)rune.Value; - _outputBuffer [position].Attributes = (ushort)Contents [row, col, 1]; - WindowsConsole.SmallRect.Update (ref _damageRegion, (short)col, (short)row); - var width = rune.GetColumns (); - while (width > 1 && col < Cols - 1) { - col++; - position = row * Cols + col; - _outputBuffer [position].Char.UnicodeChar = (char)0x00; - _outputBuffer [position].Attributes = (ushort)Contents [row, col, 1]; - WindowsConsole.SmallRect.Update (ref _damageRegion, (short)col, (short)row); - width--; - } - } - } - - WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion); + WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion, UseTrueColor); WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); } diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs index e661dfa33d..a4b2cdd6d6 100644 --- a/Terminal.Gui/Drawing/Color.cs +++ b/Terminal.Gui/Drawing/Color.cs @@ -1,8 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Text.Json.Serialization; -using System; +using System.Text.RegularExpressions; namespace Terminal.Gui { /// @@ -82,7 +85,26 @@ public enum Color { /// /// Indicates the RGB for true colors. /// - public class TrueColor { + public readonly struct TrueColor : IEquatable { + private static readonly ImmutableDictionary TrueColorToConsoleColorMap = new Dictionary () { + { new TrueColor (0,0,0),Color.Black }, + { new TrueColor (0, 0, 0x80),Color.Blue }, + { new TrueColor (0, 0x80, 0),Color.Green}, + { new TrueColor (0, 0x80, 0x80),Color.Cyan}, + { new TrueColor (0x80, 0, 0),Color.Red}, + { new TrueColor (0x80, 0, 0x80),Color.Magenta}, + { new TrueColor (0xC1, 0x9C, 0x00),Color.Brown}, // TODO confirm this + { new TrueColor (0xC0, 0xC0, 0xC0),Color.Gray}, + { new TrueColor (0x80, 0x80, 0x80),Color.DarkGray}, + { new TrueColor (0, 0, 0xFF),Color.BrightBlue}, + { new TrueColor (0, 0xFF, 0),Color.BrightGreen}, + { new TrueColor (0, 0xFF, 0xFF),Color.BrightCyan}, + { new TrueColor (0xFF, 0, 0),Color.BrightRed}, + { new TrueColor (0xFF, 0, 0xFF),Color.BrightMagenta }, + { new TrueColor (0xFF, 0xFF, 0),Color.BrightYellow}, + { new TrueColor (0xFF, 0xFF, 0xFF),Color.White}, + }.ToImmutableDictionary (); + /// /// Red color component. /// @@ -110,44 +132,94 @@ public TrueColor (int red, int green, int blue) } /// - /// Converts true color to console color. + /// Converts the provided text to a . /// - /// - public Color ToConsoleColor () - { - var trueColorMap = new Dictionary () { - { new TrueColor (0,0,0),Color.Black}, - { new TrueColor (0, 0, 0x80),Color.Blue}, - { new TrueColor (0, 0x80, 0),Color.Green}, - { new TrueColor (0, 0x80, 0x80),Color.Cyan}, - { new TrueColor (0x80, 0, 0),Color.Red}, - { new TrueColor (0x80, 0, 0x80),Color.Magenta}, - { new TrueColor (0xC1, 0x9C, 0x00),Color.Brown}, // TODO confirm this - { new TrueColor (0xC0, 0xC0, 0xC0),Color.Gray}, - { new TrueColor (0x80, 0x80, 0x80),Color.DarkGray}, - { new TrueColor (0, 0, 0xFF),Color.BrightBlue}, - { new TrueColor (0, 0xFF, 0),Color.BrightGreen}, - { new TrueColor (0, 0xFF, 0xFF),Color.BrightCyan}, - { new TrueColor (0xFF, 0, 0),Color.BrightRed}, - { new TrueColor (0xFF, 0, 0xFF),Color.BrightMagenta }, - { new TrueColor (0xFF, 0xFF, 0),Color.BrightYellow}, - { new TrueColor (0xFF, 0xFF, 0xFF),Color.White}, - }; - // Iterate over all colors in the map - var distances = trueColorMap.Select ( - k => Tuple.Create ( - // the candidate we are considering matching against (RGB) - k.Key, + /// The text to analyze. + /// The parsed value. + /// A boolean value indcating whether it was successful. + public static bool TryParse (string text, [NotNullWhen(true)] out TrueColor? trueColor) + { + // empty color + if ((text == null) || (text.Length == 0)) { + trueColor = null; + return false; + } - CalculateDistance (k.Key, this) - )); + // #RRGGBB or #RGB + if ((text [0] == '#') && + ((text.Length == 7) || (text.Length == 4))) { + if (text.Length == 7) { + var r = Convert.ToInt32 (text.Substring (1, 2), 16); + var g = Convert.ToInt32 (text.Substring (3, 2), 16); + var b = Convert.ToInt32 (text.Substring (5, 2), 16); + trueColor = new TrueColor (r, g, b); + } else { + var rText = char.ToString (text [1]); + var gText = char.ToString (text [2]); + var bText = char.ToString (text [3]); + + var r = Convert.ToInt32 (rText + rText, 16); + var g = Convert.ToInt32 (gText + gText, 16); + var b = Convert.ToInt32 (bText + bText, 16); + trueColor = new TrueColor (r, g, b); + } + return true; + } + + // rgb(XX,YY,ZZ) + var match = Regex.Match (text, @"rgb\((\d+),(\d+),(\d+)\)"); + if (match.Success) { + var r = int.Parse (match.Groups [1].Value); + var g = int.Parse (match.Groups [2].Value); + var b = int.Parse (match.Groups [3].Value); + trueColor = new TrueColor (r, g, b); + return true; + } - // get the closest - var match = distances.OrderBy (t => t.Item2).First (); - return trueColorMap [match.Item1]; + trueColor = null; + return false; + } + + /// + /// Converts a to a using a default mapping. + /// + /// The to convert. + /// + public static TrueColor FromConsoleColor (Color consoleColor) + { + switch (consoleColor) { + case Color.Black: return new TrueColor (0, 0, 0); + case Color.Blue: return new TrueColor (0, 0, 0x80); + case Color.Green: return new TrueColor (0, 0x80, 0); + case Color.Cyan: return new TrueColor (0, 0x80, 0x80); + case Color.Red: return new TrueColor (0x80, 0, 0); + case Color.Magenta: return new TrueColor (0x80, 0, 0x80); + case Color.Brown: return new TrueColor (0xC1, 0x9C, 0x00); // TODO confirm this + case Color.Gray: return new TrueColor (0xC0, 0xC0, 0xC0); + case Color.DarkGray: return new TrueColor (0x80, 0x80, 0x80); + case Color.BrightBlue: return new TrueColor (0, 0, 0xFF); + case Color.BrightGreen: return new TrueColor (0, 0xFF, 0); + case Color.BrightCyan: return new TrueColor (0, 0xFF, 0xFF); + case Color.BrightRed: return new TrueColor (0xFF, 0, 0); + case Color.BrightMagenta: return new TrueColor (0xFF, 0, 0xFF); + case Color.BrightYellow: return new TrueColor (0xFF, 0xFF, 0); + case Color.White: return new TrueColor (0xFF, 0xFF, 0xFF); + default: return new TrueColor (0x80, 0, 0); // TODO getting unknown color value (-1) during JSON serialization + // Don't know how to map it + }; + } + + /// + /// Converts the provided to using a default mapping. + /// + /// + /// + public static Color ToConsoleColor (TrueColor trueColor) + { + return TrueColorToConsoleColorMap.OrderBy (kv => CalculateDistance (kv.Key, trueColor)).First ().Value; } - private float CalculateDistance (TrueColor color1, TrueColor color2) + private static float CalculateDistance (TrueColor color1, TrueColor color2) { // use RGB distance return @@ -155,6 +227,45 @@ private float CalculateDistance (TrueColor color1, TrueColor color2) Math.Abs (color1.Green - color2.Green) + Math.Abs (color1.Blue - color2.Blue); } + + /// + public static bool operator == (TrueColor left, TrueColor right) + { + return left.Equals (right); + } + + /// + public static bool operator != (TrueColor left, TrueColor right) + { + return !left.Equals (right); + } + + /// + public override bool Equals (object obj) + { + return obj is TrueColor other && Equals (other); + } + + /// + public bool Equals (TrueColor other) + { + return + Red == other.Red && + Green == other.Green && + Blue == other.Blue; + } + + /// + public override int GetHashCode () + { + return HashCode.Combine (Red, Green, Blue); + } + + /// + public override string ToString () + { + return $"#{Red:X2}{Green:X2}{Blue:X2}"; + } } /// @@ -166,7 +277,13 @@ private float CalculateDistance (TrueColor color1, TrueColor color2) /// class to define color schemes that can be used in an application. /// [JsonConverter (typeof (AttributeJsonConverter))] - public struct Attribute { + public struct Attribute : IEquatable { + + /// + /// Default empty attribute. + /// + public static readonly Attribute Default = new Attribute (); + /// /// The -specific color value. If is /// the value of this property is invalid (typically because the Attribute was created before a driver was loaded) @@ -187,6 +304,18 @@ public struct Attribute { [JsonConverter (typeof (ColorJsonConverter))] public Color Background { get; private init; } + /// + /// Gets the TrueColor foreground color. + /// + [JsonConverter (typeof (TrueColorJsonConverter))] + public TrueColor TrueColorForeground { get; private init; } + + /// + /// Gets the TrueColor background color. + /// + [JsonConverter (typeof (TrueColorJsonConverter))] + public TrueColor TrueColorBackground { get; private init; } + /// /// Initializes a new instance with a platform-specific color value. /// @@ -204,6 +333,8 @@ internal Attribute (int value) Value = value; Foreground = foreground; Background = background; + TrueColorForeground = TrueColor.FromConsoleColor (foreground); + TrueColorBackground = TrueColor.FromConsoleColor (background); } /// @@ -214,9 +345,11 @@ internal Attribute (int value) /// Background public Attribute (int value, Color foreground, Color background) { - Value = value; Foreground = foreground; Background = background; + TrueColorForeground = TrueColor.FromConsoleColor (foreground); + TrueColorBackground = TrueColor.FromConsoleColor (background); + Value = value; Initialized = true; } @@ -227,11 +360,58 @@ public Attribute (int value, Color foreground, Color background) /// Background public Attribute (Color foreground = new Color (), Color background = new Color ()) { + Foreground = foreground; + Background = background; + TrueColorForeground = TrueColor.FromConsoleColor (foreground); + TrueColorBackground = TrueColor.FromConsoleColor (background); + var make = Make (foreground, background); Initialized = make.Initialized; Value = make.Value; + } + + /// + /// Initializes a new instance of the class. Populates + /// and . Also computes + /// and (basic console colors) in case + /// driver does not support true color rendering. + /// + /// + /// + public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground) + { + Foreground = TrueColor.ToConsoleColor (trueColorForeground); + Background = TrueColor.ToConsoleColor (trueColorBackground); + TrueColorForeground = trueColorForeground; + TrueColorBackground = trueColorBackground; + var make = Make (Foreground, Background); + Value = make.Value; + Initialized = make.Initialized; + } + + /// + /// + /// Initializes a new instance of the class. Populates + /// and with explicit + /// fallback values for and (in case + /// driver does not support true color rendering). + /// + /// If you do not want to manually specify the fallback colors use + /// instead which auto calculates these. + /// + /// True color RGB values you would like to use. + /// True color RGB values you would like to use. + /// Simple console color replacement if driver does not support true color. + /// Simple console color replacement if driver does not support true color. + public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground, Color foreground, Color background) + { Foreground = foreground; Background = background; + TrueColorForeground = trueColorForeground; + TrueColorBackground = trueColorBackground; + var make = Make (Foreground, Background); + Value = make.Value; + Initialized = make.Initialized; } /// @@ -240,16 +420,6 @@ public Attribute (int value, Color foreground, Color background) /// /// The color. public Attribute (Color color) : this (color, color) { } - - /// - public override bool Equals (object obj) - { - if (obj is Attribute other) { - return this.Value == other.Value; - } - - return false; - } /// /// Compares two attributes for equality. @@ -268,7 +438,23 @@ public override bool Equals (object obj) public static bool operator != (Attribute left, Attribute right) => !(left == right); /// - public override int GetHashCode () => this.Value.GetHashCode (); + public override bool Equals (object obj) + { + return obj is Attribute other && Equals (other); + } + + /// + public bool Equals (Attribute other) + { + return Value == other.Value && + Foreground == other.Foreground && + Background == other.Background && + TrueColorForeground == other.TrueColorForeground && + TrueColorBackground == other.TrueColorBackground; + } + + /// + public override int GetHashCode () => HashCode.Combine (Value, Foreground, Background, TrueColorForeground, TrueColorBackground); /// /// Creates an from the specified foreground and background colors. @@ -286,7 +472,7 @@ public static Attribute Make (Color foreground, Color background) // Create the attribute, but show it's not been initialized return new Attribute () { Initialized = false, - Foreground = foreground, + Foreground = foreground, Background = background }; } @@ -342,11 +528,11 @@ public override string ToString () /// [JsonConverter (typeof (ColorSchemeJsonConverter))] public class ColorScheme : IEquatable { - Attribute _normal = new Attribute (Color.White, Color.Black); - Attribute _focus = new Attribute (Color.White, Color.Black); - Attribute _hotNormal = new Attribute (Color.White, Color.Black); - Attribute _hotFocus = new Attribute (Color.White, Color.Black); - Attribute _disabled = new Attribute (Color.White, Color.Black); + Attribute _normal = Attribute.Default; + Attribute _focus = Attribute.Default; + Attribute _hotNormal = Attribute.Default; + Attribute _hotFocus = Attribute.Default; + Attribute _disabled = Attribute.Default; /// /// Used by and to track which ColorScheme diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs new file mode 100644 index 0000000000..65017e5359 --- /dev/null +++ b/UICatalog/Scenarios/Images.cs @@ -0,0 +1,136 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System; +using System.Collections.Concurrent; +using System.IO; +using Terminal.Gui; +using Attribute = Terminal.Gui.Attribute; + + + +namespace UICatalog.Scenarios { + [ScenarioMetadata (Name: "Images", Description: "Demonstration of how to render an image with/without true color support.")] + [ScenarioCategory ("Colors")] + public class Images : Scenario { + public override void Setup () + { + base.Setup (); + + var x = 0; + var y = 0; + + var canTrueColor = Application.Driver.SupportsTrueColorOutput; + + var lblDriverName = new Label ($"Current driver is {Application.Driver.GetType ().Name}") { + X = x, + Y = y++ + }; + Win.Add (lblDriverName); + y++; + + var cbSupportsTrueColor = new CheckBox ("Driver supports true color ") { + X = x, + Y = y++, + Checked = canTrueColor, + CanFocus = false + }; + Win.Add (cbSupportsTrueColor); + + var cbUseTrueColor = new CheckBox ("Use true color") { + X = x, + Y = y++, + Checked = Application.Driver.UseTrueColor, + Enabled = canTrueColor, + }; + cbUseTrueColor.Toggled += (_, evt) => Application.Driver.UseTrueColor = evt.NewValue ?? false; + Win.Add (cbUseTrueColor); + + var btnOpenImage = new Button ("Open Image") { + X = x, + Y = y++ + }; + Win.Add (btnOpenImage); + + var imageView = new ImageView () { + X = x, + Y = y++, + Width = Dim.Fill (), + Height = Dim.Fill (), + }; + Win.Add (imageView); + + + btnOpenImage.Clicked += (_, _) => { + var ofd = new OpenDialog ("Open Image") { AllowsMultipleSelection = false }; + Application.Run (ofd); + + if (ofd.Canceled) + return; + + var path = ofd.FilePaths [0]; + + if (string.IsNullOrWhiteSpace (path)) { + return; + } + + if (!File.Exists (path)) { + return; + } + + Image img; + + try { + img = Image.Load (File.ReadAllBytes (path)); + } catch (Exception ex) { + + MessageBox.ErrorQuery ("Could not open file", ex.Message, "Ok"); + return; + } + + imageView.SetImage (img); + }; + } + + class ImageView : View { + + private Image fullResImage; + private Image matchSize; + + ConcurrentDictionary cache = new ConcurrentDictionary (); + + internal void SetImage (Image image) + { + fullResImage = image; + this.SetNeedsDisplay (); + } + + public override void OnDrawContent(Rect bounds) + { + base.OnDrawContent (bounds); + + if (fullResImage == null) { + return; + } + + // if we have not got a cached resized image of this size + if (matchSize == null || bounds.Width != matchSize.Width || bounds.Height != matchSize.Height) { + + // generate one + matchSize = fullResImage.Clone (x => x.Resize (bounds.Width, bounds.Height)); + } + + for (int y = 0; y < bounds.Height; y++) { + for (int x = 0; x < bounds.Width; x++) { + var rgb = matchSize [x, y]; + + var attr = cache.GetOrAdd (rgb, (rgb) => new Attribute (new TrueColor (), new TrueColor (rgb.R, rgb.G, rgb.B))); + + Driver.SetAttribute (attr); + AddRune (x, y, (System.Text.Rune)' '); + } + } + } + } + } +} diff --git a/UICatalog/Scenarios/TrueColors.cs b/UICatalog/Scenarios/TrueColors.cs new file mode 100644 index 0000000000..df3afab63f --- /dev/null +++ b/UICatalog/Scenarios/TrueColors.cs @@ -0,0 +1,116 @@ +using System; +using Terminal.Gui; + +namespace UICatalog.Scenarios { + + [ScenarioMetadata (Name: "True Colors", Description: "Demonstration of true color support.")] + [ScenarioCategory ("Colors")] + public class TrueColors : Scenario { + + public override void Setup () + { + var x = 2; + var y = 1; + + var canTrueColor = true; // Application.Driver.SupportsTrueColorOutput; + + var lblDriverName = new Label ($"Current driver is {Application.Driver.GetType ().Name}") { + X = x, + Y = y++ + }; + Win.Add (lblDriverName); + y++; + + var cbSupportsTrueColor = new CheckBox ("Driver supports true color ") { + X = x, + Y = y++, + Checked = canTrueColor, + CanFocus = false + }; + Win.Add (cbSupportsTrueColor); + + var cbUseTrueColor = new CheckBox ("Use true color") { + X = x, + Y = y++, + Checked = Application.Driver.UseTrueColor, + Enabled = canTrueColor, + }; + cbUseTrueColor.Toggled += (_, evt) => Application.Driver.UseTrueColor = evt.NewValue ?? false; + Win.Add (cbUseTrueColor); + + y += 2; + SetupGradient ("Red gradient", x, ref y, (i) => new TrueColor (i, 0, 0)); + SetupGradient ("Green gradient", x, ref y, (i) => new TrueColor (0, i, 0)); + SetupGradient ("Blue gradient", x, ref y, (i) => new TrueColor (0, 0, i)); + SetupGradient ("Yellow gradient", x, ref y, (i) => new TrueColor (i, i, 0)); + SetupGradient ("Magenta gradient", x, ref y, (i) => new TrueColor (i, 0, i)); + SetupGradient ("Cyan gradient", x, ref y, (i) => new TrueColor (0, i, i)); + SetupGradient ("Gray gradient", x, ref y, (i) => new TrueColor (i, i, i)); + + Win.Add (new Label ("Mouse over to get the gradient view color:") { + X = Pos.AnchorEnd (44), + Y = 2 + }); + Win.Add (new Label ("Red:") { + X = Pos.AnchorEnd (44), + Y = 4 + }); + Win.Add (new Label ("Green:") { + X = Pos.AnchorEnd (44), + Y = 5 + }); + Win.Add (new Label ("Blue:") { + X = Pos.AnchorEnd (44), + Y = 6 + }); + + var lblRed = new Label ("na") { + X = Pos.AnchorEnd (32), + Y = 4 + }; + Win.Add (lblRed); + var lblGreen = new Label ("na") { + X = Pos.AnchorEnd (32), + Y = 5 + }; + Win.Add (lblGreen); + var lblBlue = new Label ("na") { + X = Pos.AnchorEnd (32), + Y = 6 + }; + Win.Add (lblBlue); + + Application.RootMouseEvent = (e) => { + var normal = e.View.GetNormalColor (); + if (e.View != null) { + lblRed.Text = normal.TrueColorForeground.Red.ToString (); + lblGreen.Text = normal.TrueColorForeground.Green.ToString (); + lblBlue.Text = normal.TrueColorForeground.Blue.ToString (); + } + }; + } + + private void SetupGradient (string name, int x, ref int y, Func colorFunc) + { + var gradient = new Label (name) { + X = x, + Y = y++, + }; + Win.Add (gradient); + for (int dx = x, i = 0; i <= 256; i += 4) { + var l = new Label (" ") { + X = dx++, + Y = y, + ColorScheme = new ColorScheme () { + Normal = new Terminal.Gui.Attribute ( + colorFunc (i > 255 ? 255 : i), + colorFunc (i > 255 ? 255 : i) + ) + } + }; + Win.Add (l); + } + y += 2; + } + } +} diff --git a/UnitTests/Configuration/JsonConverterTests.cs b/UnitTests/Configuration/JsonConverterTests.cs index 51bf898cd8..a3e4ac43f2 100644 --- a/UnitTests/Configuration/JsonConverterTests.cs +++ b/UnitTests/Configuration/JsonConverterTests.cs @@ -1,12 +1,5 @@ -using Xunit; -using Terminal.Gui; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Text.Json; -using Attribute = Terminal.Gui.Attribute; +using System.Text.Json; +using Xunit; namespace Terminal.Gui.ConfigurationTests { public class ColorJsonConverterTests { @@ -132,6 +125,56 @@ public void TestDeserializeColor_BrightRed () } } + public class TrueColorJsonConverterTests { + [Theory] + [InlineData (0,0,0, "\"#000000\"")] + public void SerializesToHexCode (int r, int g, int b, string expected) + { + // Arrange + + // Act + var actual = JsonSerializer.Serialize (new TrueColor (r, g, b), new JsonSerializerOptions { + Converters = { new TrueColorJsonConverter () } + }); + + //Assert + Assert.Equal (expected, actual); + + } + + [Theory] + [InlineData ("\"#000000\"", 0, 0, 0)] + public void DeserializesFromHexCode (string hexCode, int r, int g, int b) + { + // Arrange + TrueColor expected = new TrueColor (r, g, b); + + // Act + var actual = JsonSerializer.Deserialize (hexCode, new JsonSerializerOptions { + Converters = { new TrueColorJsonConverter () } + }); + + //Assert + Assert.Equal (expected, actual); + } + + [Theory] + [InlineData ("\"rgb(0,0,0)\"", 0, 0, 0)] + public void DeserializesFromRgb (string rgb, int r, int g, int b) + { + // Arrange + TrueColor expected = new TrueColor (r, g, b); + + // Act + var actual = JsonSerializer.Deserialize (rgb, new JsonSerializerOptions { + Converters = { new TrueColorJsonConverter () } + }); + + //Assert + Assert.Equal (expected, actual); + } + } + public class AttributeJsonConverterTests { [Fact, AutoInitShutdown] public void TestDeserialize () From 40d1042eb6bc78e6b6eb7669994e639de3c298ba Mon Sep 17 00:00:00 2001 From: adstep Date: Tue, 27 Jun 2023 19:57:13 -0700 Subject: [PATCH 62/99] add colorconverter changes --- Terminal.Gui/Configuration/AttributeJsonConverter.cs | 8 +++++++- Terminal.Gui/Drawing/Color.cs | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Terminal.Gui/Configuration/AttributeJsonConverter.cs b/Terminal.Gui/Configuration/AttributeJsonConverter.cs index 2bdd9c5ccf..34709c109d 100644 --- a/Terminal.Gui/Configuration/AttributeJsonConverter.cs +++ b/Terminal.Gui/Configuration/AttributeJsonConverter.cs @@ -32,6 +32,7 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J Attribute attribute = new Attribute (); Color foreground = (Color)(-1); Color background = (Color)(-1); + int valuePair = 0; while (reader.Read ()) { if (reader.TokenType == JsonTokenType.EndObject) { if (foreground == (Color)(-1) || background == (Color)(-1)) { @@ -48,6 +49,8 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J reader.Read (); string color = $"\"{reader.GetString ()}\""; + valuePair++; + switch (propertyName.ToLower ()) { case "foreground": foreground = JsonSerializer.Deserialize (color, options); @@ -68,7 +71,10 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J throw new JsonException ($"Unknown Attribute property {propertyName}."); } - attribute = new Attribute (foreground, background); + if (valuePair == 2) { + attribute = new Attribute (foreground, background); + valuePair = 0; + } } throw new JsonException (); } diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs index a4b2cdd6d6..4a0585d8af 100644 --- a/Terminal.Gui/Drawing/Color.cs +++ b/Terminal.Gui/Drawing/Color.cs @@ -204,8 +204,7 @@ public static TrueColor FromConsoleColor (Color consoleColor) case Color.BrightMagenta: return new TrueColor (0xFF, 0, 0xFF); case Color.BrightYellow: return new TrueColor (0xFF, 0xFF, 0); case Color.White: return new TrueColor (0xFF, 0xFF, 0xFF); - default: return new TrueColor (0x80, 0, 0); // TODO getting unknown color value (-1) during JSON serialization - // Don't know how to map it + default: throw new ArgumentException ($"No supported TrueColor mapping for '{consoleColor}'."); }; } From 2f729181a4d9e5258e8624f138ffec6d4d34ae48 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 5 Jul 2023 17:12:12 -0600 Subject: [PATCH 63/99] fixed merged v2_develop --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index a1f2d9a5a7..a4da7c1b78 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -867,7 +867,6 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) break; case WindowsConsole.EventType.Focus: - keyModifiers = null; break; } } From c547014dbda0cd2b71dd338e6a6d2fda873a28a7 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Thu, 6 Jul 2023 14:56:23 -0600 Subject: [PATCH 64/99] Fixing merge bugs --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 278 +------------------ Terminal.Gui/Drawing/Color.cs | 38 ++- Terminal.Gui/Views/TextField.cs | 2 +- Terminal.Gui/Views/TextView.cs | 2 +- UnitTests/ConsoleDrivers/AttributeTests.cs | 42 +++ UnitTests/UnitTests.csproj | 1 + 6 files changed, 83 insertions(+), 280 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index dcbc60ed74..eef6208603 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -4,6 +4,7 @@ using System.Text; using System; using System.Diagnostics; +using static Terminal.Gui.ColorScheme; namespace Terminal.Gui; @@ -114,171 +115,8 @@ public abstract class ConsoleDriver { public int Row { get; internal set; } /// - /// Creates a new instance, initialized with the values from . - /// - /// The scheme to initialize the new instance with. - public ColorScheme (ColorScheme scheme) : base () - { - if (scheme != null) { - _normal = scheme.Normal; - _focus = scheme.Focus; - _hotNormal = scheme.HotNormal; - _disabled = scheme.Disabled; - _hotFocus = scheme.HotFocus; - } - } - - /// - /// The foreground and background color for text when the view is not focused, hot, or disabled. - /// - public Attribute Normal { - get { return _normal; } - set { - if (!value.HasValidColors) { - return; - } - _normal = value; - } - } - - /// - /// The foreground and background color for text when the view has the focus. - /// - public Attribute Focus { - get { return _focus; } - set { - if (!value.HasValidColors) { - return; - } - _focus = value; - } - } - - /// - /// The foreground and background color for text when the view is highlighted (hot). - /// - public Attribute HotNormal { - get { return _hotNormal; } - set { - if (!value.HasValidColors) { - return; - } - _hotNormal = value; - } - } - - /// - /// The foreground and background color for text when the view is highlighted (hot) and has focus. - /// - public Attribute HotFocus { - get { return _hotFocus; } - set { - if (!value.HasValidColors) { - return; - } - _hotFocus = value; - } - } - - /// - /// The default foreground and background color for text, when the view is disabled. - /// - public Attribute Disabled { - get { return _disabled; } - set { - if (!value.HasValidColors) { - return; - } - _disabled = value; - } - } - - /// - /// Compares two objects for equality. - /// - /// - /// true if the two objects are equal - public override bool Equals (object obj) - { - return Equals (obj as ColorScheme); - } - - /// - /// Compares two objects for equality. - /// - /// - /// true if the two objects are equal - public bool Equals (ColorScheme other) - { - return other != null && - EqualityComparer.Default.Equals (_normal, other._normal) && - EqualityComparer.Default.Equals (_focus, other._focus) && - EqualityComparer.Default.Equals (_hotNormal, other._hotNormal) && - EqualityComparer.Default.Equals (_hotFocus, other._hotFocus) && - EqualityComparer.Default.Equals (_disabled, other._disabled); - } - - /// - /// Returns a hashcode for this instance. - /// - /// hashcode for this instance - public override int GetHashCode () - { - int hashCode = -1242460230; - hashCode = hashCode * -1521134295 + _normal.GetHashCode (); - hashCode = hashCode * -1521134295 + _focus.GetHashCode (); - hashCode = hashCode * -1521134295 + _hotNormal.GetHashCode (); - hashCode = hashCode * -1521134295 + _hotFocus.GetHashCode (); - hashCode = hashCode * -1521134295 + _disabled.GetHashCode (); - return hashCode; - } - - /// - /// Compares two objects for equality. - /// - /// - /// - /// true if the two objects are equivalent - public static bool operator == (ColorScheme left, ColorScheme right) - { - return EqualityComparer.Default.Equals (left, right); - } - - /// - /// Compares two objects for inequality. - /// - /// - /// - /// true if the two objects are not equivalent - public static bool operator != (ColorScheme left, ColorScheme right) - { - return !(left == right); - } - - internal void Initialize () - { - // If the new scheme was created before a driver was loaded, we need to re-make - // the attributes - if (!_normal.Initialized) { - _normal = new Attribute (_normal.Foreground, _normal.Background); - } - if (!_focus.Initialized) { - _focus = new Attribute (_focus.Foreground, _focus.Background); - } - if (!_hotNormal.Initialized) { - _hotNormal = new Attribute (_hotNormal.Foreground, _hotNormal.Background); - } - if (!_hotFocus.Initialized) { - _hotFocus = new Attribute (_hotFocus.Foreground, _hotFocus.Background); - } - if (!_disabled.Initialized) { - _disabled = new Attribute (_disabled.Foreground, _disabled.Background); - } - } - } - - /// - /// The default s for the application. + /// Updates and to the specified column and row in . + /// Used by and to determine where to add content. /// /// /// @@ -356,108 +194,14 @@ public void AddStr (string str) /// /// Tests whether the specified coordinate are valid for drawing. /// - - - /// - /// ConsoleDriver is an abstract class that defines the requirements for a console driver. - /// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. - /// - public abstract class ConsoleDriver { - - /// - /// The handler fired when the terminal is resized. - /// - protected Action TerminalResized; - - /// - /// The current number of columns in the terminal. - /// - public abstract int Cols { get; } - - /// - /// The current number of rows in the terminal. - /// - public abstract int Rows { get; } - - /// - /// The current left in the terminal. - /// - public abstract int Left { get; } - - /// - /// The current top in the terminal. - /// - public abstract int Top { get; } - - /// - /// Get the operation system clipboard. - /// - public abstract IClipboard Clipboard { get; } - - /// - /// - /// If (the default) the height of the Terminal.Gui application () - /// tracks to the height of the visible console view when the console is resized. In this case - /// scrolling in the console will be disabled and all will remain visible. - /// - /// - /// If then height of the Terminal.Gui application only tracks - /// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). - /// In this case console scrolling is enabled and the contents ( high) will scroll - /// as the console scrolls. - /// - /// - /// - /// NOTE: This functionaliy is currently broken on Windows Terminal. - /// - public abstract bool EnableConsoleScrolling { get; set; } - - /// - /// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - /// - public virtual int [,,] Contents { get; } - - /// - /// Initializes the driver - /// - /// Method to invoke when the terminal is resized. - public abstract void Init (Action terminalResized); - /// - /// Moves the cursor to the specified column and row. - /// - /// Column to move the cursor to. - /// Row to move the cursor to. - public abstract void Move (int col, int row); - - /// - /// Tests if the specified rune is supported by the driver. - /// - /// - /// if the rune can be properly presented; if the driver - /// does not support displaying this rune. - public virtual bool IsRuneSupported (Rune rune) - { - if (rune.Value > RuneExtensions.MaxUnicodeCodePoint) { - return false; - } - return true; - } - - /// - /// Adds the specified rune to the display at the current cursor position. - /// - /// Rune to add. - public abstract void AddRune (Rune rune); - - /// - /// Ensures that the column and line are in a valid range from the size of the driver. - /// - /// The column. - /// The row. - /// The clip. - /// trueif it's a valid range,falseotherwise. - public bool IsValidContent (int col, int row, Rect clip) => - col >= 0 && row >= 0 && col < Cols && row < Rows && clip.Contains (col, row); + /// The column. + /// The row. + /// if the coordinate is outside of the + /// screen bounds or outside of . otherwise. + public bool IsValidLocation (int col, int row) => + col >= 0 && row >= 0 && + col < Cols && row < Rows && + Clip.Contains (col, row); /// /// Gets or sets the clip rectangle that and are diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs index e661dfa33d..b44478c3a0 100644 --- a/Terminal.Gui/Drawing/Color.cs +++ b/Terminal.Gui/Drawing/Color.cs @@ -240,17 +240,24 @@ public Attribute (int value, Color foreground, Color background) /// /// The color. public Attribute (Color color) : this (color, color) { } - + /// public override bool Equals (object obj) { - if (obj is Attribute other) { - return this.Value == other.Value; - } + return obj is Attribute other && Equals (other); + } - return false; + /// + public bool Equals (Attribute other) + { + return Value == other.Value && + Foreground == other.Foreground && + Background == other.Background; } + /// + public override int GetHashCode () => HashCode.Combine (Value, Foreground, Background); + /// /// Compares two attributes for equality. /// @@ -267,9 +274,6 @@ public override bool Equals (object obj) /// public static bool operator != (Attribute left, Attribute right) => !(left == right); - /// - public override int GetHashCode () => this.Value.GetHashCode (); - /// /// Creates an from the specified foreground and background colors. /// @@ -286,7 +290,7 @@ public static Attribute Make (Color foreground, Color background) // Create the attribute, but show it's not been initialized return new Attribute () { Initialized = false, - Foreground = foreground, + Foreground = foreground, Background = background }; } @@ -340,7 +344,6 @@ public override string ToString () /// /// See also: . /// - [JsonConverter (typeof (ColorSchemeJsonConverter))] public class ColorScheme : IEquatable { Attribute _normal = new Attribute (Color.White, Color.Black); Attribute _focus = new Attribute (Color.White, Color.Black); @@ -362,7 +365,7 @@ public ColorScheme () { } /// /// Creates a new instance, initialized with the values from . /// - /// The scheme to initlize the new instance with. + /// The scheme to initialize the new instance with. public ColorScheme (ColorScheme scheme) : base () { if (scheme != null) { @@ -374,6 +377,19 @@ public ColorScheme (ColorScheme scheme) : base () } } + /// + /// Creates a new instance, initialized with the values from . + /// + /// The attribute to initialize the new instance with. + public ColorScheme (Attribute attribute) + { + _normal = attribute; + _focus = attribute; + _hotNormal = attribute; + _disabled = attribute; + _hotFocus = attribute; + } + /// /// The foreground and background color for text when the view is not focused, hot, or disabled. /// diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 439a4dfa97..d9f3d29f1d 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -567,7 +567,7 @@ void Adjust () return; int offB = OffSetBackground (); - bool need = !_needsDisplay.IsEmpty || !Used; + bool need = NeedsDisplay || !Used; if (_point < _first) { _first = _point; need = true; diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 7bc88f64e3..5a1f49293b 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3239,7 +3239,7 @@ void Adjust () { var offB = OffSetBackground (); var line = GetCurrentLine (); - bool need = !_needsDisplay.IsEmpty || _wrapNeeded || !Used; + bool need = NeedsDisplay || _wrapNeeded || !Used; var tSize = TextModel.DisplaySize (line, -1, -1, false, TabWidth); var dSize = TextModel.DisplaySize (line, _leftColumn, _currentColumn, true, TabWidth); if (!_wordWrap && _currentColumn < _leftColumn) { diff --git a/UnitTests/ConsoleDrivers/AttributeTests.cs b/UnitTests/ConsoleDrivers/AttributeTests.cs index f9331c0090..4f46c1a84c 100644 --- a/UnitTests/ConsoleDrivers/AttributeTests.cs +++ b/UnitTests/ConsoleDrivers/AttributeTests.cs @@ -196,5 +196,47 @@ public void IsValid_Tests () attr = new Attribute ((Color)(-1), (Color)(-1)); Assert.False (attr.HasValidColors); } + + [Fact] + public void Equals_NotInitialized() + { + var attr1 = new Attribute (Color.Red, Color.Green); + var attr2 = new Attribute (Color.Red, Color.Green); + + Assert.True (attr1.Equals (attr2)); + Assert.True (attr2.Equals (attr1)); + } + + [Fact] + public void NotEquals_NotInitialized () + { + var attr1 = new Attribute (Color.Red, Color.Green); + var attr2 = new Attribute (Color.Green, Color.Red); + + Assert.False (attr1.Equals (attr2)); + Assert.False (attr2.Equals (attr1)); + } + + [Fact, AutoInitShutdown] + public void Equals_Initialized () + { + Assert.NotNull(Application.Driver); + + var attr1 = new Attribute (Color.Red, Color.Green); + var attr2 = new Attribute (Color.Red, Color.Green); + + Assert.True (attr1.Equals (attr2)); + Assert.True (attr2.Equals (attr1)); + } + + [Fact,AutoInitShutdown] + public void NotEquals_Initialized () + { + var attr1 = new Attribute (Color.Red, Color.Green); + var attr2 = new Attribute (Color.Green, Color.Red); + + Assert.False (attr1.Equals (attr2)); + Assert.False (attr2.Equals (attr1)); + } } } diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index 57747a3ba0..5c2b9ec7b0 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -57,5 +57,6 @@ False + C:\Users\charlie\s\gui-cs\Terminal.Gui\UnitTests\bin\Debug\net7.0\fine-code-coverage\coverage-tool-output\UnitTests-fcc-mscodecoverage-generated.runsettings \ No newline at end of file From b071793a6b034ccb47e579e1b04933f62935ad02 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Thu, 6 Jul 2023 15:22:41 -0600 Subject: [PATCH 65/99] Fixed merge bugs --- Terminal.Gui/Drawing/Color.cs | 3 +- ...l_TIGGER_2023-07-06.14_58_23.cobertura.xml | 251649 +++++++++++++++ UnitTests/UnitTests.csproj | 1 - 3 files changed, 251651 insertions(+), 2 deletions(-) create mode 100644 TestResults/15ea9649-0d81-41ee-aae3-4f1f2c15810a/charl_TIGGER_2023-07-06.14_58_23.cobertura.xml diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs index b44478c3a0..3c3f537eda 100644 --- a/Terminal.Gui/Drawing/Color.cs +++ b/Terminal.Gui/Drawing/Color.cs @@ -344,6 +344,7 @@ public override string ToString () /// /// See also: . /// + [JsonConverter (typeof (ColorSchemeJsonConverter))] public class ColorScheme : IEquatable { Attribute _normal = new Attribute (Color.White, Color.Black); Attribute _focus = new Attribute (Color.White, Color.Black); @@ -472,7 +473,7 @@ public override bool Equals (object obj) /// true if the two objects are equal public bool Equals (ColorScheme other) { - return other != null && + return other != null && EqualityComparer.Default.Equals (_normal, other._normal) && EqualityComparer.Default.Equals (_focus, other._focus) && EqualityComparer.Default.Equals (_hotNormal, other._hotNormal) && diff --git a/TestResults/15ea9649-0d81-41ee-aae3-4f1f2c15810a/charl_TIGGER_2023-07-06.14_58_23.cobertura.xml b/TestResults/15ea9649-0d81-41ee-aae3-4f1f2c15810a/charl_TIGGER_2023-07-06.14_58_23.cobertura.xml new file mode 100644 index 0000000000..ff0872dddd --- /dev/null +++ b/TestResults/15ea9649-0d81-41ee-aae3-4f1f2c15810a/charl_TIGGER_2023-07-06.14_58_23.cobertura.xml @@ -0,0 +1,251649 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index 5c2b9ec7b0..57747a3ba0 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -57,6 +57,5 @@ False - C:\Users\charlie\s\gui-cs\Terminal.Gui\UnitTests\bin\Debug\net7.0\fine-code-coverage\coverage-tool-output\UnitTests-fcc-mscodecoverage-generated.runsettings \ No newline at end of file From 200bdc4cdf04b921e0516b570dd4e9aa7e386ed7 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 10 Jul 2023 09:50:23 -0700 Subject: [PATCH 66/99] Fixed merge bugs - all unit tests pass --- .../Configuration/AttributeJsonConverter.cs | 29 +++--- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 40 +++++---- .../ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs | 21 +++++ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 48 ++++------ Terminal.Gui/Drawing/Color.cs | 89 ++++++++++--------- UICatalog/Scenarios/Images.cs | 2 +- UICatalog/Scenarios/TrueColors.cs | 8 +- .../Configuration/ConfigurationMangerTests.cs | 4 +- UnitTests/Configuration/JsonConverterTests.cs | 2 +- UnitTests/UnitTests.csproj | 8 +- UnitTests/Views/TreeViewTests.cs | 9 +- UnitTests/xunit.runner.json | 7 +- 12 files changed, 143 insertions(+), 124 deletions(-) diff --git a/Terminal.Gui/Configuration/AttributeJsonConverter.cs b/Terminal.Gui/Configuration/AttributeJsonConverter.cs index 0ac996b81e..390c70a0f6 100644 --- a/Terminal.Gui/Configuration/AttributeJsonConverter.cs +++ b/Terminal.Gui/Configuration/AttributeJsonConverter.cs @@ -32,10 +32,11 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J Attribute attribute = new Attribute (); Color foreground = (Color)(-1); Color background = (Color)(-1); - int valuePair = 0; + TrueColor? trueColorForeground = null; + TrueColor? trueColorBackground = null; while (reader.Read ()) { if (reader.TokenType == JsonTokenType.EndObject) { - if (foreground == (Color)(-1) || background == (Color)(-1)) { + if (!attribute.TrueColorForeground.HasValue || !attribute.TrueColorBackground.HasValue) { throw new JsonException ($"Both Foreground and Background colors must be provided."); } return attribute; @@ -49,8 +50,6 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J reader.Read (); string color = $"\"{reader.GetString ()}\""; - valuePair++; - switch (propertyName.ToLower ()) { case "foreground": foreground = JsonSerializer.Deserialize (color, options); @@ -58,6 +57,12 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J case "background": background = JsonSerializer.Deserialize (color, options); break; + case "truecolorforeground": + trueColorForeground = JsonSerializer.Deserialize (color, options); + break; + case "truecolorbackground": + trueColorBackground = JsonSerializer.Deserialize (color, options); + break; //case "Bright": // attribute.Bright = reader.GetBoolean (); // break; @@ -71,9 +76,11 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J throw new JsonException ($"Unknown Attribute property {propertyName}."); } - if (valuePair == 2) { + if (foreground != (Color)(-1) && background != (Color)(-1)) { attribute = new Attribute (foreground, background); - valuePair = 0; + } + if (trueColorForeground.HasValue && trueColorBackground.HasValue) { + attribute = new Attribute (trueColorForeground, trueColorBackground); } } throw new JsonException (); @@ -86,10 +93,12 @@ public override void Write (Utf8JsonWriter writer, Attribute value, JsonSerializ ColorJsonConverter.Instance.Write (writer, value.Foreground, options); writer.WritePropertyName (nameof (Attribute.Background)); ColorJsonConverter.Instance.Write (writer, value.Background, options); - writer.WritePropertyName (nameof (Attribute.TrueColorForeground)); - TrueColorJsonConverter.Instance.Write (writer, value.TrueColorForeground, options); - writer.WritePropertyName (nameof (Attribute.TrueColorBackground)); - TrueColorJsonConverter.Instance.Write (writer, value.TrueColorBackground, options); + if (value.TrueColorForeground.HasValue && value.TrueColorBackground.HasValue) { + writer.WritePropertyName (nameof (Attribute.TrueColorForeground)); + TrueColorJsonConverter.Instance.Write (writer, value.TrueColorForeground.Value, options); + writer.WritePropertyName (nameof (Attribute.TrueColorBackground)); + TrueColorJsonConverter.Instance.Write (writer, value.TrueColorBackground.Value, options); + } writer.WriteEndObject (); } } diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 31c7486b8b..dd0576bc4b 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -20,8 +20,6 @@ namespace Terminal.Gui; /// - for unit testing. /// public abstract class ConsoleDriver { - private bool useTrueColor = false; - /// /// Prepare the driver and set the key and mouse events handlers. /// @@ -116,23 +114,6 @@ public abstract class ConsoleDriver { /// public int Row { get; internal set; } - /// - /// Gets whether the supports TrueColor output. - /// - public virtual bool SupportsTrueColorOutput { get => false; } - - /// - /// Gets or sets whether the should use TrueColor output. - /// - /// - /// Can only be enabled if is true, indicating - /// that the supports it. - /// - public bool UseTrueColor { - get => useTrueColor; - set => this.useTrueColor = (value && SupportsTrueColorOutput); - } - /// /// Updates and to the specified column and row in . /// Used by and to determine where to add content. @@ -274,6 +255,27 @@ public Rect Clip { #region Color Handling + + /// + /// Gets whether the supports TrueColor output. + /// + public virtual bool SupportsTrueColor { get => false; } + + private bool _useTrueColor = true; + + // TODO: Make this a ConfiguationManager setting on Application + /// + /// Gets or sets whether the should use TrueColor output. + /// + /// + /// Can only be enabled if is true, indicating + /// that the supports it. + /// + public bool UseTrueColor { + get => _useTrueColor && SupportsTrueColor; + set => this._useTrueColor = (value && SupportsTrueColor); + } + Attribute _currentAttribute; /// diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs index d105f36085..3468dd3400 100644 --- a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs @@ -226,6 +226,27 @@ public enum DECSCUSR_Style { /// https://terminalguide.namepad.de/seq/csi_sm/ /// public static string CSI_SetGraphicsRendition (params int [] parameters) => $"{CSI}{string.Join (";", parameters)}m"; + + /// + /// ESC[38;5;{id}m - Set foreground color (256 colors) + /// + public static string CSI_SetForegroundColor (int id) => $"{CSI}38;5;{id}m"; + + /// + /// ESC[48;5;{id}m - Set background color (256 colors) + /// + public static string CSI_SetBackgroundColor (int id) => $"{CSI}48;5;{id}m"; + + /// + /// ESC[38;2;{r};{g};{b}m Set foreground color as RGB. + /// + public static string CSI_SetForegroundColorRGB (int r, int g, int b) => $"{CSI}38;2;{r};{g};{b}m"; + + /// + /// ESC[48;2;{r};{g};{b}m Set background color as RGB. + /// + public static string CSI_SetBackgroundColorRGB (int r, int g, int b) => $"{CSI}48;2;{r};{g};{b}m"; + #endregion #region Requests diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index d61ef0796c..4bcc91d685 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -17,13 +17,6 @@ internal class WindowsConsole { public const int STD_INPUT_HANDLE = -10; public const int STD_ERROR_HANDLE = -12; - readonly static char [] SafeCursor = new [] { '\x1b', '7', '\x1b', '[', '0', ';', '0', 'H' }; - readonly static char [] RestoreCursor = new [] { '\x1b', '8' }; - readonly static char [] SendTrueColorFg = new [] { '\x1b', '[', '3', '8', ';', '2', ';' }; - readonly static char [] SendTrueColorBg = new [] { ';', '4', '8', ';', '2', ';' }; - readonly static char [] SendColorFg = new [] { '\x1b', '[', '3', '8', ';', '5', ';' }; - readonly static char [] SendColorBg = new [] { ';', '4', '8', ';', '5', ';' }; - IntPtr _inputHandle, _outputHandle; IntPtr _screenBuffer; readonly uint _originalConsoleMode; @@ -32,7 +25,6 @@ internal class WindowsConsole { CursorVisibility? _pendingCursorVisibility = null; readonly StringBuilder _stringBuilder = new StringBuilder (256 * 1024); - public WindowsConsole () { _inputHandle = GetStdHandle (STD_INPUT_HANDLE); @@ -73,7 +65,8 @@ private bool WriteConsoleTrueColorOutput (ExtendedCharInfo [] charInfoBuffer) { _stringBuilder.Clear (); - _stringBuilder.Append (SafeCursor); + _stringBuilder.Append (EscSeqUtils.CSI_SaveCursorPosition); + _stringBuilder.Append (EscSeqUtils.CSI_SetCursorPosition (0, 0)); Attribute? prev = null; foreach (var info in charInfoBuffer) { @@ -81,29 +74,18 @@ private bool WriteConsoleTrueColorOutput (ExtendedCharInfo [] charInfoBuffer) if (attr != prev) { prev = attr; - - _stringBuilder.Append (SendTrueColorFg); - _stringBuilder.Append (attr.TrueColorForeground.Red); - _stringBuilder.Append (';'); - _stringBuilder.Append (attr.TrueColorForeground.Green); - _stringBuilder.Append (';'); - _stringBuilder.Append (attr.TrueColorForeground.Blue); - _stringBuilder.Append (SendTrueColorBg); - _stringBuilder.Append (attr.TrueColorBackground.Red); - _stringBuilder.Append (';'); - _stringBuilder.Append (attr.TrueColorBackground.Green); - _stringBuilder.Append (';'); - _stringBuilder.Append (attr.TrueColorBackground.Blue); - _stringBuilder.Append ('m'); + + _stringBuilder.Append (EscSeqUtils.CSI_SetForegroundColorRGB (attr.TrueColorForeground.Value.Red, attr.TrueColorForeground.Value.Green, attr.TrueColorForeground.Value.Blue)); + _stringBuilder.Append (EscSeqUtils.CSI_SetBackgroundColorRGB (attr.TrueColorBackground.Value.Red, attr.TrueColorBackground.Value.Green, attr.TrueColorBackground.Value.Blue)); } _stringBuilder.Append (info.Char != '\x1b' ? info.Char : ' '); } - _stringBuilder.Append (RestoreCursor); + _stringBuilder.Append (EscSeqUtils.CSI_RestoreCursorPosition); string s = _stringBuilder.ToString (); - + return WriteConsole (_screenBuffer, s, (uint)(s.Length), out uint _, null); } @@ -521,11 +503,11 @@ public struct CharInfo { [FieldOffset (2)] public ushort Attributes; } - public struct ExtendedCharInfo { + public struct ExtendedCharInfo { public char Char { get; set; } public Attribute Attribute { get; set; } - public ExtendedCharInfo(char character, Attribute attribute) + public ExtendedCharInfo (char character, Attribute attribute) { Char = character; Attribute = attribute; @@ -778,7 +760,7 @@ internal class WindowsDriver : ConsoleDriver { public WindowsConsole WinConsole { get; private set; } - public override bool SupportsTrueColorOutput => Environment.OSVersion.Version.Build >= 14931; + public override bool SupportsTrueColor => Environment.OSVersion.Version.Build >= 14931; public WindowsDriver () { @@ -948,10 +930,10 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) TerminalResized?.Invoke (); break; - case WindowsConsole.EventType.Focus: - break; - } + case WindowsConsole.EventType.Focus: + break; } + } WindowsConsole.ButtonState? _lastMouseButtonPressed = null; bool _isButtonPressed = false; @@ -1453,7 +1435,7 @@ public override void AddRune (Rune systemRune) } Contents [Row, Col, 0] = rune.Value; Contents [Row, Col, 1] = CurrentAttribute.Value; - _outputBuffer [GetOutputBufferPosition()] = new WindowsConsole.ExtendedCharInfo ((char)rune.Value, CurrentAttribute); + _outputBuffer [GetOutputBufferPosition ()] = new WindowsConsole.ExtendedCharInfo ((char)rune.Value, CurrentAttribute); WindowsConsole.SmallRect.Update (ref _damageRegion, (short)Col, (short)Row); if (Col > 0) { @@ -1805,7 +1787,7 @@ public override void End () // end of the screen. // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE // backbuffer! So we need to use [0J instead. - Console.Out.Write (EscSeqUtils.CSI_ClearScreen(0)); + Console.Out.Write (EscSeqUtils.CSI_ClearScreen (0)); // Disable alternative screen buffer. Console.Out.Write (EscSeqUtils.CSI_RestoreAltBufferWithBackscroll); diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs index c5af5705b5..f617dab498 100644 --- a/Terminal.Gui/Drawing/Color.cs +++ b/Terminal.Gui/Drawing/Color.cs @@ -85,6 +85,7 @@ public enum Color { /// /// Indicates the RGB for true colors. /// + [JsonConverter (typeof (TrueColorJsonConverter))] public readonly struct TrueColor : IEquatable { private static readonly ImmutableDictionary TrueColorToConsoleColorMap = new Dictionary () { { new TrueColor (0,0,0),Color.Black }, @@ -137,7 +138,7 @@ public TrueColor (int red, int green, int blue) /// The text to analyze. /// The parsed value. /// A boolean value indcating whether it was successful. - public static bool TryParse (string text, [NotNullWhen(true)] out TrueColor? trueColor) + public static bool TryParse (string text, [NotNullWhen (true)] out TrueColor? trueColor) { // empty color if ((text == null) || (text.Length == 0)) { @@ -172,7 +173,7 @@ public static bool TryParse (string text, [NotNullWhen(true)] out TrueColor? tru var r = int.Parse (match.Groups [1].Value); var g = int.Parse (match.Groups [2].Value); var b = int.Parse (match.Groups [3].Value); - trueColor = new TrueColor (r, g, b); + trueColor = new TrueColor (r, g, b); return true; } @@ -185,27 +186,29 @@ public static bool TryParse (string text, [NotNullWhen(true)] out TrueColor? tru /// /// The to convert. /// - public static TrueColor FromConsoleColor (Color consoleColor) - { - switch (consoleColor) { - case Color.Black: return new TrueColor (0, 0, 0); - case Color.Blue: return new TrueColor (0, 0, 0x80); - case Color.Green: return new TrueColor (0, 0x80, 0); - case Color.Cyan: return new TrueColor (0, 0x80, 0x80); - case Color.Red: return new TrueColor (0x80, 0, 0); - case Color.Magenta: return new TrueColor (0x80, 0, 0x80); - case Color.Brown: return new TrueColor (0xC1, 0x9C, 0x00); // TODO confirm this - case Color.Gray: return new TrueColor (0xC0, 0xC0, 0xC0); - case Color.DarkGray: return new TrueColor (0x80, 0x80, 0x80); - case Color.BrightBlue: return new TrueColor (0, 0, 0xFF); - case Color.BrightGreen: return new TrueColor (0, 0xFF, 0); - case Color.BrightCyan: return new TrueColor (0, 0xFF, 0xFF); - case Color.BrightRed: return new TrueColor (0xFF, 0, 0); - case Color.BrightMagenta: return new TrueColor (0xFF, 0, 0xFF); - case Color.BrightYellow: return new TrueColor (0xFF, 0xFF, 0); - case Color.White: return new TrueColor (0xFF, 0xFF, 0xFF); - default: throw new ArgumentException ($"No supported TrueColor mapping for '{consoleColor}'."); + public static TrueColor? FromConsoleColor (Color consoleColor) + { + return consoleColor switch { + Color.Black => new TrueColor (0, 0, 0), + Color.Blue => new TrueColor (0, 0, 0x80), + Color.Green => new TrueColor (0, 0x80, 0), + Color.Cyan => new TrueColor (0, 0x80, 0x80), + Color.Red => new TrueColor (0x80, 0, 0), + Color.Magenta => new TrueColor (0x80, 0, 0x80), + Color.Brown => new TrueColor (0xC1, 0x9C, 0x00) // TODO confirm this + , + Color.Gray => new TrueColor (0xC0, 0xC0, 0xC0), + Color.DarkGray => new TrueColor (0x80, 0x80, 0x80), + Color.BrightBlue => new TrueColor (0, 0, 0xFF), + Color.BrightGreen => new TrueColor (0, 0xFF, 0), + Color.BrightCyan => new TrueColor (0, 0xFF, 0xFF), + Color.BrightRed => new TrueColor (0xFF, 0, 0), + Color.BrightMagenta => new TrueColor (0xFF, 0, 0xFF), + Color.BrightYellow => new TrueColor (0xFF, 0xFF, 0), + Color.White => new TrueColor (0xFF, 0xFF, 0xFF), + var _ => null }; + ; } /// @@ -213,9 +216,13 @@ public static TrueColor FromConsoleColor (Color consoleColor) /// /// /// - public static Color ToConsoleColor (TrueColor trueColor) + public static Color ToConsoleColor (TrueColor? trueColor) { - return TrueColorToConsoleColorMap.OrderBy (kv => CalculateDistance (kv.Key, trueColor)).First ().Value; + if (trueColor.HasValue) { + return TrueColorToConsoleColorMap.MinBy (kv => CalculateDistance (kv.Key, trueColor.Value)).Value; + } else { + return (Color)(-1); + } } private static float CalculateDistance (TrueColor color1, TrueColor color2) @@ -281,7 +288,7 @@ public struct Attribute : IEquatable { /// /// Default empty attribute. /// - public static readonly Attribute Default = new Attribute (); + public static readonly Attribute Default = new Attribute (Color.White, Color.Black); /// /// The -specific color value. If is @@ -307,13 +314,13 @@ public struct Attribute : IEquatable { /// Gets the TrueColor foreground color. /// [JsonConverter (typeof (TrueColorJsonConverter))] - public TrueColor TrueColorForeground { get; private init; } + public TrueColor? TrueColorForeground { get; private init; } /// /// Gets the TrueColor background color. /// [JsonConverter (typeof (TrueColorJsonConverter))] - public TrueColor TrueColorBackground { get; private init; } + public TrueColor? TrueColorBackground { get; private init; } /// /// Initializes a new instance with a platform-specific color value. @@ -377,7 +384,7 @@ public Attribute (int value, Color foreground, Color background) /// /// /// - public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground) + public Attribute (TrueColor? trueColorForeground, TrueColor? trueColorBackground) { Foreground = TrueColor.ToConsoleColor (trueColorForeground); Background = TrueColor.ToConsoleColor (trueColorBackground); @@ -395,7 +402,7 @@ public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground) /// fallback values for and (in case /// driver does not support true color rendering). /// - /// If you do not want to manually specify the fallback colors use + /// If you do not want to manually specify the fallback colors use /// instead which auto calculates these. /// /// True color RGB values you would like to use. @@ -420,8 +427,6 @@ public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground, /// The color. public Attribute (Color color) : this (color, color) { } - /// - public override int GetHashCode () => HashCode.Combine (Value, Foreground, Background); /// /// Compares two attributes for equality. @@ -448,11 +453,15 @@ public override bool Equals (object obj) /// public bool Equals (Attribute other) { + if (TrueColorForeground.HasValue || TrueColorBackground.HasValue) { + return + TrueColorForeground == other.TrueColorForeground && + TrueColorBackground == other.TrueColorBackground; + } + return Value == other.Value && Foreground == other.Foreground && - Background == other.Background && - TrueColorForeground == other.TrueColorForeground && - TrueColorBackground == other.TrueColorBackground; + Background == other.Background; } /// @@ -657,12 +666,12 @@ public override bool Equals (object obj) /// true if the two objects are equal public bool Equals (ColorScheme other) { - return other != null && - EqualityComparer.Default.Equals (_normal, other._normal) && - EqualityComparer.Default.Equals (_focus, other._focus) && - EqualityComparer.Default.Equals (_hotNormal, other._hotNormal) && - EqualityComparer.Default.Equals (_hotFocus, other._hotFocus) && - EqualityComparer.Default.Equals (_disabled, other._disabled); + return other != null && + EqualityComparer.Default.Equals (_normal, other._normal) && + EqualityComparer.Default.Equals (_focus, other._focus) && + EqualityComparer.Default.Equals (_hotNormal, other._hotNormal) && + EqualityComparer.Default.Equals (_hotFocus, other._hotFocus) && + EqualityComparer.Default.Equals (_disabled, other._disabled); } /// diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs index 65017e5359..ea74bea58c 100644 --- a/UICatalog/Scenarios/Images.cs +++ b/UICatalog/Scenarios/Images.cs @@ -20,7 +20,7 @@ public override void Setup () var x = 0; var y = 0; - var canTrueColor = Application.Driver.SupportsTrueColorOutput; + var canTrueColor = Application.Driver.SupportsTrueColor; var lblDriverName = new Label ($"Current driver is {Application.Driver.GetType ().Name}") { X = x, diff --git a/UICatalog/Scenarios/TrueColors.cs b/UICatalog/Scenarios/TrueColors.cs index df3afab63f..e4c7df5005 100644 --- a/UICatalog/Scenarios/TrueColors.cs +++ b/UICatalog/Scenarios/TrueColors.cs @@ -12,7 +12,7 @@ public override void Setup () var x = 2; var y = 1; - var canTrueColor = true; // Application.Driver.SupportsTrueColorOutput; + var canTrueColor = Application.Driver.SupportsTrueColor; var lblDriverName = new Label ($"Current driver is {Application.Driver.GetType ().Name}") { X = x, @@ -83,9 +83,9 @@ public override void Setup () Application.RootMouseEvent = (e) => { var normal = e.View.GetNormalColor (); if (e.View != null) { - lblRed.Text = normal.TrueColorForeground.Red.ToString (); - lblGreen.Text = normal.TrueColorForeground.Green.ToString (); - lblBlue.Text = normal.TrueColorForeground.Blue.ToString (); + lblRed.Text = normal.TrueColorForeground.Value.Red.ToString (); + lblGreen.Text = normal.TrueColorForeground.Value.Green.ToString (); + lblBlue.Text = normal.TrueColorForeground.Value.Blue.ToString (); } }; } diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index 08b447337d..70a1d50a89 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -570,7 +570,7 @@ public void TestConfigurationManagerInvalidJsonThrows () ""UserDefined"": { ""AbNormal"": { ""foreground"": ""green"", - ""background"": ""1234"" + ""background"": ""black"" } } } @@ -660,7 +660,7 @@ public void TestConfigurationManagerInvalidJsonLogs () ""UserDefined"": { ""AbNormal"": { ""foreground"": ""green"", - ""background"": ""1234"" + ""background"": ""black"" } } } diff --git a/UnitTests/Configuration/JsonConverterTests.cs b/UnitTests/Configuration/JsonConverterTests.cs index a3e4ac43f2..0b0178ac90 100644 --- a/UnitTests/Configuration/JsonConverterTests.cs +++ b/UnitTests/Configuration/JsonConverterTests.cs @@ -198,7 +198,7 @@ public void TestSerialize () // Test serializing to human-readable color names var attribute = new Attribute (Color.Blue, Color.Green); var json = JsonSerializer.Serialize (attribute, ConfigurationManagerTests._jsonOptions); - Assert.Equal ("{\"Foreground\":\"Blue\",\"Background\":\"Green\"}", json); + Assert.Equal ("{\"Foreground\":\"Blue\",\"Background\":\"Green\",\"TrueColorForeground\":\"#000080\",\"TrueColorBackground\":\"#008000\"}", json); } } diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index 57747a3ba0..d6690e6632 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -21,12 +21,12 @@ TRACE;DEBUG_IDISPOSABLE - - + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/UnitTests/Views/TreeViewTests.cs b/UnitTests/Views/TreeViewTests.cs index 0644fac65c..0dc4538899 100644 --- a/UnitTests/Views/TreeViewTests.cs +++ b/UnitTests/Views/TreeViewTests.cs @@ -847,13 +847,15 @@ public void TestTreeViewColor () tv.AddObject (n2); tv.Expand (n1); - var pink = new Attribute (Color.Magenta, Color.Black); - var hotpink = new Attribute (Color.BrightMagenta, Color.Black); - tv.ColorScheme = new ColorScheme (); tv.LayoutSubviews (); tv.Draw (); + // create a new color scheme + var pink = new Attribute (Color.Magenta, Color.Black); + var hotpink = new Attribute (Color.BrightMagenta, Color.Black); + + // Normal drawing of the tree view TestHelpers.AssertDriverContentsAre ( @"├-normal @@ -870,7 +872,6 @@ public void TestTreeViewColor () ", new [] { tv.ColorScheme.Normal, pink }); - // create a new color scheme var pinkScheme = new ColorScheme { Normal = pink, Focus = hotpink diff --git a/UnitTests/xunit.runner.json b/UnitTests/xunit.runner.json index c108763855..136d06384a 100644 --- a/UnitTests/xunit.runner.json +++ b/UnitTests/xunit.runner.json @@ -1,6 +1 @@ -{ - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "parallelizeTestCollections": false, - "parallelizeAssembly": false, - "stopOnFail": true -} + \ No newline at end of file From e12005425ffeb9a1cb158472e2753a15a226b0aa Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Tue, 11 Jul 2023 14:11:28 -0700 Subject: [PATCH 67/99] Debugging netdriver --- .../ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs | 13 ++++++++++++- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 15 +++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs index 3468dd3400..1f7784af5e 100644 --- a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs @@ -267,7 +267,18 @@ public enum DECSCUSR_Style { public static readonly string CSI_ReportDeviceAttributes = CSI + "0c"; /// - /// The terminal reply to : Windows Terminal Will emit “\x1b[?1;0c”, indicating "VT101 with No Options". + /// The terminal reply to : + /// Windows Terminal v1.17 and below Will emit “\x1b[?1;0c”, indicating "VT101 with No Options". + /// Windows Terminal v1.18+ emits: \x1b[?61;6;7;22;23;24;28;32;42c" + /// - 61 - indicates VT525 + /// - 7 - indicates VT400 + /// - 6 - indicates Selective Erase + /// - 22 - indicates ANSI Color + /// - 23 - indicates ANSI Text Locator + /// - 24 - indicates VT200 Highlighting + /// - 28 - indicates Rectangular Editing + /// - 32 - indicates National Replacement Character Sets + /// - 42 - indicates ISO Latin-1 Character Set /// public const string CSI_ReportDeviceAttributes_Terminator = "c"; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 920ede62bf..0d47790266 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -232,6 +232,8 @@ void RequestWindowSize () break; case true: //Request the size of the text area in characters. + // BUGBUG: This is getting called repeatedly / all the time with + // Windows terminal v1.18+ EscSeqRequests.Add (EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator); Console.Out.Write (EscSeqUtils.CSI_ReportTerminalSizeInChars); break; @@ -972,7 +974,7 @@ public override Attribute MakeColor (Color foreground, Color background) #region Cursor Handling bool SetCursorPosition (int col, int row) { - if (IsWinPlatform) { + //if (IsWinPlatform) { // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. try { Console.SetCursorPosition (col, row); @@ -980,11 +982,12 @@ bool SetCursorPosition (int col, int row) } catch (Exception) { return false; } - } else { - // TODO: Explain why + 1 is needed (and why we do this for non-Windows). - Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1)); - return true; - } + // BUGBUG: This breaks -usc on WSL; not sure why. But commenting out fixes. + //} else { + // // TODO: Explain why + 1 is needed (and why we do this for non-Windows). + // Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1)); + // return true; + //} } CursorVisibility? _cachedCursorVisibility; From d52dafc8041c157a7f50370501e907868b2e21b0 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 12 Jul 2023 13:34:57 -0700 Subject: [PATCH 68/99] More netdriver diag --- .../ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs | 44 ++++++---- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 82 ++++++------------- 2 files changed, 56 insertions(+), 70 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs index 1f7784af5e..b95544d5b0 100644 --- a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs @@ -262,23 +262,36 @@ public enum DECSCUSR_Style { public const string CSI_RequestCursorPositionReport_Terminator = "R"; /// - /// ESC [ 0 c - DA Device Attributes - Report the terminal identity. + /// ESC [ 0 c - Send Device Attributes (Primary DA) + /// + /// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Application-Program-Command-functions + /// https://www.xfree86.org/current/ctlseqs.html + /// Windows Terminal v1.17 and below emits “\x1b[?1;0c”, indicating "VT101 with No Options". + /// Windows Terminal v1.18+ emits: \x1b[?61;6;7;22;23;24;28;32;42c" + /// See https://github.com/microsoft/terminal/pull/14906 + /// + /// 1 = 132 column mode + /// 6 = Selective erase + /// 7 = Soft fonts + /// 22 = Color text + /// 23 = Greek character sets + /// 24 = Turkish character sets + /// 28 = Rectangular area operations + /// 32 = Text macros + /// 42 = ISO Latin-2 character set + /// /// - public static readonly string CSI_ReportDeviceAttributes = CSI + "0c"; + public static readonly string CSI_SendDeviceAttributes = CSI + "0c"; /// - /// The terminal reply to : - /// Windows Terminal v1.17 and below Will emit “\x1b[?1;0c”, indicating "VT101 with No Options". - /// Windows Terminal v1.18+ emits: \x1b[?61;6;7;22;23;24;28;32;42c" - /// - 61 - indicates VT525 - /// - 7 - indicates VT400 - /// - 6 - indicates Selective Erase - /// - 22 - indicates ANSI Color - /// - 23 - indicates ANSI Text Locator - /// - 24 - indicates VT200 Highlighting - /// - 28 - indicates Rectangular Editing - /// - 32 - indicates National Replacement Character Sets - /// - 42 - indicates ISO Latin-1 Character Set + /// ESC [ > 0 c - Send Device Attributes (Secondary DA) + /// Windows Terminal v1.18+ emits: "\x1b[>0;10;1c" (vt100, unknown, vt220) + /// + public static readonly string CSI_SendDeviceAttributes2 = CSI + ">0c"; + + /// + /// The terminator indicating a reply to or + /// /// public const string CSI_ReportDeviceAttributes_Terminator = "c"; @@ -289,7 +302,7 @@ public enum DECSCUSR_Style { public static readonly string CSI_ReportTerminalSizeInChars = CSI + "18t"; /// - /// The terminal reply to : ESC [ 8 ; height ; width t + /// The terminator indicating a reply to : ESC [ 8 ; height ; width t /// public const string CSI_ReportTerminalSizeInChars_Terminator = "t"; @@ -382,6 +395,7 @@ public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, Cons public static void DecodeEscSeq (EscSeqRequests escSeqRequests, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod, out string c1Control, out string code, out string [] values, out string terminator, out bool isMouse, out List buttonState, out Point pos, out bool isResponse, Action continuousButtonPressedHandler) { char [] kChars = GetKeyCharArray (cki); + Debug.WriteLine ($"EscSeq: {new string(kChars)}"); (c1Control, code, values, terminator) = GetEscapeResult (kChars); isMouse = false; buttonState = new List () { 0 }; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 0d47790266..46f9c10765 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -118,7 +118,6 @@ internal class NetEvents { #if PROCESS_REQUEST bool _neededProcessRequest; #endif - public bool IsTerminalWithOptions { get; set; } public EscSeqRequests EscSeqRequests { get; } = new EscSeqRequests (); public NetEvents (ConsoleDriver consoleDriver) @@ -206,37 +205,27 @@ void CheckWindowSizeChange () void RequestWindowSize () { while (true) { - // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. - Thread.Sleep (10); + // Wait for a while then check if screen has changed sizes + Task.Delay (500).Wait (); + if (_stopTasks) { return; } - switch (IsTerminalWithOptions) { - case false: - int buffHeight, buffWidth; - if (((NetDriver)_consoleDriver).IsWinPlatform) { - buffHeight = Math.Max (Console.BufferHeight, 0); - buffWidth = Math.Max (Console.BufferWidth, 0); - } else { - buffHeight = _consoleDriver.Rows; - buffWidth = _consoleDriver.Cols; - } - if (EnqueueWindowSizeEvent ( - Math.Max (Console.WindowHeight, 0), - Math.Max (Console.WindowWidth, 0), - buffHeight, - buffWidth)) { + int buffHeight, buffWidth; + if (((NetDriver)_consoleDriver).IsWinPlatform) { + buffHeight = Math.Max (Console.BufferHeight, 0); + buffWidth = Math.Max (Console.BufferWidth, 0); + } else { + buffHeight = _consoleDriver.Rows; + buffWidth = _consoleDriver.Cols; + } + if (EnqueueWindowSizeEvent ( + Math.Max (Console.WindowHeight, 0), + Math.Max (Console.WindowWidth, 0), + buffHeight, + buffWidth)) { - return; - } - break; - case true: - //Request the size of the text area in characters. - // BUGBUG: This is getting called repeatedly / all the time with - // Windows terminal v1.18+ - EscSeqRequests.Add (EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator); - Console.Out.Write (EscSeqUtils.CSI_ReportTerminalSizeInChars); - break; + return; } } } @@ -434,21 +423,7 @@ void HandleRequestResponseEvent (string c1Control, string code, string [] values return; } break; - case EscSeqUtils.CSI_ReportDeviceAttributes_Terminator: - try { - var parent = EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ()); - if (parent == null) { Debug.WriteLine ("Not supported!"); } - } catch (Exception ex) { - Debug.WriteLine (ex.Message); - } - if (c1Control == "CSI" && values.Length == 2 - && values [0] == "1" && values [1] == "0") { - // Reports CSI?1;0c ("VT101 with No Options") - IsTerminalWithOptions = false; - } else { - IsTerminalWithOptions = true; - } - break; + case EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator: switch (values [0]) { case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue: @@ -975,14 +950,14 @@ public override Attribute MakeColor (Color foreground, Color background) bool SetCursorPosition (int col, int row) { //if (IsWinPlatform) { - // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. - try { - Console.SetCursorPosition (col, row); - return true; - } catch (Exception) { - return false; - } - // BUGBUG: This breaks -usc on WSL; not sure why. But commenting out fixes. + // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. + try { + Console.SetCursorPosition (col, row); + return true; + } catch (Exception) { + return false; + } + // BUGBUG: This breaks -usc on WSL; not sure why. But commenting out fixes. //} else { // // TODO: Explain why + 1 is needed (and why we do this for non-Windows). // Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1)); @@ -1012,7 +987,7 @@ public override bool SetCursorVisibility (CursorVisibility visibility) { _cachedCursorVisibility = visibility; var isVisible = Console.CursorVisible = visibility == CursorVisibility.Default; - Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor); + //Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor); return isVisible; } @@ -1233,9 +1208,6 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle // Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. mLoop.ProcessInput = (e) => ProcessInput (e); - - _mainLoop._netEvents.EscSeqRequests.Add (EscSeqUtils.CSI_ReportDeviceAttributes_Terminator); - Console.Out.Write (EscSeqUtils.CSI_ReportDeviceAttributes); } volatile bool _winSizeChanging; From f0ecb5f496b21ee09f7177e406b1d8ca80641273 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Fri, 14 Jul 2023 12:16:03 -0700 Subject: [PATCH 69/99] API docs for escutils --- Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs | 5 +++-- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs index b95544d5b0..182e58bea0 100644 --- a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs @@ -270,7 +270,8 @@ public enum DECSCUSR_Style { /// Windows Terminal v1.18+ emits: \x1b[?61;6;7;22;23;24;28;32;42c" /// See https://github.com/microsoft/terminal/pull/14906 /// - /// 1 = 132 column mode + /// 61 - The device conforms to level 1 of the character cell display architecture + /// (See https://github.com/microsoft/terminal/issues/15693#issuecomment-1633304497) /// 6 = Selective erase /// 7 = Soft fonts /// 22 = Color text @@ -285,7 +286,7 @@ public enum DECSCUSR_Style { /// /// ESC [ > 0 c - Send Device Attributes (Secondary DA) - /// Windows Terminal v1.18+ emits: "\x1b[>0;10;1c" (vt100, unknown, vt220) + /// Windows Terminal v1.18+ emits: "\x1b[>0;10;1c" (vt100, firmware version 1.0, vt220) /// public static readonly string CSI_SendDeviceAttributes2 = CSI + ">0c"; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 46f9c10765..88c17b0cae 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -182,8 +182,10 @@ void ProcessInputResultQueue () _isEscSeq = false; break; } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) { - ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod); - _cki = null; + if (_cki != null) { + ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; + } break; } else { _inputResultQueue.Enqueue (new InputResult { From ef582f3b1bdaa2c8a65acf26863c576c6fa82168 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Fri, 14 Jul 2023 13:41:08 -0700 Subject: [PATCH 70/99] Update unicode scenario to stress more stuff --- Terminal.Gui/Drawing/Glyphs.cs | 15 +++++++++++++++ UICatalog/Scenarios/Unicode.cs | 18 +++++------------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Terminal.Gui/Drawing/Glyphs.cs b/Terminal.Gui/Drawing/Glyphs.cs index 6edb70107f..fa81604418 100644 --- a/Terminal.Gui/Drawing/Glyphs.cs +++ b/Terminal.Gui/Drawing/Glyphs.cs @@ -139,6 +139,11 @@ public class GlyphDefinitions { /// public Rune Collapse { get; set; } = (Rune)'-'; + /// + /// Identical To (U+226) + /// + public Rune IdenticalTo { get; set; } = (Rune)'≡'; + /// /// Apple (non-BMP). Because snek. And because it's an example of a non-BMP surrogate pair. See Issue #2610. /// @@ -166,6 +171,16 @@ public class GlyphDefinitions { /// public Rune File { get; set; } = (Rune)'☰'; + /// + /// Horizontal Ellipsis - … U+2026 + /// + public Rune HorizontalEllipsis { get; set; } = (Rune)'…'; + + /// + /// Vertical Four Dots - ⁞ U+205e + /// + public Rune VerticalFourDots { get; set; } = (Rune)'⁞'; + #region ----------------- Lines ----------------- /// /// Box Drawings Horizontal Line - Light (U+2500) - ─ diff --git a/UICatalog/Scenarios/Unicode.cs b/UICatalog/Scenarios/Unicode.cs index 866679a615..e5e53095c8 100644 --- a/UICatalog/Scenarios/Unicode.cs +++ b/UICatalog/Scenarios/Unicode.cs @@ -9,16 +9,9 @@ namespace UICatalog.Scenarios { public class UnicodeInMenu : Scenario { public override void Setup () { - const string IdenticalSign = "\u2261"; - const string ArrowUpSign = "\u2191"; - const string ArrowDownSign = "\u2193"; - const string EllipsesSign = "\u2026"; - const string StashSign = "\u205E"; - - //string text = "Hello world, how are you today? Pretty neat!\nSecond line\n\nFourth Line."; string unicode = "Τὴ γλῶσσα μοῦ ἔδωσαν ἑλληνικὴ\nτὸ σπίτι φτωχικὸ στὶς ἀμμουδιὲς τοῦ Ὁμήρου.\nΜονάχη ἔγνοια ἡ γλῶσσα μου στὶς ἀμμουδιὲς τοῦ Ὁμήρου."; - string gitString = $"gui.cs master {IdenticalSign} {ArrowDownSign}18 {ArrowUpSign}10 {StashSign}1 {EllipsesSign}"; + string gitString = $"gui.cs 糊 (hú) {ConfigurationManager.Glyphs.IdenticalTo} {ConfigurationManager.Glyphs.DownArrow}18 {ConfigurationManager.Glyphs.UpArrow}10 {ConfigurationManager.Glyphs.VerticalFourDots}1 {ConfigurationManager.Glyphs.HorizontalEllipsis}"; var menu = new MenuBar (new MenuBarItem [] { new MenuBarItem ("_Файл", new MenuItem [] { @@ -30,7 +23,7 @@ public override void Setup () new MenuBarItem ("_Edit", new MenuItem [] { new MenuItem ("_Copy", "", null), new MenuItem ("C_ut", "", null), - new MenuItem ("_Paste", "", null) + new MenuItem ("_糊", "hú (Paste)", null) }) }); Application.Top.Add (menu); @@ -60,11 +53,10 @@ public override void Setup () label = new Label ("CheckBox:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 }; Win.Add (label); var checkBox = new CheckBox (gitString) { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50) }; - var ckbAllowNull = new CheckBox ("Allow null checked") { X = Pos.Right (checkBox) + 1, Y = Pos.Y (label) }; - ckbAllowNull.Toggled += (s,e) => checkBox.AllowNullChecked = (bool)!e.OldValue; - Win.Add (checkBox, ckbAllowNull); + var checkBoxRight = new CheckBox ($"Align Right - {gitString}") { X = 20, Y = Pos.Bottom (checkBox), Width = Dim.Percent (50), TextAlignment = TextAlignment.Right}; + Win.Add (checkBox, checkBoxRight); - label = new Label ("ComboBox:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 }; + label = new Label ("ComboBox:") { X = Pos.X (label), Y = Pos.Bottom (checkBoxRight) + 1 }; Win.Add (label); var comboBox = new ComboBox () { X = 20, From d813b700dcb53a2686c794d5ca6a04ce8821c1ef Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Fri, 14 Jul 2023 15:31:51 -0700 Subject: [PATCH 71/99] Contents: Now a 2D array of Cells; WIP --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 4 +- .../CursesDriver/CursesDriver.cs | 222 ++++++++--------- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 224 +++++++++--------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 63 +++-- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 174 +++++--------- UnitTests/TestHelpers.cs | 23 +- 6 files changed, 324 insertions(+), 386 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index dd0576bc4b..aa5b3e8d02 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -85,7 +85,7 @@ public abstract class ConsoleDriver { /// The format of the array is rows, columns, and 3 values on the last column: Rune, Attribute and Dirty Flag /// /// - public int [,,] Contents { get; internal set; } + //public int [,,] Contents { get; internal set; } ///// ///// The contents of the application output. The driver outputs this buffer to the terminal when @@ -94,7 +94,7 @@ public abstract class ConsoleDriver { ///// The format of the array is rows, columns. The first index is the row, the second index is the column. ///// ///// - //public Cell [,] Contents { get; internal set; } + public Cell [,] Contents { get; internal set; } /// /// Initializes the driver diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 62a02f17ee..a75a538aca 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -49,111 +49,111 @@ public override bool IsRuneSupported (Rune rune) public override void AddRune (Rune systemRune) { - var rune = systemRune.MakePrintable (); - var runeWidth = rune.GetColumns (); - var validLocation = IsValidLocation (Col, Row); - - if (validLocation) { - if (!_atValidLocation) { - // Move was called with an invalid location. - // Since then, the clip changed, and now we are at a valid location. - Curses.move (Row, Col); - _atValidLocation = false; - } - if (runeWidth == 0 && Col > 0) { - // This is a combining character, and we are not at the beginning of the line. - var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); - var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; - Contents [Row, Col - 1, 0] = normalized [0]; - Contents [Row, Col - 1, 1] = CurrentAttribute.Value; - Contents [Row, Col - 1, 2] = 1; - Curses.attrset (Contents [Row, Col - 1, 1]); - Curses.mvaddch (Row, Col - 1, normalized [0]); - } else { - Contents [Row, Col, 1] = CurrentAttribute.Value; - Contents [Row, Col, 2] = 1; - - if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumns () > 1) { - // This is a single-width character, and we are not at the beginning of the line. - var curAttr = CurrentAttribute; - Curses.attrset (Contents [Row, Col - 1, 1]); - Curses.mvaddch (Row, Col - 1, Rune.ReplacementChar.Value); - Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; - Curses.move (Row, Col); - Curses.attrset (curAttr.Value); - - } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumns () > 1) { - // This is a single-width character, and we are not at the end of the line. - var curAttr = CurrentAttribute; - Curses.attrset (Contents [Row, Col + 1, 1]); - Curses.mvaddch (Row, Col + 1, Rune.ReplacementChar.Value); - Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; - Curses.move (Row, Col); - Curses.attrset (curAttr.Value); - - } - if (runeWidth > 1 && Col == Clip.Right - 1) { - // This is a double-width character, and we are at the end of the line. - Curses.addch (Rune.ReplacementChar.Value); - Contents [Row, Col, 0] = Rune.ReplacementChar.Value; - } else { - // This is a single-width character, or we are not at the end of the line. - - var curAttr = CurrentAttribute; - Curses.attrset (Contents [Row, Col, 1]); - - if (rune.IsBmp) { - Contents [Row, Col, 0] = rune.Value; - Curses.addch (Contents [Row, Col, 0]); - } else { - var column = Col; - ReadOnlySpan remainingInput = rune.ToString ().AsSpan (); - while (!remainingInput.IsEmpty) { - // Decode - OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); - - if (opStatus != OperationStatus.Done) { - result = Rune.ReplacementChar; - } - Contents [Row, column, 0] = result.Value; - Contents [Row, column, 1] = CurrentAttribute.Value; - - Curses.attrset (Contents [Row, column, 1]); - // BUGBUG: workaround curses not supporting non BMP? # - Curses.mvaddch (Row, column, Rune.ReplacementChar.Value); - //Curses.mvaddch (Row, column, Contents [Row, column, 0]); - - // Slice and loop again - remainingInput = remainingInput.Slice (charsConsumed); - column++; - } - Curses.move (Row, Col); - } - Curses.attrset (curAttr.Value); - } - } - } else { - _atValidLocation = false; - } + //var rune = systemRune.MakePrintable (); + //var runeWidth = rune.GetColumns (); + //var validLocation = IsValidLocation (Col, Row); + + //if (validLocation) { + // if (!_atValidLocation) { + // // Move was called with an invalid location. + // // Since then, the clip changed, and now we are at a valid location. + // Curses.move (Row, Col); + // _atValidLocation = false; + // } + // if (runeWidth == 0 && Col > 0) { + // // This is a combining character, and we are not at the beginning of the line. + // var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); + // var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; + // Contents [Row, Col - 1, 0] = normalized [0]; + // Contents [Row, Col - 1, 1] = CurrentAttribute.Value; + // Contents [Row, Col - 1, 2] = 1; + // Curses.attrset (Contents [Row, Col - 1, 1]); + // Curses.mvaddch (Row, Col - 1, normalized [0]); + // } else { + // Contents [Row, Col, 1] = CurrentAttribute.Value; + // Contents [Row, Col, 2] = 1; + + // if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumns () > 1) { + // // This is a single-width character, and we are not at the beginning of the line. + // var curAttr = CurrentAttribute; + // Curses.attrset (Contents [Row, Col - 1, 1]); + // Curses.mvaddch (Row, Col - 1, Rune.ReplacementChar.Value); + // Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; + // Curses.move (Row, Col); + // Curses.attrset (curAttr.Value); + + // } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumns () > 1) { + // // This is a single-width character, and we are not at the end of the line. + // var curAttr = CurrentAttribute; + // Curses.attrset (Contents [Row, Col + 1, 1]); + // Curses.mvaddch (Row, Col + 1, Rune.ReplacementChar.Value); + // Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; + // Curses.move (Row, Col); + // Curses.attrset (curAttr.Value); + + // } + // if (runeWidth > 1 && Col == Clip.Right - 1) { + // // This is a double-width character, and we are at the end of the line. + // Curses.addch (Rune.ReplacementChar.Value); + // Contents [Row, Col, 0] = Rune.ReplacementChar.Value; + // } else { + // // This is a single-width character, or we are not at the end of the line. + + // var curAttr = CurrentAttribute; + // Curses.attrset (Contents [Row, Col, 1]); + + // if (rune.IsBmp) { + // Contents [Row, Col, 0] = rune.Value; + // Curses.addch (Contents [Row, Col, 0]); + // } else { + // var column = Col; + // ReadOnlySpan remainingInput = rune.ToString ().AsSpan (); + // while (!remainingInput.IsEmpty) { + // // Decode + // OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); + + // if (opStatus != OperationStatus.Done) { + // result = Rune.ReplacementChar; + // } + // Contents [Row, column, 0] = result.Value; + // Contents [Row, column, 1] = CurrentAttribute.Value; + + // Curses.attrset (Contents [Row, column, 1]); + // // BUGBUG: workaround curses not supporting non BMP? # + // Curses.mvaddch (Row, column, Rune.ReplacementChar.Value); + // //Curses.mvaddch (Row, column, Contents [Row, column, 0]); + + // // Slice and loop again + // remainingInput = remainingInput.Slice (charsConsumed); + // column++; + // } + // Curses.move (Row, Col); + // } + // Curses.attrset (curAttr.Value); + // } + // } + //} else { + // _atValidLocation = false; + //} - if (runeWidth is < 0 or > 0) { - Col++; - } + //if (runeWidth is < 0 or > 0) { + // Col++; + //} - if (runeWidth > 1) { - // This is a double-width character, and we are not at the end of the line. - if (validLocation && Col < Clip.Right) { - Contents [Row, Col, 1] = CurrentAttribute.Value; - Contents [Row, Col, 2] = 0; - - //if (rune.IsBmp) { - // // BUGBUG: workaround #2610 - // Contents [Row, Col, 0] = (char)0x00; - // Curses.addch (Contents [Row, Col, 0]); - //} - } - Col++; - } + //if (runeWidth > 1) { + // // This is a double-width character, and we are not at the end of the line. + // if (validLocation && Col < Clip.Right) { + // Contents [Row, Col, 1] = CurrentAttribute.Value; + // Contents [Row, Col, 2] = 0; + + // //if (rune.IsBmp) { + // // // BUGBUG: workaround #2610 + // // Contents [Row, Col, 0] = (char)0x00; + // // Curses.addch (Contents [Row, Col, 0]); + // //} + // } + // Col++; + //} } public override void Refresh () @@ -701,14 +701,14 @@ public virtual void ResizeScreen () public override void UpdateOffScreen () { - Contents = new int [Rows, Cols, 3]; - for (int row = 0; row < Rows; row++) { - for (int col = 0; col < Cols; col++) { - Contents [row, col, 0] = ' '; - Contents [row, col, 1] = Colors.TopLevel.Normal.Value; - Contents [row, col, 2] = 0; - } - } + //Contents = new int [Rows, Cols, 3]; + //for (int row = 0; row < Rows; row++) { + // for (int col = 0; col < Cols; col++) { + // Contents [row, col, 0] = ' '; + // Contents [row, col, 1] = Colors.TopLevel.Normal.Value; + // Contents [row, col, 2] = 0; + // } + //} } public static bool Is_WSL_Platform () diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 59719b1aac..d27a65109f 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -66,56 +66,56 @@ public FakeDriver () public override void AddRune (Rune rune) { - rune = rune.MakePrintable (); - var runeWidth = rune.GetColumns (); - var validLocation = IsValidLocation (Col, Row); - - if (validLocation) { - if (rune.IsCombiningMark () && Col > 0) { - // Decode the previous rune - var previousRune = new Rune (Contents [Row, Col - 1, 0]); - var newCombo = new StringBuilder (); - ReadOnlySpan remainingInput = previousRune.ToString ().AsSpan (); - while (!remainingInput.IsEmpty) { - // Decode - OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); - - if (opStatus is OperationStatus.DestinationTooSmall or OperationStatus.InvalidData) { - result = Rune.ReplacementChar; - } - - newCombo.Append (result); - // Slice and loop again - remainingInput = remainingInput [charsConsumed..]; - } - newCombo.Append (rune); - - var combined = newCombo.ToString (); - var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; - Contents [Row, Col - 1, 0] = normalized [0];// BUGBUG: This is wrong, we need to handle the case where the rune is more than one char - Contents [Row, Col - 1, 1] = CurrentAttribute.Value; - Contents [Row, Col - 1, 2] = 1; - Col--; - } else { - Contents [Row, Col, 0] = rune.Value; - Contents [Row, Col, 1] = CurrentAttribute.Value; - - if (Col > 0) { - var left = new Rune (Contents [Row, Col - 1, 0]); - if (left.GetColumns () > 1) { - Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; - } - } - - if (runeWidth > 1) { - Col++; - Contents [Row, Col, 0] = Rune.ReplacementChar.Value; - Contents [Row, Col, 1] = CurrentAttribute.Value; - Contents [Row, Col, 2] = 1; - } - } - } - Col++; + //rune = rune.MakePrintable (); + //var runeWidth = rune.GetColumns (); + //var validLocation = IsValidLocation (Col, Row); + + //if (validLocation) { + // if (rune.IsCombiningMark () && Col > 0) { + // // Decode the previous rune + // var previousRune = new Rune (Contents [Row, Col - 1, 0]); + // var newCombo = new StringBuilder (); + // ReadOnlySpan remainingInput = previousRune.ToString ().AsSpan (); + // while (!remainingInput.IsEmpty) { + // // Decode + // OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); + + // if (opStatus is OperationStatus.DestinationTooSmall or OperationStatus.InvalidData) { + // result = Rune.ReplacementChar; + // } + + // newCombo.Append (result); + // // Slice and loop again + // remainingInput = remainingInput [charsConsumed..]; + // } + // newCombo.Append (rune); + + // var combined = newCombo.ToString (); + // var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; + // Contents [Row, Col - 1, 0] = normalized [0];// BUGBUG: This is wrong, we need to handle the case where the rune is more than one char + // Contents [Row, Col - 1, 1] = CurrentAttribute.Value; + // Contents [Row, Col - 1, 2] = 1; + // Col--; + // } else { + // Contents [Row, Col, 0] = rune.Value; + // Contents [Row, Col, 1] = CurrentAttribute.Value; + + // if (Col > 0) { + // var left = new Rune (Contents [Row, Col - 1, 0]); + // if (left.GetColumns () > 1) { + // Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; + // } + // } + + // if (runeWidth > 1) { + // Col++; + // Contents [Row, Col, 0] = Rune.ReplacementChar.Value; + // Contents [Row, Col, 1] = CurrentAttribute.Value; + // Contents [Row, Col, 2] = 1; + // } + // } + //} + //Col++; } public override void End () @@ -144,53 +144,53 @@ public override void Init (Action terminalResized) public override void UpdateScreen () { - int top = Top; - int left = Left; - int rows = Math.Min (FakeConsole.WindowHeight + top, Rows); - int cols = Cols; - - var savedRow = FakeConsole.CursorTop; - var savedCol = FakeConsole.CursorLeft; - var savedCursorVisible = FakeConsole.CursorVisible; - for (int row = top; row < rows; row++) { - if (!_dirtyLine [row]) { - continue; - } - _dirtyLine [row] = false; - for (int col = left; col < cols; col++) { - FakeConsole.CursorTop = row; - FakeConsole.CursorLeft = col; - for (; col < cols; col++) { - if (Contents [row, col, 2] == 0) { - FakeConsole.CursorLeft++; - continue; - } - - var color = Contents [row, col, 1]; - // NOTE: In real drivers setting the color can be a performance hit, so we only do it when needed. - // in fakedriver we don't care about perf. - SetColor (color); - - var rune = (Rune)Contents [row, col, 0]; - if (rune.Utf16SequenceLength == 1) { - FakeConsole.Write (rune); - } else { - // TODO: Not sure we need to do this. I think we can just write the rune. - - FakeConsole.Write (rune.ToString ()); - } - //if (Rune.DecodeSurrogatePair (rune, out char [] spair)) { - // FakeConsole.Write (spair); - //} else { - // FakeConsole.Write ((char)rune); - //} - Contents [row, col, 2] = 0; - } - } - } - FakeConsole.CursorTop = savedRow; - FakeConsole.CursorLeft = savedCol; - FakeConsole.CursorVisible = savedCursorVisible; + //int top = Top; + //int left = Left; + //int rows = Math.Min (FakeConsole.WindowHeight + top, Rows); + //int cols = Cols; + + //var savedRow = FakeConsole.CursorTop; + //var savedCol = FakeConsole.CursorLeft; + //var savedCursorVisible = FakeConsole.CursorVisible; + //for (int row = top; row < rows; row++) { + // if (!_dirtyLine [row]) { + // continue; + // } + // _dirtyLine [row] = false; + // for (int col = left; col < cols; col++) { + // FakeConsole.CursorTop = row; + // FakeConsole.CursorLeft = col; + // for (; col < cols; col++) { + // if (Contents [row, col, 2] == 0) { + // FakeConsole.CursorLeft++; + // continue; + // } + + // var color = Contents [row, col, 1]; + // // NOTE: In real drivers setting the color can be a performance hit, so we only do it when needed. + // // in fakedriver we don't care about perf. + // SetColor (color); + + // var rune = (Rune)Contents [row, col, 0]; + // if (rune.Utf16SequenceLength == 1) { + // FakeConsole.Write (rune); + // } else { + // // TODO: Not sure we need to do this. I think we can just write the rune. + + // FakeConsole.Write (rune.ToString ()); + // } + // //if (Rune.DecodeSurrogatePair (rune, out char [] spair)) { + // // FakeConsole.Write (spair); + // //} else { + // // FakeConsole.Write ((char)rune); + // //} + // Contents [row, col, 2] = 0; + // } + // } + //} + //FakeConsole.CursorTop = savedRow; + //FakeConsole.CursorLeft = savedCol; + //FakeConsole.CursorVisible = savedCursorVisible; } public override void Refresh () @@ -546,21 +546,21 @@ public virtual void ResizeScreen () public override void UpdateOffScreen () { - Contents = new int [Rows, Cols, 3]; - _dirtyLine = new bool [Rows]; - - // Can raise an exception while is still resizing. - try { - for (int row = 0; row < Rows; row++) { - for (int c = 0; c < Cols; c++) { - Contents [row, c, 0] = ' '; - Contents [row, c, 1] = 0; - Contents [row, c, 2] = 0; - _dirtyLine [row] = true; - } - } - } catch (IndexOutOfRangeException) { } - Clip = new Rect (0, 0, Cols, Rows); + //Contents = new int [Rows, Cols, 3]; + //_dirtyLine = new bool [Rows]; + + //// Can raise an exception while is still resizing. + //try { + // for (int row = 0; row < Rows; row++) { + // for (int c = 0; c < Cols; c++) { + // Contents [row, c, 0] = ' '; + // Contents [row, c, 1] = 0; + // Contents [row, c, 2] = 0; + // _dirtyLine [row] = true; + // } + // } + //} catch (IndexOutOfRangeException) { } + //Clip = new Rect (0, 0, Cols, Rows); } public override void UpdateCursor () diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 88c17b0cae..3eec16da56 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -586,12 +586,6 @@ public NetDriver () public override void AddRune (Rune systemRune) { - if (Contents.Length != Rows * Cols * 3) { - // BUGBUG: Shouldn't this throw an exception? Doing so to see what happens - throw new InvalidOperationException ("Driver contents are wrong size"); - //return; - } - int runeWidth = -1; var validLocation = IsValidLocation (Col, Row); if (validLocation) { @@ -599,29 +593,31 @@ public override void AddRune (Rune systemRune) runeWidth = rune.GetColumns (); if (runeWidth == 0 && Col > 0) { // This is a combining character, and we are not at the beginning of the line. - var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); + // TODO: Remove hard-coded [0] once combining pairs is supported + var combined = $"{Contents [Row, Col - 1].Runes [0]}{rune.Value}"; var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; - Contents [Row, Col - 1, 0] = normalized [0]; - Contents [Row, Col - 1, 1] = CurrentAttribute.Value; - Contents [Row, Col - 1, 2] = 1; + Contents [Row, Col - 1].Runes [0] = (Rune)normalized [0]; + Contents [Row, Col - 1].Attribute = CurrentAttribute; + Contents [Row, Col - 1].IsDirty = true; } else { - Contents [Row, Col, 1] = CurrentAttribute.Value; - Contents [Row, Col, 2] = 1; + Contents [Row, Col].Attribute = CurrentAttribute; + Contents [Row, Col].IsDirty = true; - if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumns () > 1) { + if (runeWidth < 2 && Col > 0 && Contents [Row, Col - 1].Runes [0].GetColumns () > 1) { // This is a single-width character, and we are not at the beginning of the line. - Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; - } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumns () > 1) { + Contents [Row, Col - 1].Runes [0] = Rune.ReplacementChar; + Contents [Row, Col - 1].IsDirty = true; + } else if (runeWidth < 2 && Col <= Clip.Right - 1 && Contents [Row, Col].Runes [0].GetColumns () > 1) { // This is a single-width character, and we are not at the end of the line. - Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; - Contents [Row, Col + 1, 2] = 1; + Contents [Row, Col + 1].Runes [0] = Rune.ReplacementChar; + Contents [Row, Col + 1].IsDirty = true; } if (runeWidth > 1 && Col == Clip.Right - 1) { // This is a double-width character, and we are at the end of the line. - Contents [Row, Col, 0] = Rune.ReplacementChar.Value; + Contents [Row, Col].Runes [0] = Rune.ReplacementChar; } else { // This is a single-width character, or we are not at the end of the line. - Contents [Row, Col, 0] = rune.Value; + Contents [Row, Col].Runes [0] = rune; } _dirtyLine [Row] = true; } @@ -634,8 +630,8 @@ public override void AddRune (Rune systemRune) if (runeWidth > 1) { // This is a double-width character, and we are not at the end of the line. if (validLocation && Col < Clip.Right) { - Contents [Row, Col, 1] = CurrentAttribute.Value; - Contents [Row, Col, 2] = 0; + Contents [Row, Col].Attribute = CurrentAttribute; + Contents [Row, Col].IsDirty = false; } Col++; } @@ -776,17 +772,20 @@ public virtual void ResizeScreen () public override void UpdateOffScreen () { - Contents = new int [Rows, Cols, 3]; + // TODO: This method is really "Clear Contents" now and should not be abstract (or virtual) + Contents = new Cell [Rows, Cols]; _dirtyLine = new bool [Rows]; lock (Contents) { // Can raise an exception while is still resizing. try { - for (int row = 0; row < Rows; row++) { - for (int c = 0; c < Cols; c++) { - Contents [row, c, 0] = ' '; - Contents [row, c, 1] = new Attribute (Color.White, Color.Black).Value; - Contents [row, c, 2] = 0; + for (var row = 0; row < Rows; row++) { + for (var c = 0; c < Cols; c++) { + Contents [row, c] = new Cell () { + Runes = new List { (Rune)' ' }, + Attribute = new Attribute (Color.White, Color.Black), + IsDirty = true + }; _dirtyLine [row] = true; } } @@ -802,7 +801,7 @@ public override void Refresh () public override void UpdateScreen () { - if (_winSizeChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols * 3 + if (_winSizeChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols || (!EnableConsoleScrolling && Rows != Console.WindowHeight) || (EnableConsoleScrolling && Rows != _largestBufferHeight)) { return; @@ -835,7 +834,7 @@ public override void UpdateScreen () lastCol = -1; var outputWidth = 0; for (; col < cols; col++) { - if (Contents [row, col, 2] == 0) { + if (!Contents [row, col].IsDirty) { if (output.Length > 0) { WriteToConsole (output, ref lastCol, row, ref outputWidth); } else if (lastCol == -1) { @@ -850,7 +849,7 @@ public override void UpdateScreen () lastCol = col; } - Attribute attr = new Attribute (Contents [row, col, 1]); + Attribute attr = Contents [row, col].Attribute.Value; // Performance: Only send the escape sequence if the attribute has changed. if (attr != redrawAttr) { redrawAttr = attr; @@ -858,13 +857,13 @@ public override void UpdateScreen () MapColors ((ConsoleColor)attr.Background, false), MapColors ((ConsoleColor)attr.Foreground, true))); } outputWidth++; - var rune = (Rune)Contents [row, col, 0]; + var rune = (Rune)Contents [row, col].Runes [0]; output.Append (rune.ToString ()); if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { WriteToConsole (output, ref lastCol, row, ref outputWidth); Console.CursorLeft--; } - Contents [row, col, 2] = 0; + Contents [row, col].IsDirty = false; } } if (output.Length > 0) { diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 4bcc91d685..d1f5b00194 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1425,140 +1425,58 @@ public override bool IsRuneSupported (Rune rune) return base.IsRuneSupported (rune) && rune.IsBmp; } + bool [] _dirtyLine; + public override void AddRune (Rune systemRune) { - if (IsValidLocation (Col, Row)) { - var rune = systemRune.MakePrintable (); - - if (!rune.IsBmp) { - rune = Rune.ReplacementChar; - } - Contents [Row, Col, 0] = rune.Value; - Contents [Row, Col, 1] = CurrentAttribute.Value; - _outputBuffer [GetOutputBufferPosition ()] = new WindowsConsole.ExtendedCharInfo ((char)rune.Value, CurrentAttribute); - WindowsConsole.SmallRect.Update (ref _damageRegion, (short)Col, (short)Row); - - if (Col > 0) { - var left = new Rune (Contents [Row, Col - 1, 0]); - if (left.GetColumns () > 1) { - int prevPosition = Row * Cols + (Col - 1); - Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; - _outputBuffer [prevPosition].Char = (char)Rune.ReplacementChar.Value; - } - } - if (rune.GetColumns () > 1 && Col == Clip.Right - 1) { - // This is a double-width character, and we are at the end of the line. - Contents [Row, Col, 0] = Rune.ReplacementChar.Value; - Contents [Row, Col, 1] = CurrentAttribute.Value; - Contents [Row, Col, 2] = 1; - _outputBuffer [GetOutputBufferPosition ()] = new WindowsConsole.ExtendedCharInfo ((char)Rune.ReplacementChar.Value, CurrentAttribute); - Col++; - } - - //var extraColumns = 0; - //ReadOnlySpan remainingInput = rune.ToString ().AsSpan (); - //while (!remainingInput.IsEmpty) { - // // Decode - // OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); - - // if (opStatus == OperationStatus.DestinationTooSmall || opStatus == OperationStatus.InvalidData) { - // result = Rune.ReplacementChar; - // } - // Contents [Row, Col + extraColumns, 0] = result.Value; - // Contents [Row, Col + extraColumns, 1] = CurrentAttribute; - - // // Slice and loop again - // remainingInput = remainingInput.Slice (charsConsumed); - // if (runeWidth > 1) { - // extraColumns++; - // Contents [Row, Col + extraColumns, 0] = ' '; - // Contents [Row, Col + extraColumns, 1] = CurrentAttribute; - // } - //} - } - //if (runeWidth == 0 && Col > 0) { + //int runeWidth = -1; + //var validLocation = IsValidLocation (Col, Row); + //if (validLocation) { + // var rune = systemRune.MakePrintable (); + // runeWidth = rune.GetColumns (); + // if (runeWidth == 0 && Col > 0) { // // This is a combining character, and we are not at the beginning of the line. // var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); // var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; // Contents [Row, Col - 1, 0] = normalized [0]; - // Contents [Row, Col - 1, 1] = CurrentAttribute; + // Contents [Row, Col - 1, 1] = CurrentAttribute.Value; // Contents [Row, Col - 1, 2] = 1; // } else { - // Contents [Row, Col, 1] = CurrentAttribute; + // Contents [Row, Col, 1] = CurrentAttribute.Value; // Contents [Row, Col, 2] = 1; // if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumns () > 1) { // // This is a single-width character, and we are not at the beginning of the line. // Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; - // } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumns () > 1) { // // This is a single-width character, and we are not at the end of the line. // Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; // Contents [Row, Col + 1, 2] = 1; // } - // if (runeWidth > 1 && Col == Clip.Right - 1) { // // This is a double-width character, and we are at the end of the line. // Contents [Row, Col, 0] = Rune.ReplacementChar.Value; // } else { // // This is a single-width character, or we are not at the end of the line. - // // Add the glyph (wide or not) - // //if (rune.IsBmp) { - // // Contents [Row, Col, 0] = rune.Value; - // // _outputBuffer [position].Char.UnicodeChar = (char)rune.Value; - // //} else { - // var column = Col; - // ReadOnlySpan remainingInput = rune.ToString ().AsSpan (); - // while (!remainingInput.IsEmpty) { - // // Decode - // OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); - - // if (opStatus != OperationStatus.Done) { - // result = Rune.ReplacementChar; - // } - // Contents [Row, column, 0] = result.Value; - // Contents [Row, column, 1] = CurrentAttribute; - - // // Slice and loop again - // remainingInput = remainingInput.Slice (charsConsumed); - // Col = ++column; - // } - // //// BUGBUG: workaround #2610 - // //Contents [Row, Col, 0] = (char)Rune.ReplacementChar.Value; - // //_outputBuffer [position].Char.UnicodeChar = (char)Rune.ReplacementChar.Value; - // //} + // Contents [Row, Col, 0] = rune.Value; // } + // _dirtyLine [Row] = true; // } //} - ////Col++; - ////if (runeWidth is < 0 or > 0) { - //// // This is a double-width codepoint, and we are not at the end of the line. - //// // Move to the right one and... - //// Col++; - ////} - - ////if (runeWidth > 1) { - //// // This is a double-width character, and we are not at the end of the line. - //// // ...clear the next cell. - //// if (validLocation && Col < Clip.Right) { - //// Contents [Row, Col, 1] = CurrentAttribute; - //// Contents [Row, Col, 2] = 0; - - //// //position = GetOutputBufferPosition (); - //// //_outputBuffer [position].Attributes = (ushort)CurrentAttribute; - - //// if (rune.IsBmp) { - //// // // BUGBUG: workaround #2610 - put a null char in the second position - //// //Contents [Row, Col, 0] = (char)0x00; - //// // _outputBuffer [position].Char.UnicodeChar = (char)0x00; - //// } - //// } - //// Col++; - ////} - Col++; + //if (runeWidth is < 0 or > 0) { + // Col++; + //} + //if (runeWidth > 1) { + // // This is a double-width character, and we are not at the end of the line. + // if (validLocation && Col < Clip.Right) { + // Contents [Row, Col, 1] = CurrentAttribute.Value; + // Contents [Row, Col, 2] = 0; + // } + // Col++; + //} } public override void Init (Action terminalResized) @@ -1603,6 +1521,8 @@ public virtual void ResizeScreen () Bottom = (short)Rows, Right = (short)Cols }; + _dirtyLine = new bool [Rows]; + WinConsole.ForceRefreshCursorVisibility (); if (!EnableConsoleScrolling) { Console.Out.Write (EscSeqUtils.CSI_ClearScreen (EscSeqUtils.ClearScreenOptions.CursorToEndOfScreen)); @@ -1611,20 +1531,26 @@ public virtual void ResizeScreen () public override void UpdateOffScreen () { - Contents = new int [Rows, Cols, 3]; - WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - - for (int row = 0; row < Rows; row++) { - for (int col = 0; col < Cols; col++) { - int position = row * Cols + col; - _outputBuffer [position].Attribute = Colors.TopLevel.Normal; - _outputBuffer [position].Char = ' '; - Contents [row, col, 0] = _outputBuffer [position].Char; - Contents [row, col, 1] = _outputBuffer [position].Attribute.Value; - Contents [row, col, 2] = 0; - WindowsConsole.SmallRect.Update (ref _damageRegion, (short)col, (short)row); - } - } + //Contents = new int [Rows, Cols, 3]; + //_dirtyLine = new bool [Rows]; + + //for (int row = 0; row < Rows; row++) { + // for (int col = 0; col < Cols; col++) { + // int position = row * Cols + col; + // _outputBuffer [position].Attribute = new Attribute (Color.White, Color.Black); + // _outputBuffer [position].Char = ' '; + // Contents [row, col, 0] = _outputBuffer [position].Char; + // Contents [row, col, 1] = _outputBuffer [position].Attribute.Value; + // Contents [row, col, 2] = 0; + // } + // _dirtyLine [row] = true; + //} + //_damageRegion = new WindowsConsole.SmallRect () { + // Top = 0, + // Left = 0, + // Bottom = (short)Rows, + // Right = (short)Cols + //}; } public override void UpdateScreen () @@ -1645,6 +1571,16 @@ public override void UpdateScreen () Y = (short)Clip.Height }; + for (int row = 0; row < Rows; row++) { + for (int col = 0; col < Cols; col++) { + int position = row * Cols + col; + //_outputBuffer [position].Char = (char)Contents [row, col].Runes [0]; + //_outputBuffer [position].Attribute = Contents [row, col].Attribute; + } + } + //WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); + + WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion, UseTrueColor); WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); } diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index ee4ba4b599..0f14a48d7f 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -120,7 +120,8 @@ public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelp for (int r = 0; r < driver.Rows; r++) { for (int c = 0; c < driver.Cols; c++) { - Rune rune = (Rune)contents [r, c, 0]; + // TODO: Remove hard-coded [0] once combining pairs is supported + Rune rune = contents [r, c].Runes[0]; if (rune.DecodeSurrogatePair (out char [] spair)) { sb.Append (spair); } else { @@ -174,7 +175,8 @@ public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestO for (var r = 0; r < driver.Rows; r++) { var runes = new List (); for (var c = 0; c < driver.Cols; c++) { - var rune = (Rune)contents [r, c, 0]; + // TODO: Remove hard-coded [0] once combining pairs is supported + Rune rune = contents [r, c].Runes [0]; if (rune != (Rune)' ') { if (x == -1) { x = c; @@ -267,7 +269,7 @@ public static void AssertDriverColorsAre (string expectedLook, params Attribute for (var c = 0; c < line.Length; c++) { - Attribute val = new Attribute( contents [r, c, 1]); + var val = contents [r, c].Attribute; var match = expectedColors.Where (e => e == val).ToList (); switch (match.Count) { @@ -305,15 +307,16 @@ internal static void AssertDriverUsedColors (params Attribute [] expectedColors) for (var r = 0; r < driver.Rows; r++) { for (var c = 0; c < driver.Cols; c++) { - var val = new Attribute(contents [r, c, 1]); + var val = contents [r, c].Attribute; + if (val.HasValue) { + colorsUsed.Add (val.Value); - colorsUsed.Add (val); + var match = toFind.FirstOrDefault (e => e == val); - var match = toFind.FirstOrDefault (e => e == val); - - // need to check twice because Attribute is a struct and therefore cannot be null - if (toFind.Any (e => e == val)) { - toFind.Remove (match); + // need to check twice because Attribute is a struct and therefore cannot be null + if (toFind.Any (e => e == val)) { + toFind.Remove (match); + } } }} From 362027bc3cd1c040c27bdb9cc634f2259206f37c Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sat, 15 Jul 2023 07:26:12 -0700 Subject: [PATCH 72/99] AddRune and ClearContents no longer virtual/abstract --- Terminal.Gui/Application.cs | 2 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 89 +- .../CursesDriver/CursesDriver.cs | 127 +- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 79 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 87 +- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 89 +- UnitTests/ConsoleDrivers/ClipRegionTests.cs | 17 +- UnitTests/View/Layout/LayoutTests.cs | 2 +- UnitTests/View/ViewTests.cs | 12 +- UnitTests/Views/ListViewTests.cs | 2 +- UnitTests/Views/ProgressBarTests.cs | 2352 ++++++++--------- UnitTests/Views/TableViewTests.cs | 2 +- UnitTests/Views/TextFieldTests.cs | 2 +- 13 files changed, 1298 insertions(+), 1564 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index 4fd20cef85..94da94c98c 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -554,7 +554,7 @@ public static void Run (Toplevel view, Func errorHandler = null /// public static void Refresh () { - Driver.UpdateOffScreen (); + Driver.ClearContents(); View last = null; foreach (var v in _toplevels.Reverse ()) { if (v.Visible) { diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index aa5b3e8d02..6721ce07ce 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -3,6 +3,7 @@ // using System.Text; using System; +using System.Collections.Generic; using System.Diagnostics; using static Terminal.Gui.ColorScheme; @@ -160,7 +161,63 @@ public virtual bool IsRuneSupported (Rune rune) /// /// /// Rune to add. - public abstract void AddRune (Rune rune); + public void AddRune (Rune rune) + { + int runeWidth = -1; + var validLocation = IsValidLocation (Col, Row); + if (validLocation) { + rune = rune.MakePrintable (); + runeWidth = rune.GetColumns (); + if (runeWidth == 0 && Col > 0) { + // This is a combining character, and we are not at the beginning of the line. + // TODO: Remove hard-coded [0] once combining pairs is supported + + // Convert Runes to string and concatenate + string combined = Contents [Row, Col - 1].Runes [0].ToString () + rune.ToString (); + + // Normalize to Form C (Canonical Composition) + string normalized = combined.Normalize (NormalizationForm.FormC); + + Contents [Row, Col - 1].Runes [0] = (Rune)normalized [0]; + Contents [Row, Col - 1].Attribute = CurrentAttribute; + Contents [Row, Col - 1].IsDirty = true; + } else { + Contents [Row, Col].Attribute = CurrentAttribute; + Contents [Row, Col].IsDirty = true; + + if (runeWidth < 2 && Col > 0 && Contents [Row, Col - 1].Runes [0].GetColumns () > 1) { + // This is a single-width character, and we are not at the beginning of the line. + Contents [Row, Col - 1].Runes [0] = Rune.ReplacementChar; + Contents [Row, Col - 1].IsDirty = true; + } else if (runeWidth < 2 && Col <= Clip.Right - 1 && Contents [Row, Col].Runes [0].GetColumns () > 1) { + // This is a single-width character, and we are not at the end of the line. + Contents [Row, Col + 1].Runes [0] = Rune.ReplacementChar; + Contents [Row, Col + 1].IsDirty = true; + } + if (runeWidth > 1 && Col == Clip.Right - 1) { + // This is a double-width character, and we are at the end of the line. + Contents [Row, Col].Runes [0] = Rune.ReplacementChar; + } else { + // This is a single-width character, or we are not at the end of the line. + Contents [Row, Col].Runes [0] = rune; + } + _dirtyLines [Row] = true; + } + } + + if (runeWidth is < 0 or > 0) { + Col++; + } + + if (runeWidth > 1) { + // This is a double-width character, and we are not at the end of the line. + if (validLocation && Col < Clip.Right) { + Contents [Row, Col].Attribute = CurrentAttribute; + Contents [Row, Col].IsDirty = false; + } + Col++; + } + } /// /// Adds the specified to the display at the current cursor position. This method @@ -243,10 +300,36 @@ public Rect Clip { /// upon success public abstract bool EnsureCursorVisibility (); + // As performance is a concern, we keep track of the dirty lines and only refresh those. + // This is in addition to the dirty flag on each cell. + internal bool [] _dirtyLines; + /// - /// Clears the buffer and the driver buffer. + /// Clears the of the driver. /// - public abstract void UpdateOffScreen (); + public void ClearContents () + { + // TODO: This method is really "Clear Contents" now and should not be abstract (or virtual) + Contents = new Cell [Rows, Cols]; + _dirtyLines = new bool [Rows]; + + lock (Contents) { + // Can raise an exception while is still resizing. + try { + for (var row = 0; row < Rows; row++) { + for (var c = 0; c < Cols; c++) { + Contents [row, c] = new Cell () { + Runes = new List { (Rune)' ' }, + Attribute = new Attribute (Color.White, Color.Black), + IsDirty = true + }; + _dirtyLines [row] = true; + } + } + } catch (IndexOutOfRangeException) { } + } + + } /// /// Redraws the physical screen with the contents that have been queued up via any of the printing commands. diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index a75a538aca..cc446bc4db 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -47,115 +47,6 @@ public override bool IsRuneSupported (Rune rune) return base.IsRuneSupported (rune) && rune.IsBmp; } - public override void AddRune (Rune systemRune) - { - //var rune = systemRune.MakePrintable (); - //var runeWidth = rune.GetColumns (); - //var validLocation = IsValidLocation (Col, Row); - - //if (validLocation) { - // if (!_atValidLocation) { - // // Move was called with an invalid location. - // // Since then, the clip changed, and now we are at a valid location. - // Curses.move (Row, Col); - // _atValidLocation = false; - // } - // if (runeWidth == 0 && Col > 0) { - // // This is a combining character, and we are not at the beginning of the line. - // var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); - // var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; - // Contents [Row, Col - 1, 0] = normalized [0]; - // Contents [Row, Col - 1, 1] = CurrentAttribute.Value; - // Contents [Row, Col - 1, 2] = 1; - // Curses.attrset (Contents [Row, Col - 1, 1]); - // Curses.mvaddch (Row, Col - 1, normalized [0]); - // } else { - // Contents [Row, Col, 1] = CurrentAttribute.Value; - // Contents [Row, Col, 2] = 1; - - // if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumns () > 1) { - // // This is a single-width character, and we are not at the beginning of the line. - // var curAttr = CurrentAttribute; - // Curses.attrset (Contents [Row, Col - 1, 1]); - // Curses.mvaddch (Row, Col - 1, Rune.ReplacementChar.Value); - // Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; - // Curses.move (Row, Col); - // Curses.attrset (curAttr.Value); - - // } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumns () > 1) { - // // This is a single-width character, and we are not at the end of the line. - // var curAttr = CurrentAttribute; - // Curses.attrset (Contents [Row, Col + 1, 1]); - // Curses.mvaddch (Row, Col + 1, Rune.ReplacementChar.Value); - // Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; - // Curses.move (Row, Col); - // Curses.attrset (curAttr.Value); - - // } - // if (runeWidth > 1 && Col == Clip.Right - 1) { - // // This is a double-width character, and we are at the end of the line. - // Curses.addch (Rune.ReplacementChar.Value); - // Contents [Row, Col, 0] = Rune.ReplacementChar.Value; - // } else { - // // This is a single-width character, or we are not at the end of the line. - - // var curAttr = CurrentAttribute; - // Curses.attrset (Contents [Row, Col, 1]); - - // if (rune.IsBmp) { - // Contents [Row, Col, 0] = rune.Value; - // Curses.addch (Contents [Row, Col, 0]); - // } else { - // var column = Col; - // ReadOnlySpan remainingInput = rune.ToString ().AsSpan (); - // while (!remainingInput.IsEmpty) { - // // Decode - // OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); - - // if (opStatus != OperationStatus.Done) { - // result = Rune.ReplacementChar; - // } - // Contents [Row, column, 0] = result.Value; - // Contents [Row, column, 1] = CurrentAttribute.Value; - - // Curses.attrset (Contents [Row, column, 1]); - // // BUGBUG: workaround curses not supporting non BMP? # - // Curses.mvaddch (Row, column, Rune.ReplacementChar.Value); - // //Curses.mvaddch (Row, column, Contents [Row, column, 0]); - - // // Slice and loop again - // remainingInput = remainingInput.Slice (charsConsumed); - // column++; - // } - // Curses.move (Row, Col); - // } - // Curses.attrset (curAttr.Value); - // } - // } - //} else { - // _atValidLocation = false; - //} - - //if (runeWidth is < 0 or > 0) { - // Col++; - //} - - //if (runeWidth > 1) { - // // This is a double-width character, and we are not at the end of the line. - // if (validLocation && Col < Clip.Right) { - // Contents [Row, Col, 1] = CurrentAttribute.Value; - // Contents [Row, Col, 2] = 0; - - // //if (rune.IsBmp) { - // // // BUGBUG: workaround #2610 - // // Contents [Row, Col, 0] = (char)0x00; - // // Curses.addch (Contents [Row, Col, 0]); - // //} - // } - // Col++; - //} - } - public override void Refresh () { Curses.raw (); @@ -168,7 +59,7 @@ private void ProcessWinChange () { if (Curses.CheckWinChange ()) { ResizeScreen (); - UpdateOffScreen (); + ClearContents (); TerminalResized?.Invoke (); } } @@ -231,7 +122,7 @@ static short ColorToCursesColorNumber (Color color) case Color.BrightCyan: return Curses.COLOR_CYAN | Curses.COLOR_GRAY; case Color.BrightRed: - return Curses.COLOR_RED | Curses.COLOR_GRAY; + return Curses.COLOR_RED | Curses.COLOR_GRAY; case Color.BrightMagenta: return Curses.COLOR_MAGENTA | Curses.COLOR_GRAY; case Color.BrightYellow: @@ -689,7 +580,7 @@ public override void Init (Action terminalResized) } ResizeScreen (); - UpdateOffScreen (); + ClearContents (); } @@ -699,18 +590,6 @@ public virtual void ResizeScreen () Curses.refresh (); } - public override void UpdateOffScreen () - { - //Contents = new int [Rows, Cols, 3]; - //for (int row = 0; row < Rows; row++) { - // for (int col = 0; col < Cols; col++) { - // Contents [row, col, 0] = ' '; - // Contents [row, col, 1] = Colors.TopLevel.Normal.Value; - // Contents [row, col, 2] = 0; - // } - //} - } - public static bool Is_WSL_Platform () { // xclip does not work on WSL, so we need to use the Windows clipboard vis Powershell diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index d27a65109f..93dfdacc6f 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -63,61 +63,7 @@ public FakeDriver () } } } - - public override void AddRune (Rune rune) - { - //rune = rune.MakePrintable (); - //var runeWidth = rune.GetColumns (); - //var validLocation = IsValidLocation (Col, Row); - - //if (validLocation) { - // if (rune.IsCombiningMark () && Col > 0) { - // // Decode the previous rune - // var previousRune = new Rune (Contents [Row, Col - 1, 0]); - // var newCombo = new StringBuilder (); - // ReadOnlySpan remainingInput = previousRune.ToString ().AsSpan (); - // while (!remainingInput.IsEmpty) { - // // Decode - // OperationStatus opStatus = Rune.DecodeFromUtf16 (remainingInput, out Rune result, out int charsConsumed); - - // if (opStatus is OperationStatus.DestinationTooSmall or OperationStatus.InvalidData) { - // result = Rune.ReplacementChar; - // } - - // newCombo.Append (result); - // // Slice and loop again - // remainingInput = remainingInput [charsConsumed..]; - // } - // newCombo.Append (rune); - - // var combined = newCombo.ToString (); - // var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; - // Contents [Row, Col - 1, 0] = normalized [0];// BUGBUG: This is wrong, we need to handle the case where the rune is more than one char - // Contents [Row, Col - 1, 1] = CurrentAttribute.Value; - // Contents [Row, Col - 1, 2] = 1; - // Col--; - // } else { - // Contents [Row, Col, 0] = rune.Value; - // Contents [Row, Col, 1] = CurrentAttribute.Value; - - // if (Col > 0) { - // var left = new Rune (Contents [Row, Col - 1, 0]); - // if (left.GetColumns () > 1) { - // Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; - // } - // } - - // if (runeWidth > 1) { - // Col++; - // Contents [Row, Col, 0] = Rune.ReplacementChar.Value; - // Contents [Row, Col, 1] = CurrentAttribute.Value; - // Contents [Row, Col, 2] = 1; - // } - // } - //} - //Col++; - } - + public override void End () { FakeConsole.ResetColor (); @@ -138,7 +84,7 @@ public override void Init (Action terminalResized) // Call InitializeColorSchemes before UpdateOffScreen as it references Colors CurrentAttribute = MakeColor (Color.White, Color.Black); InitializeColorSchemes (); - UpdateOffScreen (); + ClearContents(); } @@ -510,7 +456,7 @@ public void SetWindowPosition (int left, int top) void ProcessResize () { ResizeScreen (); - UpdateOffScreen (); + ClearContents(); TerminalResized?.Invoke (); } @@ -544,25 +490,6 @@ public virtual void ResizeScreen () Clip = new Rect (0, 0, Cols, Rows); } - public override void UpdateOffScreen () - { - //Contents = new int [Rows, Cols, 3]; - //_dirtyLine = new bool [Rows]; - - //// Can raise an exception while is still resizing. - //try { - // for (int row = 0; row < Rows; row++) { - // for (int c = 0; c < Cols; c++) { - // Contents [row, c, 0] = ' '; - // Contents [row, c, 1] = 0; - // Contents [row, c, 2] = 0; - // _dirtyLine [row] = true; - // } - // } - //} catch (IndexOutOfRangeException) { } - //Clip = new Rect (0, 0, Cols, Rows); - } - public override void UpdateCursor () { if (!EnsureCursorVisibility ()) { diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 3eec16da56..1dfc3a8736 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -582,62 +582,6 @@ public NetDriver () { } - bool [] _dirtyLine; - - public override void AddRune (Rune systemRune) - { - int runeWidth = -1; - var validLocation = IsValidLocation (Col, Row); - if (validLocation) { - var rune = systemRune.MakePrintable (); - runeWidth = rune.GetColumns (); - if (runeWidth == 0 && Col > 0) { - // This is a combining character, and we are not at the beginning of the line. - // TODO: Remove hard-coded [0] once combining pairs is supported - var combined = $"{Contents [Row, Col - 1].Runes [0]}{rune.Value}"; - var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; - Contents [Row, Col - 1].Runes [0] = (Rune)normalized [0]; - Contents [Row, Col - 1].Attribute = CurrentAttribute; - Contents [Row, Col - 1].IsDirty = true; - } else { - Contents [Row, Col].Attribute = CurrentAttribute; - Contents [Row, Col].IsDirty = true; - - if (runeWidth < 2 && Col > 0 && Contents [Row, Col - 1].Runes [0].GetColumns () > 1) { - // This is a single-width character, and we are not at the beginning of the line. - Contents [Row, Col - 1].Runes [0] = Rune.ReplacementChar; - Contents [Row, Col - 1].IsDirty = true; - } else if (runeWidth < 2 && Col <= Clip.Right - 1 && Contents [Row, Col].Runes [0].GetColumns () > 1) { - // This is a single-width character, and we are not at the end of the line. - Contents [Row, Col + 1].Runes [0] = Rune.ReplacementChar; - Contents [Row, Col + 1].IsDirty = true; - } - if (runeWidth > 1 && Col == Clip.Right - 1) { - // This is a double-width character, and we are at the end of the line. - Contents [Row, Col].Runes [0] = Rune.ReplacementChar; - } else { - // This is a single-width character, or we are not at the end of the line. - Contents [Row, Col].Runes [0] = rune; - } - _dirtyLine [Row] = true; - } - } - - if (runeWidth is < 0 or > 0) { - Col++; - } - - if (runeWidth > 1) { - // This is a double-width character, and we are not at the end of the line. - if (validLocation && Col < Clip.Right) { - Contents [Row, Col].Attribute = CurrentAttribute; - Contents [Row, Col].IsDirty = false; - } - Col++; - } - - } - public override void End () { _mainLoop._netEvents.StopTasks (); @@ -708,7 +652,7 @@ public override void Init (Action terminalResized) } ResizeScreen (); - UpdateOffScreen (); + ClearContents(); CurrentAttribute = MakeColor (Color.White, Color.Black); InitializeColorSchemes (); @@ -770,29 +714,6 @@ public virtual void ResizeScreen () Clip = new Rect (0, 0, Cols, Rows); } - public override void UpdateOffScreen () - { - // TODO: This method is really "Clear Contents" now and should not be abstract (or virtual) - Contents = new Cell [Rows, Cols]; - _dirtyLine = new bool [Rows]; - - lock (Contents) { - // Can raise an exception while is still resizing. - try { - for (var row = 0; row < Rows; row++) { - for (var c = 0; c < Cols; c++) { - Contents [row, c] = new Cell () { - Runes = new List { (Rune)' ' }, - Attribute = new Attribute (Color.White, Color.Black), - IsDirty = true - }; - _dirtyLine [row] = true; - } - } - } catch (IndexOutOfRangeException) { } - } - } - public override void Refresh () { UpdateScreen (); @@ -822,13 +743,13 @@ public override void UpdateScreen () if (Console.WindowHeight < 1) { return; } - if (!_dirtyLine [row]) { + if (!_dirtyLines [row]) { continue; } if (!SetCursorPosition (0, row)) { return; } - _dirtyLine [row] = false; + _dirtyLines [row] = false; output.Clear (); for (var col = left; col < cols; col++) { lastCol = -1; @@ -1250,7 +1171,7 @@ void ProcessInput (NetEvents.InputResult inputEvent) Cols = inputEvent.WindowSizeEvent.Size.Width; Rows = _largestBufferHeight; ResizeScreen (); - UpdateOffScreen (); + ClearContents(); _winSizeChanging = false; TerminalResized?.Invoke (); break; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index d1f5b00194..6d9b8da30f 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -798,7 +798,7 @@ private void ChangeWin (Size e) Cols = newSize.Width; Rows = newSize.Height; ResizeScreen (); - UpdateOffScreen (); + ClearContents (); TerminalResized.Invoke (); } } @@ -926,7 +926,7 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) } //System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}"); ResizeScreen (); - UpdateOffScreen (); + ClearContents (); TerminalResized?.Invoke (); break; @@ -1424,61 +1424,7 @@ public override bool IsRuneSupported (Rune rune) { return base.IsRuneSupported (rune) && rune.IsBmp; } - - bool [] _dirtyLine; - - public override void AddRune (Rune systemRune) - { - - //int runeWidth = -1; - //var validLocation = IsValidLocation (Col, Row); - //if (validLocation) { - // var rune = systemRune.MakePrintable (); - // runeWidth = rune.GetColumns (); - // if (runeWidth == 0 && Col > 0) { - // // This is a combining character, and we are not at the beginning of the line. - // var combined = new String (new char [] { (char)Contents [Row, Col - 1, 0], (char)rune.Value }); - // var normalized = !combined.IsNormalized () ? combined.Normalize () : combined; - // Contents [Row, Col - 1, 0] = normalized [0]; - // Contents [Row, Col - 1, 1] = CurrentAttribute.Value; - // Contents [Row, Col - 1, 2] = 1; - // } else { - // Contents [Row, Col, 1] = CurrentAttribute.Value; - // Contents [Row, Col, 2] = 1; - - // if (runeWidth < 2 && Col > 0 && ((Rune)(Contents [Row, Col - 1, 0])).GetColumns () > 1) { - // // This is a single-width character, and we are not at the beginning of the line. - // Contents [Row, Col - 1, 0] = Rune.ReplacementChar.Value; - // } else if (runeWidth < 2 && Col <= Clip.Right - 1 && ((Rune)(Contents [Row, Col, 0])).GetColumns () > 1) { - // // This is a single-width character, and we are not at the end of the line. - // Contents [Row, Col + 1, 0] = Rune.ReplacementChar.Value; - // Contents [Row, Col + 1, 2] = 1; - // } - // if (runeWidth > 1 && Col == Clip.Right - 1) { - // // This is a double-width character, and we are at the end of the line. - // Contents [Row, Col, 0] = Rune.ReplacementChar.Value; - // } else { - // // This is a single-width character, or we are not at the end of the line. - // Contents [Row, Col, 0] = rune.Value; - // } - // _dirtyLine [Row] = true; - // } - //} - - //if (runeWidth is < 0 or > 0) { - // Col++; - //} - - //if (runeWidth > 1) { - // // This is a double-width character, and we are not at the end of the line. - // if (validLocation && Col < Clip.Right) { - // Contents [Row, Col, 1] = CurrentAttribute.Value; - // Contents [Row, Col, 2] = 0; - // } - // Col++; - //} - } - + public override void Init (Action terminalResized) { TerminalResized = terminalResized; @@ -1504,7 +1450,7 @@ public override void Init (Action terminalResized) InitializeColorSchemes (); ResizeScreen (); - UpdateOffScreen (); + ClearContents (); } public virtual void ResizeScreen () @@ -1521,37 +1467,14 @@ public virtual void ResizeScreen () Bottom = (short)Rows, Right = (short)Cols }; - _dirtyLine = new bool [Rows]; + _dirtyLines = new bool [Rows]; WinConsole.ForceRefreshCursorVisibility (); if (!EnableConsoleScrolling) { Console.Out.Write (EscSeqUtils.CSI_ClearScreen (EscSeqUtils.ClearScreenOptions.CursorToEndOfScreen)); } } - - public override void UpdateOffScreen () - { - //Contents = new int [Rows, Cols, 3]; - //_dirtyLine = new bool [Rows]; - - //for (int row = 0; row < Rows; row++) { - // for (int col = 0; col < Cols; col++) { - // int position = row * Cols + col; - // _outputBuffer [position].Attribute = new Attribute (Color.White, Color.Black); - // _outputBuffer [position].Char = ' '; - // Contents [row, col, 0] = _outputBuffer [position].Char; - // Contents [row, col, 1] = _outputBuffer [position].Attribute.Value; - // Contents [row, col, 2] = 0; - // } - // _dirtyLine [row] = true; - //} - //_damageRegion = new WindowsConsole.SmallRect () { - // Top = 0, - // Left = 0, - // Bottom = (short)Rows, - // Right = (short)Cols - //}; - } + public override void UpdateScreen () { diff --git a/UnitTests/ConsoleDrivers/ClipRegionTests.cs b/UnitTests/ConsoleDrivers/ClipRegionTests.cs index 619b166e0a..9afaa05611 100644 --- a/UnitTests/ConsoleDrivers/ClipRegionTests.cs +++ b/UnitTests/ConsoleDrivers/ClipRegionTests.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Text; using Xunit; using Xunit.Abstractions; @@ -90,24 +91,24 @@ public void AddRune_Is_Clipped (Type driverType) driver.Move (0, 0); driver.AddRune ('x'); - Assert.Equal ('x', driver.Contents [0, 0, 0]); + Assert.Equal ((Rune)'x', driver.Contents [0, 0].Runes [0]); driver.Move (5, 5); driver.AddRune ('x'); - Assert.Equal ('x', driver.Contents [5, 5, 0]); + Assert.Equal ((Rune)'x', driver.Contents [5, 5].Runes [0]); // Clear the contents driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), ' '); - Assert.Equal (' ', driver.Contents [0, 0, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes [0]); // Setup the region with a single rectangle, fill screen with 'x' driver.Clip = new Rect (5, 5, 5, 5); driver.FillRect (new Rect (0, 0, driver.Rows, driver.Cols), 'x'); - Assert.Equal (' ', (char)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (char)driver.Contents [4, 9, 0]); - Assert.Equal ('x', (char)driver.Contents [5, 5, 0]); - Assert.Equal ('x', (char)driver.Contents [9, 9, 0]); - Assert.Equal (' ', (char)driver.Contents [10, 10, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes [0]); + Assert.Equal ((Rune)' ', driver.Contents [4, 9].Runes [0]); + Assert.Equal ((Rune)'x', driver.Contents [5, 5].Runes [0]); + Assert.Equal ((Rune)'x', driver.Contents [9, 9].Runes [0]); + Assert.Equal ((Rune)' ', driver.Contents [10, 10].Runes [0]); Application.Shutdown (); } diff --git a/UnitTests/View/Layout/LayoutTests.cs b/UnitTests/View/Layout/LayoutTests.cs index c6bfb8bd6f..5b6c4492fe 100644 --- a/UnitTests/View/Layout/LayoutTests.cs +++ b/UnitTests/View/Layout/LayoutTests.cs @@ -550,7 +550,7 @@ string GetContents () { var text = ""; for (int i = 0; i < 4; i++) { - text += (char)Application.Driver.Contents [0, i, 0]; + text += Application.Driver.Contents [0, i].Runes[0]; } return text; } diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index 92bbd9251a..7273c5e8f6 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -534,11 +534,11 @@ public void LabelChangeText_RendersCorrectly_Constructors (int choice) Application.Begin (Application.Top); // should have the initial text - Assert.Equal ('t', driver.Contents [0, 0, 0]); - Assert.Equal ('e', driver.Contents [0, 1, 0]); - Assert.Equal ('s', driver.Contents [0, 2, 0]); - Assert.Equal ('t', driver.Contents [0, 3, 0]); - Assert.Equal (' ', driver.Contents [0, 4, 0]); + Assert.Equal ((Rune)'t', driver.Contents [0, 0].Runes [0]); + Assert.Equal ((Rune)'e', driver.Contents [0, 1].Runes [0]); + Assert.Equal ((Rune)'s', driver.Contents [0, 2].Runes [0]); + Assert.Equal ((Rune)'t', driver.Contents [0, 3].Runes [0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes [0]); } finally { Application.Shutdown (); } @@ -680,7 +680,7 @@ int RunesCount () for (int i = 0; i < Application.Driver.Rows; i++) { for (int j = 0; j < Application.Driver.Cols; j++) { - if (contents [i, j, 0] != ' ') { + if (contents [i, j].Runes[0] != (Rune)' ') { runesCount++; } } diff --git a/UnitTests/Views/ListViewTests.cs b/UnitTests/Views/ListViewTests.cs index ff011910f4..d96d869ccd 100644 --- a/UnitTests/Views/ListViewTests.cs +++ b/UnitTests/Views/ListViewTests.cs @@ -240,7 +240,7 @@ string GetContents (int line) { var item = ""; for (int i = 0; i < 7; i++) { - item += (char)Application.Driver.Contents [line, i, 0]; + item += Application.Driver.Contents [line, i].Runes[0]; } return item; } diff --git a/UnitTests/Views/ProgressBarTests.cs b/UnitTests/Views/ProgressBarTests.cs index 841ffafac8..e04d4566b8 100644 --- a/UnitTests/Views/ProgressBarTests.cs +++ b/UnitTests/Views/ProgressBarTests.cs @@ -178,613 +178,613 @@ public void Pulse_Redraw_BidirectionalMarquee_True_Default () pb.Pulse (); pb.Draw (); if (i == 0) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 1) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 2) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 3) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 4) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 5) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 6) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 7) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 8) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 9) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 10) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 11) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 12) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 13) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 14) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 15) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 16) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 17) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 18) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 19) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 20) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 21) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 22) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 23) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 24) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 25) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 26) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 27) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 28) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 29) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 30) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 31) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 32) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 33) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 34) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 35) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 36) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 37) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } } } @@ -808,613 +808,613 @@ public void Pulse_Redraw_BidirectionalMarquee_False () pb.Pulse (); pb.Draw (); if (i == 0) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 1) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 2) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 3) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 4) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 5) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 6) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 7) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 8) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 9) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 10) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 11) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 12) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 13) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 14) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 15) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 16) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 17) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 18) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 19) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 20) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 21) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 22) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 23) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 24) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 25) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 26) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 27) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 28) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 29) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 30) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 31) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 32) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 14].Runes[0]); } else if (i == 33) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 34) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 35) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 36) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } else if (i == 37) { - Assert.Equal (' ', (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 6, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 7, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 8, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 9, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 10, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 11, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 12, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 13, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 14, 0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 6].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 7].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 8].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 9].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 10].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 11].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 12].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 13].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Runes[0]); } } } @@ -1436,47 +1436,47 @@ public void Fraction_Redraw () pb.Fraction += 0.2F; pb.Draw (); if (i == 0) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); } else if (i == 1) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); } else if (i == 2) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); } else if (i == 3) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); } else if (i == 4) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); } else if (i == 5) { - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 0, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 1, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 2, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 3, 0]); - Assert.Equal (CM.Glyphs.BlocksMeterSegment.Value, (double)driver.Contents [0, 4, 0]); - Assert.Equal (' ', (double)driver.Contents [0, 5, 0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Runes[0]); + Assert.Equal (CM.Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Runes[0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 5].Runes[0]); } } } diff --git a/UnitTests/Views/TableViewTests.cs b/UnitTests/Views/TableViewTests.cs index d179adef69..f5f8f3b763 100644 --- a/UnitTests/Views/TableViewTests.cs +++ b/UnitTests/Views/TableViewTests.cs @@ -1901,7 +1901,7 @@ public void LongColumnTest () // Now test making the width too small for the MinAcceptableWidth // the Column won't fit so should not be rendered var driver = ((FakeDriver)Application.Driver); - driver.UpdateOffScreen (); + driver.ClearContents (); tableView.Bounds = new Rect (0, 0, 9, 5); diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index 2dba686584..5a4582dc1d 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -1137,7 +1137,7 @@ string GetContents () { var item = ""; for (int i = 0; i < 16; i++) { - item += (char)Application.Driver.Contents [0, i, 0]; + item += Application.Driver.Contents [0, i].Runes [0]; } return item; } From 21fa6c3f9df63c7fbac10b6fc8954e1659bc24dd Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sun, 16 Jul 2023 08:03:48 -0700 Subject: [PATCH 73/99] WindowsDriver renders correctly again --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 7 +++- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 43 +++++++++++++++----- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 6721ce07ce..9b3a1a778b 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -210,10 +210,13 @@ public void AddRune (Rune rune) } if (runeWidth > 1) { - // This is a double-width character, and we are not at the end of the line. + Debug.Assert(runeWidth <= 2); if (validLocation && Col < Clip.Right) { - Contents [Row, Col].Attribute = CurrentAttribute; + // This is a double-width character, and we are not at the end of the line. + // Col now points to the second column of the character. Ensure it doesn't + // Get rendered. Contents [Row, Col].IsDirty = false; + Contents [Row, Col].Attribute = CurrentAttribute; } Col++; } diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 6d9b8da30f..6bf7accaa5 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -79,7 +79,14 @@ private bool WriteConsoleTrueColorOutput (ExtendedCharInfo [] charInfoBuffer) _stringBuilder.Append (EscSeqUtils.CSI_SetBackgroundColorRGB (attr.TrueColorBackground.Value.Red, attr.TrueColorBackground.Value.Green, attr.TrueColorBackground.Value.Blue)); } - _stringBuilder.Append (info.Char != '\x1b' ? info.Char : ' '); + if (info.Char != '\x1b') { + if (!info.Empty) { + _stringBuilder.Append (info.Char); + } + + } else { + _stringBuilder.Append (' '); + } } _stringBuilder.Append (EscSeqUtils.CSI_RestoreCursorPosition); @@ -506,11 +513,13 @@ public struct CharInfo { public struct ExtendedCharInfo { public char Char { get; set; } public Attribute Attribute { get; set; } + public bool Empty { get; set; } // TODO: Temp hack until virutal terminal sequences public ExtendedCharInfo (char character, Attribute attribute) { Char = character; Attribute = attribute; + Empty = false; } } @@ -1424,7 +1433,7 @@ public override bool IsRuneSupported (Rune rune) { return base.IsRuneSupported (rune) && rune.IsBmp; } - + public override void Init (Action terminalResized) { TerminalResized = terminalResized; @@ -1474,14 +1483,10 @@ public virtual void ResizeScreen () Console.Out.Write (EscSeqUtils.CSI_ClearScreen (EscSeqUtils.ClearScreenOptions.CursorToEndOfScreen)); } } - + public override void UpdateScreen () { - //if (_damageRegion.Left == -1) { - // return; - //} - if (!EnableConsoleScrolling) { var windowSize = WinConsole.GetConsoleBufferWindow (out _); if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) { @@ -1497,12 +1502,28 @@ public override void UpdateScreen () for (int row = 0; row < Rows; row++) { for (int col = 0; col < Cols; col++) { int position = row * Cols + col; - //_outputBuffer [position].Char = (char)Contents [row, col].Runes [0]; - //_outputBuffer [position].Attribute = Contents [row, col].Attribute; + _outputBuffer [position].Attribute = Contents [row, col].Attribute.GetValueOrDefault (); + if (Contents [row, col].IsDirty == false) { + _outputBuffer [position].Empty = true; + _outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; + continue; + } + _outputBuffer [position].Empty = false; + if (Contents [row, col].Runes [0].IsBmp) { + _outputBuffer [position].Char = (char)Contents [row, col].Runes [0].Value; + } else { + //_outputBuffer [position].Empty = true; + _outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; + if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { + // TODO: This is a hack to deal with non-BMP and wide characters. + col++; + position = row * Cols + col; + _outputBuffer [position].Empty = false; + _outputBuffer [position].Char = ' '; + } + } } } - //WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion, UseTrueColor); WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); From ad725ce663235ed7932f29867315d8c906abf8c4 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sun, 16 Jul 2023 13:44:49 -0500 Subject: [PATCH 74/99] Progress on Curses --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 9 +++- .../CursesDriver/CursesDriver.cs | 41 ++++++++++++++++++- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 5 +++ UICatalog/Scenarios/CharacterMap.cs | 13 +++++- 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 9b3a1a778b..29d38af86d 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -198,8 +198,12 @@ public void AddRune (Rune rune) // This is a double-width character, and we are at the end of the line. Contents [Row, Col].Runes [0] = Rune.ReplacementChar; } else { - // This is a single-width character, or we are not at the end of the line. - Contents [Row, Col].Runes [0] = rune; + if (runeWidth == 1) { + // This is a single-width character, or we are not at the end of the line. + Contents [Row, Col].Runes [0] = rune; + } else { + Contents [Row, Col].Runes [0] = (Rune)'*'; + } } _dirtyLines [Row] = true; } @@ -217,6 +221,7 @@ public void AddRune (Rune rune) // Get rendered. Contents [Row, Col].IsDirty = false; Contents [Row, Col].Attribute = CurrentAttribute; + //Contents [Row, Col].Runes [0] = (Rune)' '; } Col++; } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index cc446bc4db..27e07706c6 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -53,6 +53,7 @@ public override void Refresh () Curses.noecho (); Curses.refresh (); ProcessWinChange (); + UpdateScreen (); } private void ProcessWinChange () @@ -197,7 +198,45 @@ public override void End () Curses.endwin (); } - public override void UpdateScreen () => _window.redrawwin (); + public override void UpdateScreen () + { + for (int row = 0; row < Rows; row++) { + if (!_dirtyLines [row]) { + continue; + } + _dirtyLines [row] = false; + + for (int col = 0; col < Cols; col++) { + //Curses.mvaddch (row, col, '+'); + + if (Contents [row, col].IsDirty == false) { + //Curses.mvaddch (row, col, (char)Rune.ReplacementChar.Value); + continue; + } + Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().Value); + + if (Contents [row, col].Runes [0].IsBmp) { + Curses.mvaddch (row, col, Contents [row, col].Runes [0].Value); + } else { + Curses.mvaddch (row, col, Contents [row, col].Runes [0].Value); + //_outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; + if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { + // TODO: This is a hack to deal with non-BMP and wide characters. + //col++; + //_outputBuffer [position].Empty = false; + //_outputBuffer [position].Char = ' '; + //Curses.mvaddch (row, col, '*'); + } + } + //if (col < Cols && Contents [row, col].Runes [0].GetColumns () > 1) { + // col++; + // Curses.mvaddch (row, col, '='); + //} + } + } + + _window.redrawwin (); + } public Curses.Window _window; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 6bf7accaa5..551da271c5 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1500,6 +1500,11 @@ public override void UpdateScreen () }; for (int row = 0; row < Rows; row++) { + if (!_dirtyLines [row]) { + continue; + } + _dirtyLines [row] = false; + for (int col = 0; col < Cols; col++) { int position = row * Cols + col; _outputBuffer [position].Attribute = Contents [row, col].Attribute.GetValueOrDefault (); diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 0d8bb288a3..7e89d35f8d 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -398,16 +398,25 @@ public override void OnDrawContentComplete (Rect contentArea) Move (firstColumnX + COLUMN_WIDTH, y); Driver.SetAttribute (GetNormalColor ()); for (int col = 0; col < 16; col++) { + var x = firstColumnX + COLUMN_WIDTH * col + 1; + + //Move (x, y); + //Driver.SetAttribute (GetNormalColor ()); + //Driver.AddRune ('!'); + //Driver.AddRune ('@'); + //Driver.AddRune ('#'); + Move (x, y); if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { Driver.SetAttribute (GetFocusColor ()); } - if (val + col < 0xffff && char.IsSurrogate ((char)(val + col))) { + var rune = new Rune (val + col); + if (val + col < 0xffff && char.IsSurrogate ((char)(val + col)) || rune.GetColumns() == 0) { Driver.AddRune (Rune.ReplacementChar); } else { - Driver.AddRune (new Rune (val + col)); + Driver.AddRune (rune); } if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { From 78b3434bbbe8f5b06b452fd630c6f623afcc4274 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 17 Jul 2023 06:49:00 -0600 Subject: [PATCH 75/99] Progress on Curses --- Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 27e07706c6..74bebaba27 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -10,6 +10,7 @@ using System.Text; using Unix.Terminal; using System.Buffers; +using System.Data; namespace Terminal.Gui; @@ -228,6 +229,11 @@ public override void UpdateScreen () //Curses.mvaddch (row, col, '*'); } } + + if (Contents [row, col].Runes [0].IsSurrogatePair () && Contents [row, col].Runes [0].GetColumns () < 2) { + col--; + } + //if (col < Cols && Contents [row, col].Runes [0].GetColumns () > 1) { // col++; // Curses.mvaddch (row, col, '='); From 12df06932e2748eef6e8f18be2f7bf295b5bb645 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Mon, 17 Jul 2023 17:36:44 -0600 Subject: [PATCH 76/99] broke windowsdriver --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 29d38af86d..b16f492ca4 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -198,12 +198,12 @@ public void AddRune (Rune rune) // This is a double-width character, and we are at the end of the line. Contents [Row, Col].Runes [0] = Rune.ReplacementChar; } else { - if (runeWidth == 1) { + //if (runeWidth == 1) { // This is a single-width character, or we are not at the end of the line. Contents [Row, Col].Runes [0] = rune; - } else { - Contents [Row, Col].Runes [0] = (Rune)'*'; - } + //} else { + //Contents [Row, Col].Runes [0] = (Rune)'*'; + //} } _dirtyLines [Row] = true; } From 4cc0c29759ac767edaff5b249cd0e18f664298f7 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 19 Jul 2023 08:55:09 -0600 Subject: [PATCH 77/99] Cleaned up FakeMainLoop --- .../ConsoleDrivers/FakeDriver/FakeMainLoop.cs | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs index 7cbe1d7d63..69b9686603 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs @@ -1,21 +1,19 @@ using System; -using System.Threading; -using System.Threading.Tasks; namespace Terminal.Gui; -public class FakeMainLoop : IMainLoopDriver { - private MainLoop _mainLoop; + +internal class FakeMainLoop : IMainLoopDriver { public Action KeyPressed; public FakeMainLoop (ConsoleDriver consoleDriver = null) { - // consoleDriver is not needed/used in FakeConsole + // No implementation needed for FakeMainLoop } - + public void Setup (MainLoop mainLoop) { - _mainLoop = mainLoop; + // No implementation needed for FakeMainLoop } public void Wakeup () @@ -25,33 +23,10 @@ public void Wakeup () public bool EventsPending (bool wait) { - //if (CheckTimers (wait, out var waitTimeout)) { - // return true; - //} - // Always return true for FakeMainLoop return true; } - //private bool CheckTimers (bool wait, out int waitTimeout) - //{ - // long now = DateTime.UtcNow.Ticks; - - // if (_mainLoop.timeouts.Count > 0) { - // waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); - // if (waitTimeout < 0) - // return true; - // } else { - // waitTimeout = -1; - // } - - // if (!wait) { - // waitTimeout = 0; - // } - - // return _mainLoop.idleHandlers.Count > 0; - //} - public void Iteration () { if (FakeConsole.MockKeyPresses.Count > 0) { From c3dc548941efa87f45cbf27cc60b4c78f2ab86a7 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 19 Jul 2023 08:59:28 -0600 Subject: [PATCH 78/99] Cleaned up some build warnings --- .../ConsoleDrivers/FakeDriver/FakeConsole.cs | 11 ----------- UnitTests/FileServices/FileDialogTests.cs | 18 +++++++++--------- UnitTests/Text/StringTests.cs | 2 +- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs index 45b7469f0d..69b2032765 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs @@ -807,17 +807,6 @@ public static int Read () // The System.Console.In property is redirected from some stream other than the // console. //[SecuritySafeCritical] - /// - /// - /// - //public static ConsoleKeyInfo ReadKey (bool intercept) - //{ - // if (MockKeyPresses.Count > 0) { - // return MockKeyPresses.Pop (); - // } else { - // return new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', false, false, false); - // } - //} /// /// A stack of keypresses to return when ReadKey is called. diff --git a/UnitTests/FileServices/FileDialogTests.cs b/UnitTests/FileServices/FileDialogTests.cs index 5d1b1f282a..96e3012339 100644 --- a/UnitTests/FileServices/FileDialogTests.cs +++ b/UnitTests/FileServices/FileDialogTests.cs @@ -217,14 +217,14 @@ public void MultiSelectDirectory_CannotToggleDotDot (bool acceptWithEnter) Assert.Multiple ( () => { // Only the subfolder should be selected - Assert.Equal (1, dlg.MultiSelected.Count); + Assert.Single (dlg.MultiSelected); AssertIsTheSubfolder (dlg.Path); AssertIsTheSubfolder (dlg.MultiSelected.Single ()); }, () => { // Event should also agree with the final state Assert.NotNull (eventMultiSelected); - Assert.Equal (1, eventMultiSelected.Count); + Assert.Single (eventMultiSelected); AssertIsTheSubfolder (eventMultiSelected.Single ()); } ); @@ -330,14 +330,14 @@ public void MultiSelectDirectory_CanToggleThenAccept (bool acceptWithEnter) Assert.Multiple ( () => { // Only the subfolder should be selected - Assert.Equal (1, dlg.MultiSelected.Count); + Assert.Single (dlg.MultiSelected); AssertIsTheSubfolder (dlg.Path); AssertIsTheSubfolder (dlg.MultiSelected.Single ()); }, () => { // Event should also agree with the final state Assert.NotNull (eventMultiSelected); - Assert.Equal (1, eventMultiSelected.Count); + Assert.Single (eventMultiSelected); AssertIsTheSubfolder (eventMultiSelected.Single ()); } ); @@ -382,7 +382,7 @@ public void TestDirectoryContents_Linux () fd.Style.Culture = new CultureInfo ("en-US"); fd.Draw (); - + string expected = @$" ┌──────────────────────────────────────────────────────────────────┐ @@ -467,7 +467,7 @@ public void TestDirectoryContents_Windows_Colors () fd.Draw (); - TestHelpers.AssertDriverUsedColors (other,dir,img,exe); + TestHelpers.AssertDriverUsedColors (other, dir, img, exe); } private ColorScheme GetColorScheme (Attribute a) @@ -487,7 +487,7 @@ private ColorScheme GetColorScheme (Attribute a) [InlineData (".csv", "c:\\MyFile.csv", true)] [InlineData (".csv", "c:\\MyFile.CSV", true)] [InlineData (".csv", "c:\\MyFile.csv.bak", false)] - public void TestAllowedType_Basic(string allowed, string candidate, bool expected) + public void TestAllowedType_Basic (string allowed, string candidate, bool expected) { Assert.Equal (expected, new AllowedType ("Test", allowed).IsAllowed (candidate)); } @@ -496,7 +496,7 @@ public void TestAllowedType_Basic(string allowed, string candidate, bool expecte [InlineData ("Dockerfile", "c:\\temp\\Dockerfile", true)] [InlineData ("Dockerfile", "Dockerfile", true)] [InlineData ("Dockerfile", "someimg.Dockerfile", true)] - public void TestAllowedType_SpecificFile(string allowed, string candidate, bool expected) + public void TestAllowedType_SpecificFile (string allowed, string candidate, bool expected) { Assert.Equal (expected, new AllowedType ("Test", allowed).IsAllowed (candidate)); } @@ -504,7 +504,7 @@ public void TestAllowedType_SpecificFile(string allowed, string candidate, bool [Theory] [InlineData (".Designer.cs", "c:\\MyView.Designer.cs", true)] [InlineData (".Designer.cs", "c:\\temp/MyView.Designer.cs", true)] - [InlineData(".Designer.cs","MyView.Designer.cs",true)] + [InlineData (".Designer.cs", "MyView.Designer.cs", true)] [InlineData (".Designer.cs", "c:\\MyView.DESIGNER.CS", true)] [InlineData (".Designer.cs", "MyView.cs", false)] public void TestAllowedType_DoubleBarreled (string allowed, string candidate, bool expected) diff --git a/UnitTests/Text/StringTests.cs b/UnitTests/Text/StringTests.cs index 4b708034e9..8c9ee36bbd 100644 --- a/UnitTests/Text/StringTests.cs +++ b/UnitTests/Text/StringTests.cs @@ -16,7 +16,7 @@ public void TestGetColumns_Empty () public void TestGetColumns_Null () { string? str = null; - Assert.Equal (0, str.GetColumns ()); + Assert.Equal (0, str!.GetColumns ()); } [Fact] From c0c6dda348f4678348495cbcfa8e83cd1f21a0b4 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 19 Jul 2023 09:11:11 -0600 Subject: [PATCH 79/99] Removed _init from AutoInitShutdown as it's not needed anymore --- Terminal.Gui/Drawing/LineCanvas.cs | 5 +++-- UnitTests/TestHelpers.cs | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Terminal.Gui/Drawing/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas.cs index b5ad69b952..86a43494d4 100644 --- a/Terminal.Gui/Drawing/LineCanvas.cs +++ b/Terminal.Gui/Drawing/LineCanvas.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -142,7 +143,7 @@ public StraightLine RemoveLastLine() _lines.Remove(l); } - return l; + return l!; } /// diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index 0f14a48d7f..8d733a5871 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -55,7 +55,6 @@ public AutoInitShutdownAttribute (bool autoInit = true, ConfigurationManager.Locations = configLocation; } - static bool _init = false; bool AutoInit { get; } Type _driverType; @@ -64,7 +63,6 @@ public override void Before (MethodInfo methodUnderTest) Debug.WriteLine ($"Before: {methodUnderTest.Name}"); if (AutoInit) { Application.Init ((ConsoleDriver)Activator.CreateInstance (_driverType)); - _init = true; } } @@ -73,7 +71,6 @@ public override void After (MethodInfo methodUnderTest) Debug.WriteLine ($"After: {methodUnderTest.Name}"); if (AutoInit) { Application.Shutdown (); - _init = false; } } } From f0cf55adbde2fbbd68ff39e11ec2cf8bbc56c9a1 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 19 Jul 2023 09:11:53 -0600 Subject: [PATCH 80/99] Removed unused var --- Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 74bebaba27..f489c47c5a 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -24,21 +24,16 @@ internal class CursesDriver : ConsoleDriver { CursorVisibility? _initialCursorVisibility = null; CursorVisibility? _currentCursorVisibility = null; - // If true, Move set Col and Row to an invalid location - bool _atValidLocation; - public override void Move (int col, int row) { base.Move (col, row); if (IsValidLocation (col, row)) { Curses.move (row, col); - _atValidLocation = true; } else { // Not a valid location (outside screen or clip region) // Move within the clip region, then AddRune will actually move to Col, Row Curses.move (Clip.Y, Clip.X); - _atValidLocation = false; } } From 33e158380df69fe0565b2912329a261458e9ba0f Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 19 Jul 2023 09:12:19 -0600 Subject: [PATCH 81/99] Removed unused var --- Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 93dfdacc6f..e8874105db 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -42,9 +42,6 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN public static FakeDriver.Behaviors FakeBehaviors = new Behaviors (); - // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag - bool [] _dirtyLine; - public FakeDriver () { if (FakeBehaviors.UseFakeClipboard) { From 7569ea7cb0684a9c2793adac005d4ce5558dc4e3 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 19 Jul 2023 09:13:31 -0600 Subject: [PATCH 82/99] Fixed nullabiltiy warning in LineCanvas --- Terminal.Gui/Drawing/LineCanvas.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/Drawing/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas.cs index 86a43494d4..698b32e89c 100644 --- a/Terminal.Gui/Drawing/LineCanvas.cs +++ b/Terminal.Gui/Drawing/LineCanvas.cs @@ -74,7 +74,7 @@ public LineCanvas() ConfigurationManager.Applied += ConfigurationManager_Applied; } - private void ConfigurationManager_Applied (object sender, ConfigurationManagerEventArgs e) + private void ConfigurationManager_Applied (object? sender, ConfigurationManagerEventArgs e) { foreach (var irr in runeResolvers) { irr.Value.SetGlyphs (); From 3f823b4ea646dff82dd7e745567258b0c66bf2df Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 19 Jul 2023 10:23:33 -0600 Subject: [PATCH 83/99] Fixed charmap crash --- .../ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs | 1 - UICatalog/Scenarios/CharacterMap.cs | 17 +++++------------ UICatalog/Scenarios/TableEditor.cs | 1 - 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs index 182e58bea0..3d71b1a21f 100644 --- a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs @@ -396,7 +396,6 @@ public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, Cons public static void DecodeEscSeq (EscSeqRequests escSeqRequests, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod, out string c1Control, out string code, out string [] values, out string terminator, out bool isMouse, out List buttonState, out Point pos, out bool isResponse, Action continuousButtonPressedHandler) { char [] kChars = GetKeyCharArray (cki); - Debug.WriteLine ($"EscSeq: {new string(kChars)}"); (c1Control, code, values, terminator) = GetEscapeResult (kChars); isMouse = false; buttonState = new List () { 0 }; diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 7e89d35f8d..20e46db531 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -11,6 +11,7 @@ using System.Text.Unicode; using System.Threading.Tasks; using Terminal.Gui; +using static Terminal.Gui.SpinnerStyle; using static Terminal.Gui.TableView; namespace UICatalog.Scenarios; @@ -401,22 +402,15 @@ public override void OnDrawContentComplete (Rect contentArea) var x = firstColumnX + COLUMN_WIDTH * col + 1; - //Move (x, y); - //Driver.SetAttribute (GetNormalColor ()); - //Driver.AddRune ('!'); - //Driver.AddRune ('@'); - //Driver.AddRune ('#'); - Move (x, y); if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { Driver.SetAttribute (GetFocusColor ()); } - - var rune = new Rune (val + col); - if (val + col < 0xffff && char.IsSurrogate ((char)(val + col)) || rune.GetColumns() == 0) { - Driver.AddRune (Rune.ReplacementChar); + var scalar = val + col; + if (Rune.IsValid (scalar)) { + Driver.AddRune (new Rune (scalar)); } else { - Driver.AddRune (rune); + Driver.AddRune ((Rune)'?'); } if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { @@ -646,7 +640,6 @@ public static List GetRanges () new UnicodeRange (0x1F130, 0x1F149 ,"Squared Latin Capital Letters"), new UnicodeRange (0x12400, 0x1240f ,"Cuneiform Numbers and Punctuation"), - new UnicodeRange (0x1FA00, 0x1FA0f ,"Chess Symbols"), new UnicodeRange (0x10000, 0x1007F ,"Linear B Syllabary"), new UnicodeRange (0x10080, 0x100FF ,"Linear B Ideograms"), new UnicodeRange (0x10100, 0x1013F ,"Aegean Numbers"), diff --git a/UICatalog/Scenarios/TableEditor.cs b/UICatalog/Scenarios/TableEditor.cs index b392a72f73..d172d59b7a 100644 --- a/UICatalog/Scenarios/TableEditor.cs +++ b/UICatalog/Scenarios/TableEditor.cs @@ -786,7 +786,6 @@ public UnicodeRange (uint start, uint end, string category) new UnicodeRange(0x2b00, 0x2bff,"Miscellaneous Symbols and Arrows"), new UnicodeRange(0xFB00, 0xFb4f,"Alphabetic Presentation Forms"), new UnicodeRange(0x12400, 0x1240f,"Cuneiform Numbers and Punctuation"), - new UnicodeRange(0x1FA00, 0x1FA0f,"Chess Symbols"), new UnicodeRange((uint)(CharMap.MaxCodePoint - 16), (uint)CharMap.MaxCodePoint,"End"), new UnicodeRange (0x0020 ,0x007F ,"Basic Latin"), From 43c69a49ffc2facda3544d30edd26abaf1f83766 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 19 Jul 2023 10:27:24 -0600 Subject: [PATCH 84/99] Fixes #2758 in v2 --- UnitTests/TestHelpers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index 8d733a5871..535b3bcd3c 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -11,6 +11,7 @@ using Attribute = Terminal.Gui.Attribute; using Microsoft.VisualStudio.TestPlatform.Utilities; using Xunit.Sdk; +using System.Globalization; namespace Terminal.Gui; // This class enables test functions annotated with the [AutoInitShutdown] attribute to @@ -45,9 +46,8 @@ public AutoInitShutdownAttribute (bool autoInit = true, bool fakeClipboardIsSupportedAlwaysTrue = false, ConfigurationManager.ConfigLocations configLocation = ConfigurationManager.ConfigLocations.DefaultOnly) { - //Assert.True (autoInit == false && consoleDriverType == null); - AutoInit = autoInit; + CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US"); _driverType = consoleDriverType ?? typeof (FakeDriver); FakeDriver.FakeBehaviors.UseFakeClipboard = useFakeClipboard; FakeDriver.FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; From b80772229a446a24cd7365820d398a9443ef52e2 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 26 Jul 2023 07:41:51 -0600 Subject: [PATCH 85/99] Port testonfail fix to v2 --- .github/workflows/dotnet-core.yml | 1 + .github/workflows/publish.yml | 1 + .gitignore | 1 + ...l_TIGGER_2023-07-06.14_58_23.cobertura.xml | 251649 --------------- UnitTests/AssemblyInfo.cs | 5 - UnitTests/UnitTests.csproj | 5 + UnitTests/xunit.runner.json | 7 +- 7 files changed, 14 insertions(+), 251655 deletions(-) delete mode 100644 TestResults/15ea9649-0d81-41ee-aae3-4f1f2c15810a/charl_TIGGER_2023-07-06.14_58_23.cobertura.xml diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 6c951c2453..39bc0906e4 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -29,6 +29,7 @@ jobs: - name: Test run: | + sed -i 's/"stopOnFail": false/"stopOnFail": true/g' UnitTests/xunit.runner.json dotnet test --no-restore --verbosity normal --collect:"XPlat Code Coverage" --settings UnitTests/coverlet.runsettings mv -v UnitTests/TestResults/*/*.* UnitTests/TestResults/ diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 34ef36b1c7..5149575773 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -50,6 +50,7 @@ jobs: #- name: Test to generate Code Coverage Report # run: | + # sed -i 's/"stopOnFail": false/"stopOnFail": true/g' UnitTests/xunit.runner.json # dotnet test --verbosity normal --collect:"XPlat Code Coverage" --settings UnitTests/coverlet.runsettings # mv -v UnitTests/TestResults/*/*.* UnitTests/TestResults/ diff --git a/.gitignore b/.gitignore index 8376953074..8aab8590be 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ docfx/api docs/ UnitTests/TestResults +TestResults #git merge files *.orig diff --git a/TestResults/15ea9649-0d81-41ee-aae3-4f1f2c15810a/charl_TIGGER_2023-07-06.14_58_23.cobertura.xml b/TestResults/15ea9649-0d81-41ee-aae3-4f1f2c15810a/charl_TIGGER_2023-07-06.14_58_23.cobertura.xml deleted file mode 100644 index ff0872dddd..0000000000 --- a/TestResults/15ea9649-0d81-41ee-aae3-4f1f2c15810a/charl_TIGGER_2023-07-06.14_58_23.cobertura.xml +++ /dev/null @@ -1,251649 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/UnitTests/AssemblyInfo.cs b/UnitTests/AssemblyInfo.cs index e5b9a14979..97d54f7cfc 100644 --- a/UnitTests/AssemblyInfo.cs +++ b/UnitTests/AssemblyInfo.cs @@ -1,6 +1 @@ global using CM = Terminal.Gui.ConfigurationManager; -using Xunit; - -// Since Application is a singleton we can't run tests in parallel -[assembly: CollectionBehavior (DisableTestParallelization = true)] - diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index d6690e6632..58015ee29a 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -39,6 +39,11 @@ + + + PreserveNewest + + False diff --git a/UnitTests/xunit.runner.json b/UnitTests/xunit.runner.json index 136d06384a..c096d4186e 100644 --- a/UnitTests/xunit.runner.json +++ b/UnitTests/xunit.runner.json @@ -1 +1,6 @@ - \ No newline at end of file +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeTestCollections": false, + "parallelizeAssembly": false, + "stopOnFail": false +} \ No newline at end of file From 53a51ed81a1960be9aa95d2f176dd8faffe70d48 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 26 Jul 2023 08:41:43 -0600 Subject: [PATCH 86/99] Remove EnableConsoleScrolling --- .github/CODEOWNERS | 4 +- Terminal.Gui/Application.cs | 39 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 18 - .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 54 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 2925 ++++++------ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 4120 +++++++++-------- Terminal.Gui/Resources/config.json | 1 - Terminal.sln | 4 +- UICatalog/UICatalog.cs | 24 - UnitTests/Application/ApplicationTests.cs | 2 - .../Configuration/ConfigurationMangerTests.cs | 9 - UnitTests/Configuration/SettingsScopeTests.cs | 6 - .../ConsoleDrivers/ConsoleDriverTests.cs | 11 - .../ConsoleDrivers/ConsoleScrolllingTests.cs | 122 +- 14 files changed, 3733 insertions(+), 3606 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2e79252a05..ed35267b2f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,7 +2,9 @@ # the repo. Unless a later match takes precedence, # @global-owner1 and @global-owner2 will be requested for # review when someone opens a pull request. -* @migueldeicaza @tig +* @tig + +/docs/ @tig @bdisp @tznind # Order is important; the last matching pattern takes the most # precedence. When someone opens a pull request that only diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index 94da94c98c..cb513aff04 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -54,42 +54,7 @@ public static partial class Application { // For Unit testing - ignores UseSystemConsole internal static bool _forceFakeConsole; - - private static bool? _enableConsoleScrolling; - /// - /// The current used in the terminal. - /// - /// - /// - /// If (the default) the height of the Terminal.Gui application () - /// tracks to the height of the visible console view when the console is resized. In this case - /// scrolling in the console will be disabled and all will remain visible. - /// - /// - /// If then height of the Terminal.Gui application only tracks - /// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). - /// In this case console scrolling is enabled and the contents ( high) will scroll - /// as the console scrolls. - /// - /// This API was previously named 'HeightAsBuffer` but was renamed to make its purpose more clear. - /// - [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] - public static bool EnableConsoleScrolling { - get { - if (Driver == null) { - return _enableConsoleScrolling.HasValue && _enableConsoleScrolling.Value; - } - return Driver.EnableConsoleScrolling; - } - set { - _enableConsoleScrolling = value; - if (Driver == null) { - return; - } - Driver.EnableConsoleScrolling = value; - } - } - + private static List _cachedSupportedCultures; /// @@ -225,7 +190,6 @@ internal static void InternalInit (Func topLevelFactory, ConsoleDriver MainLoop = new MainLoop (mainLoopDriver); try { - Driver.EnableConsoleScrolling = EnableConsoleScrolling; Driver.Init (OnTerminalResized); } catch (InvalidOperationException ex) { // This is a case where the driver is unable to initialize the console. @@ -291,7 +255,6 @@ static void ResetState () NotifyStopRunState = null; _initialized = false; _mouseGrabView = null; - _enableConsoleScrolling = false; _lastMouseOwnerView = null; // Reset synchronization context to allow the user to run async/await, diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index b16f492ca4..15bf166be8 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -61,24 +61,6 @@ public abstract class ConsoleDriver { /// public IClipboard Clipboard { get; internal set; } - /// - /// - /// If (the default) the height of the Terminal.Gui application () - /// tracks to the height of the visible console view when the console is resized. In this case - /// scrolling in the console will be disabled and all will remain visible. - /// - /// - /// If then height of the Terminal.Gui application only tracks - /// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). - /// In this case console scrolling is enabled and the contents ( high) will scroll - /// as the console scrolls. - /// - /// - /// - /// NOTE: This functionaliy is currently broken on Windows Terminal. - /// - public bool EnableConsoleScrolling { get; set; } - /// /// The contents of the application output. The driver outputs this buffer to the terminal when /// is called. diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index e8874105db..d6a965b9cb 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -60,7 +60,7 @@ public FakeDriver () } } } - + public override void End () { FakeConsole.ResetColor (); @@ -81,7 +81,7 @@ public override void Init (Action terminalResized) // Call InitializeColorSchemes before UpdateOffScreen as it references Colors CurrentAttribute = MakeColor (Color.White, Color.Black); InitializeColorSchemes (); - ClearContents(); + ClearContents (); } @@ -419,31 +419,24 @@ public void SetBufferSize (int width, int height) FakeConsole.SetBufferSize (width, height); Cols = width; Rows = height; - if (!EnableConsoleScrolling) { - SetWindowSize (width, height); - } + SetWindowSize (width, height); ProcessResize (); } public void SetWindowSize (int width, int height) { FakeConsole.SetWindowSize (width, height); - if (!EnableConsoleScrolling) { - if (width != Cols || height != Rows) { - SetBufferSize (width, height); - Cols = width; - Rows = height; - } + if (width != Cols || height != Rows) { + SetBufferSize (width, height); + Cols = width; + Rows = height; } ProcessResize (); } public void SetWindowPosition (int left, int top) { - if (EnableConsoleScrolling) { - Left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); - Top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); - } else if (Left > 0 || Top > 0) { + if (Left > 0 || Top > 0) { Left = 0; Top = 0; } @@ -453,33 +446,22 @@ public void SetWindowPosition (int left, int top) void ProcessResize () { ResizeScreen (); - ClearContents(); + ClearContents (); TerminalResized?.Invoke (); } public virtual void ResizeScreen () { - if (!EnableConsoleScrolling) { - if (FakeConsole.WindowHeight > 0) { - // Can raise an exception while is still resizing. - try { - FakeConsole.CursorTop = 0; - FakeConsole.CursorLeft = 0; - FakeConsole.WindowTop = 0; - FakeConsole.WindowLeft = 0; - } catch (System.IO.IOException) { - return; - } catch (ArgumentOutOfRangeException) { - return; - } - } - } else { + if (FakeConsole.WindowHeight > 0) { + // Can raise an exception while is still resizing. try { -#pragma warning disable CA1416 - FakeConsole.WindowLeft = Math.Max (Math.Min (Left, Cols - FakeConsole.WindowWidth), 0); - FakeConsole.WindowTop = Math.Max (Math.Min (Top, Rows - FakeConsole.WindowHeight), 0); -#pragma warning restore CA1416 - } catch (Exception) { + FakeConsole.CursorTop = 0; + FakeConsole.CursorLeft = 0; + FakeConsole.WindowTop = 0; + FakeConsole.WindowLeft = 0; + } catch (System.IO.IOException) { + return; + } catch (ArgumentOutOfRangeException) { return; } } diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 1dfc3a8736..d6fbd2481c 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -14,1296 +14,1404 @@ using System.Text; namespace Terminal.Gui; -internal class NetWinVTConsole { - IntPtr _inputHandle, _outputHandle, _errorHandle; - uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode; - - public NetWinVTConsole () - { - _inputHandle = GetStdHandle (STD_INPUT_HANDLE); - if (!GetConsoleMode (_inputHandle, out uint mode)) { - throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}."); - } - _originalInputConsoleMode = mode; - if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) { - mode |= ENABLE_VIRTUAL_TERMINAL_INPUT; - if (!SetConsoleMode (_inputHandle, mode)) { - throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}."); - } - } - - _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); - if (!GetConsoleMode (_outputHandle, out mode)) { - throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}."); - } - _originalOutputConsoleMode = mode; - if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; - if (!SetConsoleMode (_outputHandle, mode)) { - throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}."); - } - } - - _errorHandle = GetStdHandle (STD_ERROR_HANDLE); - if (!GetConsoleMode (_errorHandle, out mode)) { - throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}."); - } - _originalErrorConsoleMode = mode; - if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { - mode |= DISABLE_NEWLINE_AUTO_RETURN; - if (!SetConsoleMode (_errorHandle, mode)) { - throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}."); - } - } - } - - public void Cleanup () - { - if (!SetConsoleMode (_inputHandle, _originalInputConsoleMode)) { - throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}."); - } - if (!SetConsoleMode (_outputHandle, _originalOutputConsoleMode)) { - throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}."); - } - if (!SetConsoleMode (_errorHandle, _originalErrorConsoleMode)) { - throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}."); - } - } - - const int STD_INPUT_HANDLE = -10; - const int STD_OUTPUT_HANDLE = -11; - const int STD_ERROR_HANDLE = -12; - - // Input modes. - const uint ENABLE_PROCESSED_INPUT = 1; - const uint ENABLE_LINE_INPUT = 2; - const uint ENABLE_ECHO_INPUT = 4; - const uint ENABLE_WINDOW_INPUT = 8; - const uint ENABLE_MOUSE_INPUT = 16; - const uint ENABLE_INSERT_MODE = 32; - const uint ENABLE_QUICK_EDIT_MODE = 64; - const uint ENABLE_EXTENDED_FLAGS = 128; - const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512; - - // Output modes. - const uint ENABLE_PROCESSED_OUTPUT = 1; - const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2; - const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4; - const uint DISABLE_NEWLINE_AUTO_RETURN = 8; - const uint ENABLE_LVB_GRID_WORLDWIDE = 10; - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern IntPtr GetStdHandle (int nStdHandle); - - [DllImport ("kernel32.dll")] - static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode); - - [DllImport ("kernel32.dll")] - static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode); - - [DllImport ("kernel32.dll")] - static extern uint GetLastError (); +internal class NetWinVTConsole +{ + IntPtr _inputHandle, _outputHandle, _errorHandle; + uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode; + + public NetWinVTConsole() + { + _inputHandle = GetStdHandle(STD_INPUT_HANDLE); + if (!GetConsoleMode(_inputHandle, out uint mode)) + { + throw new ApplicationException($"Failed to get input console mode, error code: {GetLastError()}."); + } + _originalInputConsoleMode = mode; + if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) + { + mode |= ENABLE_VIRTUAL_TERMINAL_INPUT; + if (!SetConsoleMode(_inputHandle, mode)) + { + throw new ApplicationException($"Failed to set input console mode, error code: {GetLastError()}."); + } + } + + _outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); + if (!GetConsoleMode(_outputHandle, out mode)) + { + throw new ApplicationException($"Failed to get output console mode, error code: {GetLastError()}."); + } + _originalOutputConsoleMode = mode; + if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) + { + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; + if (!SetConsoleMode(_outputHandle, mode)) + { + throw new ApplicationException($"Failed to set output console mode, error code: {GetLastError()}."); + } + } + + _errorHandle = GetStdHandle(STD_ERROR_HANDLE); + if (!GetConsoleMode(_errorHandle, out mode)) + { + throw new ApplicationException($"Failed to get error console mode, error code: {GetLastError()}."); + } + _originalErrorConsoleMode = mode; + if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) + { + mode |= DISABLE_NEWLINE_AUTO_RETURN; + if (!SetConsoleMode(_errorHandle, mode)) + { + throw new ApplicationException($"Failed to set error console mode, error code: {GetLastError()}."); + } + } + } + + public void Cleanup() + { + if (!SetConsoleMode(_inputHandle, _originalInputConsoleMode)) + { + throw new ApplicationException($"Failed to restore input console mode, error code: {GetLastError()}."); + } + if (!SetConsoleMode(_outputHandle, _originalOutputConsoleMode)) + { + throw new ApplicationException($"Failed to restore output console mode, error code: {GetLastError()}."); + } + if (!SetConsoleMode(_errorHandle, _originalErrorConsoleMode)) + { + throw new ApplicationException($"Failed to restore error console mode, error code: {GetLastError()}."); + } + } + + const int STD_INPUT_HANDLE = -10; + const int STD_OUTPUT_HANDLE = -11; + const int STD_ERROR_HANDLE = -12; + + // Input modes. + const uint ENABLE_PROCESSED_INPUT = 1; + const uint ENABLE_LINE_INPUT = 2; + const uint ENABLE_ECHO_INPUT = 4; + const uint ENABLE_WINDOW_INPUT = 8; + const uint ENABLE_MOUSE_INPUT = 16; + const uint ENABLE_INSERT_MODE = 32; + const uint ENABLE_QUICK_EDIT_MODE = 64; + const uint ENABLE_EXTENDED_FLAGS = 128; + const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512; + + // Output modes. + const uint ENABLE_PROCESSED_OUTPUT = 1; + const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2; + const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4; + const uint DISABLE_NEWLINE_AUTO_RETURN = 8; + const uint ENABLE_LVB_GRID_WORLDWIDE = 10; + + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr GetStdHandle(int nStdHandle); + + [DllImport("kernel32.dll")] + static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); + + [DllImport("kernel32.dll")] + static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); + + [DllImport("kernel32.dll")] + static extern uint GetLastError(); } -internal class NetEvents { - ManualResetEventSlim _inputReady = new ManualResetEventSlim (false); - ManualResetEventSlim _waitForStart = new ManualResetEventSlim (false); - ManualResetEventSlim _winChange = new ManualResetEventSlim (false); - Queue _inputResultQueue = new Queue (); - ConsoleDriver _consoleDriver; - volatile ConsoleKeyInfo [] _cki = null; - volatile static bool _isEscSeq; - int _lastWindowHeight; - bool _stopTasks; +internal class NetEvents +{ + ManualResetEventSlim _inputReady = new ManualResetEventSlim(false); + ManualResetEventSlim _waitForStart = new ManualResetEventSlim(false); + ManualResetEventSlim _winChange = new ManualResetEventSlim(false); + Queue _inputResultQueue = new Queue(); + ConsoleDriver _consoleDriver; + volatile ConsoleKeyInfo[] _cki = null; + volatile static bool _isEscSeq; + bool _stopTasks; #if PROCESS_REQUEST bool _neededProcessRequest; #endif - public EscSeqRequests EscSeqRequests { get; } = new EscSeqRequests (); - - public NetEvents (ConsoleDriver consoleDriver) - { - _consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver)); - Task.Run (ProcessInputResultQueue); - Task.Run (CheckWindowSizeChange); - } - - internal void StopTasks () - { - _stopTasks = true; - } - - public InputResult? ReadConsoleInput () - { - while (true) { - if (_stopTasks) { - return null; - } - _waitForStart.Set (); - _winChange.Set (); - - if (_inputResultQueue.Count == 0) { - _inputReady.Wait (); - _inputReady.Reset (); - } + public EscSeqRequests EscSeqRequests { get; } = new EscSeqRequests(); + + public NetEvents(ConsoleDriver consoleDriver) + { + _consoleDriver = consoleDriver ?? throw new ArgumentNullException(nameof(consoleDriver)); + Task.Run(ProcessInputResultQueue); + Task.Run(CheckWindowSizeChange); + } + + internal void StopTasks() + { + _stopTasks = true; + } + + public InputResult? ReadConsoleInput() + { + while (true) + { + if (_stopTasks) + { + return null; + } + _waitForStart.Set(); + _winChange.Set(); + + if (_inputResultQueue.Count == 0) + { + _inputReady.Wait(); + _inputReady.Reset(); + } #if PROCESS_REQUEST _neededProcessRequest = false; #endif - if (_inputResultQueue.Count > 0) { - return _inputResultQueue.Dequeue (); - } - } - } - - void ProcessInputResultQueue () - { - while (true) { - _waitForStart.Wait (); - _waitForStart.Reset (); - - if (_inputResultQueue.Count == 0) { - ConsoleKey key = 0; - ConsoleModifiers mod = 0; - ConsoleKeyInfo newConsoleKeyInfo = default; - - while (true) { - ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true); - if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq) - || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) { - if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) { - _cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, - false, false, false), _cki); - } - _isEscSeq = true; - newConsoleKeyInfo = consoleKeyInfo; - _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki); - if (Console.KeyAvailable) continue; - ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod); - _cki = null; - _isEscSeq = false; - break; - } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) { - if (_cki != null) { - ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod); - _cki = null; - } - break; - } else { - _inputResultQueue.Enqueue (new InputResult { - EventType = EventType.Key, - ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo) - }); - _isEscSeq = false; - break; - } - } - } - - _inputReady.Set (); - } - } - - void CheckWindowSizeChange () - { - void RequestWindowSize () - { - while (true) { - // Wait for a while then check if screen has changed sizes - Task.Delay (500).Wait (); - - if (_stopTasks) { - return; - } - int buffHeight, buffWidth; - if (((NetDriver)_consoleDriver).IsWinPlatform) { - buffHeight = Math.Max (Console.BufferHeight, 0); - buffWidth = Math.Max (Console.BufferWidth, 0); - } else { - buffHeight = _consoleDriver.Rows; - buffWidth = _consoleDriver.Cols; - } - if (EnqueueWindowSizeEvent ( - Math.Max (Console.WindowHeight, 0), - Math.Max (Console.WindowWidth, 0), - buffHeight, - buffWidth)) { - - return; - } - } - } - - while (true) { - if (_stopTasks) { - return; - } - _winChange.Wait (); - _winChange.Reset (); - RequestWindowSize (); - _inputReady.Set (); - } - } - - /// - /// Enqueue a window size event if the window size has changed. - /// - /// - /// - /// - /// - /// - bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int buffWidth) - { - if (!_consoleDriver.EnableConsoleScrolling) { - if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) return false; - var w = Math.Max (winWidth, 0); - var h = Math.Max (winHeight, 0); - _inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.WindowSize, - WindowSizeEvent = new WindowSizeEvent () { - Size = new Size (w, h) - } - }); - return true; - } else { - if (winWidth == _consoleDriver.Cols && - winHeight == _lastWindowHeight && - buffWidth == _consoleDriver.Cols && buffHeight == _consoleDriver.Rows) return false; - _lastWindowHeight = Math.Max (winHeight, 0); - _inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.WindowSize, - WindowSizeEvent = new WindowSizeEvent () { - Size = new Size (winWidth, _lastWindowHeight) - } - }); - return true; - } - } - - // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event) - void ProcessRequestResponse (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) - { - // isMouse is true if it's CSI<, false otherwise - EscSeqUtils.DecodeEscSeq (EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod, - out var c1Control, out var code, out var values, out var terminating, - out var isMouse, out var mouseFlags, - out var pos, out var isReq, - (f, p) => HandleMouseEvent (MapMouseFlags (f), p)); - - if (isMouse) { - foreach (var mf in mouseFlags) { - HandleMouseEvent (MapMouseFlags (mf), pos); - } - return; - } else if (isReq) { - HandleRequestResponseEvent (c1Control, code, values, terminating); - return; - } - HandleKeyboardEvent (newConsoleKeyInfo); - } - - MouseButtonState MapMouseFlags (MouseFlags mouseFlags) - { - MouseButtonState mbs = default; - foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) { - if (mouseFlags.HasFlag ((MouseFlags)flag)) { - switch (flag) { - case MouseFlags.Button1Pressed: - mbs |= MouseButtonState.Button1Pressed; - break; - case MouseFlags.Button1Released: - mbs |= MouseButtonState.Button1Released; - break; - case MouseFlags.Button1Clicked: - mbs |= MouseButtonState.Button1Clicked; - break; - case MouseFlags.Button1DoubleClicked: - mbs |= MouseButtonState.Button1DoubleClicked; - break; - case MouseFlags.Button1TripleClicked: - mbs |= MouseButtonState.Button1TripleClicked; - break; - case MouseFlags.Button2Pressed: - mbs |= MouseButtonState.Button2Pressed; - break; - case MouseFlags.Button2Released: - mbs |= MouseButtonState.Button2Released; - break; - case MouseFlags.Button2Clicked: - mbs |= MouseButtonState.Button2Clicked; - break; - case MouseFlags.Button2DoubleClicked: - mbs |= MouseButtonState.Button2DoubleClicked; - break; - case MouseFlags.Button2TripleClicked: - mbs |= MouseButtonState.Button2TripleClicked; - break; - case MouseFlags.Button3Pressed: - mbs |= MouseButtonState.Button3Pressed; - break; - case MouseFlags.Button3Released: - mbs |= MouseButtonState.Button3Released; - break; - case MouseFlags.Button3Clicked: - mbs |= MouseButtonState.Button3Clicked; - break; - case MouseFlags.Button3DoubleClicked: - mbs |= MouseButtonState.Button3DoubleClicked; - break; - case MouseFlags.Button3TripleClicked: - mbs |= MouseButtonState.Button3TripleClicked; - break; - case MouseFlags.WheeledUp: - mbs |= MouseButtonState.ButtonWheeledUp; - break; - case MouseFlags.WheeledDown: - mbs |= MouseButtonState.ButtonWheeledDown; - break; - case MouseFlags.WheeledLeft: - mbs |= MouseButtonState.ButtonWheeledLeft; - break; - case MouseFlags.WheeledRight: - mbs |= MouseButtonState.ButtonWheeledRight; - break; - case MouseFlags.Button4Pressed: - mbs |= MouseButtonState.Button4Pressed; - break; - case MouseFlags.Button4Released: - mbs |= MouseButtonState.Button4Released; - break; - case MouseFlags.Button4Clicked: - mbs |= MouseButtonState.Button4Clicked; - break; - case MouseFlags.Button4DoubleClicked: - mbs |= MouseButtonState.Button4DoubleClicked; - break; - case MouseFlags.Button4TripleClicked: - mbs |= MouseButtonState.Button4TripleClicked; - break; - case MouseFlags.ButtonShift: - mbs |= MouseButtonState.ButtonShift; - break; - case MouseFlags.ButtonCtrl: - mbs |= MouseButtonState.ButtonCtrl; - break; - case MouseFlags.ButtonAlt: - mbs |= MouseButtonState.ButtonAlt; - break; - case MouseFlags.ReportMousePosition: - mbs |= MouseButtonState.ReportMousePosition; - break; - case MouseFlags.AllEvents: - mbs |= MouseButtonState.AllEvents; - break; - } - } - } - return mbs; - } - - Point _lastCursorPosition; - - void HandleRequestResponseEvent (string c1Control, string code, string [] values, string terminating) - { - switch (terminating) { - // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed. - case EscSeqUtils.CSI_RequestCursorPositionReport_Terminator: - Point point = new Point { - X = int.Parse (values [1]) - 1, - Y = int.Parse (values [0]) - 1 - }; - if (_lastCursorPosition.Y != point.Y) { - _lastCursorPosition = point; - var eventType = EventType.WindowPosition; - var winPositionEv = new WindowPositionEvent () { - CursorPosition = point - }; - _inputResultQueue.Enqueue (new InputResult () { - EventType = eventType, - WindowPositionEvent = winPositionEv - }); - } else { - return; - } - break; - - case EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator: - switch (values [0]) { - case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue: - EnqueueWindowSizeEvent ( - Math.Max (int.Parse (values [1]), 0), - Math.Max (int.Parse (values [2]), 0), - Math.Max (int.Parse (values [1]), 0), - Math.Max (int.Parse (values [2]), 0)); - break; - default: - EnqueueRequestResponseEvent (c1Control, code, values, terminating); - break; - } - break; - default: - EnqueueRequestResponseEvent (c1Control, code, values, terminating); - break; - } - - _inputReady.Set (); - } - - void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating) - { - EventType eventType = EventType.RequestResponse; - var requestRespEv = new RequestResponseEvent () { - ResultTuple = (c1Control, code, values, terminating) - }; - _inputResultQueue.Enqueue (new InputResult () { - EventType = eventType, - RequestResponseEvent = requestRespEv - }); - } - - void HandleMouseEvent (MouseButtonState buttonState, Point pos) - { - MouseEvent mouseEvent = new MouseEvent () { - Position = pos, - ButtonState = buttonState, - }; - - _inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = mouseEvent - }); - - _inputReady.Set (); - } - - public enum EventType { - Key = 1, - Mouse = 2, - WindowSize = 3, - WindowPosition = 4, - RequestResponse = 5 - } - - [Flags] - public enum MouseButtonState { - Button1Pressed = 0x1, - Button1Released = 0x2, - Button1Clicked = 0x4, - Button1DoubleClicked = 0x8, - Button1TripleClicked = 0x10, - Button2Pressed = 0x20, - Button2Released = 0x40, - Button2Clicked = 0x80, - Button2DoubleClicked = 0x100, - Button2TripleClicked = 0x200, - Button3Pressed = 0x400, - Button3Released = 0x800, - Button3Clicked = 0x1000, - Button3DoubleClicked = 0x2000, - Button3TripleClicked = 0x4000, - ButtonWheeledUp = 0x8000, - ButtonWheeledDown = 0x10000, - ButtonWheeledLeft = 0x20000, - ButtonWheeledRight = 0x40000, - Button4Pressed = 0x80000, - Button4Released = 0x100000, - Button4Clicked = 0x200000, - Button4DoubleClicked = 0x400000, - Button4TripleClicked = 0x800000, - ButtonShift = 0x1000000, - ButtonCtrl = 0x2000000, - ButtonAlt = 0x4000000, - ReportMousePosition = 0x8000000, - AllEvents = -1 - } - - public struct MouseEvent { - public Point Position; - public MouseButtonState ButtonState; - } - - public struct WindowSizeEvent { - public Size Size; - } - - public struct WindowPositionEvent { - public int Top; - public int Left; - public Point CursorPosition; - } - - public struct RequestResponseEvent { - public (string c1Control, string code, string [] values, string terminating) ResultTuple; - } - - public struct InputResult { - public EventType EventType; - public ConsoleKeyInfo ConsoleKeyInfo; - public MouseEvent MouseEvent; - public WindowSizeEvent WindowSizeEvent; - public WindowPositionEvent WindowPositionEvent; - public RequestResponseEvent RequestResponseEvent; - } - - void HandleKeyboardEvent (ConsoleKeyInfo cki) - { - InputResult inputResult = new InputResult { - EventType = EventType.Key, - ConsoleKeyInfo = cki - }; - - _inputResultQueue.Enqueue (inputResult); - } + if (_inputResultQueue.Count > 0) + { + return _inputResultQueue.Dequeue(); + } + } + } + + void ProcessInputResultQueue() + { + while (true) + { + _waitForStart.Wait(); + _waitForStart.Reset(); + + if (_inputResultQueue.Count == 0) + { + ConsoleKey key = 0; + ConsoleModifiers mod = 0; + ConsoleKeyInfo newConsoleKeyInfo = default; + + while (true) + { + ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(true); + if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq) + || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) + { + if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) + { + _cki = EscSeqUtils.ResizeArray(new ConsoleKeyInfo((char)Key.Esc, 0, + false, false, false), _cki); + } + _isEscSeq = true; + newConsoleKeyInfo = consoleKeyInfo; + _cki = EscSeqUtils.ResizeArray(consoleKeyInfo, _cki); + if (Console.KeyAvailable) continue; + ProcessRequestResponse(ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; + _isEscSeq = false; + break; + } + else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) + { + if (_cki != null) + { + ProcessRequestResponse(ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; + } + break; + } + else + { + _inputResultQueue.Enqueue(new InputResult + { + EventType = EventType.Key, + ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo(consoleKeyInfo) + }); + _isEscSeq = false; + break; + } + } + } + + _inputReady.Set(); + } + } + + void CheckWindowSizeChange() + { + void RequestWindowSize() + { + while (true) + { + // Wait for a while then check if screen has changed sizes + Task.Delay(500).Wait(); + + if (_stopTasks) + { + return; + } + int buffHeight, buffWidth; + if (((NetDriver)_consoleDriver).IsWinPlatform) + { + buffHeight = Math.Max(Console.BufferHeight, 0); + buffWidth = Math.Max(Console.BufferWidth, 0); + } + else + { + buffHeight = _consoleDriver.Rows; + buffWidth = _consoleDriver.Cols; + } + if (EnqueueWindowSizeEvent( + Math.Max(Console.WindowHeight, 0), + Math.Max(Console.WindowWidth, 0), + buffHeight, + buffWidth)) + { + + return; + } + } + } + + while (true) + { + if (_stopTasks) + { + return; + } + _winChange.Wait(); + _winChange.Reset(); + RequestWindowSize(); + _inputReady.Set(); + } + } + + /// + /// Enqueue a window size event if the window size has changed. + /// + /// + /// + /// + /// + /// + bool EnqueueWindowSizeEvent(int winHeight, int winWidth, int buffHeight, int buffWidth) + { + if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) return false; + var w = Math.Max(winWidth, 0); + var h = Math.Max(winHeight, 0); + _inputResultQueue.Enqueue(new InputResult() + { + EventType = EventType.WindowSize, + WindowSizeEvent = new WindowSizeEvent() + { + Size = new Size(w, h) + } + }); + return true; + } + + // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event) + void ProcessRequestResponse(ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo[] cki, ref ConsoleModifiers mod) + { + // isMouse is true if it's CSI<, false otherwise + EscSeqUtils.DecodeEscSeq(EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod, + out var c1Control, out var code, out var values, out var terminating, + out var isMouse, out var mouseFlags, + out var pos, out var isReq, + (f, p) => HandleMouseEvent(MapMouseFlags(f), p)); + + if (isMouse) + { + foreach (var mf in mouseFlags) + { + HandleMouseEvent(MapMouseFlags(mf), pos); + } + return; + } + else if (isReq) + { + HandleRequestResponseEvent(c1Control, code, values, terminating); + return; + } + HandleKeyboardEvent(newConsoleKeyInfo); + } + + MouseButtonState MapMouseFlags(MouseFlags mouseFlags) + { + MouseButtonState mbs = default; + foreach (var flag in Enum.GetValues(mouseFlags.GetType())) + { + if (mouseFlags.HasFlag((MouseFlags)flag)) + { + switch (flag) + { + case MouseFlags.Button1Pressed: + mbs |= MouseButtonState.Button1Pressed; + break; + case MouseFlags.Button1Released: + mbs |= MouseButtonState.Button1Released; + break; + case MouseFlags.Button1Clicked: + mbs |= MouseButtonState.Button1Clicked; + break; + case MouseFlags.Button1DoubleClicked: + mbs |= MouseButtonState.Button1DoubleClicked; + break; + case MouseFlags.Button1TripleClicked: + mbs |= MouseButtonState.Button1TripleClicked; + break; + case MouseFlags.Button2Pressed: + mbs |= MouseButtonState.Button2Pressed; + break; + case MouseFlags.Button2Released: + mbs |= MouseButtonState.Button2Released; + break; + case MouseFlags.Button2Clicked: + mbs |= MouseButtonState.Button2Clicked; + break; + case MouseFlags.Button2DoubleClicked: + mbs |= MouseButtonState.Button2DoubleClicked; + break; + case MouseFlags.Button2TripleClicked: + mbs |= MouseButtonState.Button2TripleClicked; + break; + case MouseFlags.Button3Pressed: + mbs |= MouseButtonState.Button3Pressed; + break; + case MouseFlags.Button3Released: + mbs |= MouseButtonState.Button3Released; + break; + case MouseFlags.Button3Clicked: + mbs |= MouseButtonState.Button3Clicked; + break; + case MouseFlags.Button3DoubleClicked: + mbs |= MouseButtonState.Button3DoubleClicked; + break; + case MouseFlags.Button3TripleClicked: + mbs |= MouseButtonState.Button3TripleClicked; + break; + case MouseFlags.WheeledUp: + mbs |= MouseButtonState.ButtonWheeledUp; + break; + case MouseFlags.WheeledDown: + mbs |= MouseButtonState.ButtonWheeledDown; + break; + case MouseFlags.WheeledLeft: + mbs |= MouseButtonState.ButtonWheeledLeft; + break; + case MouseFlags.WheeledRight: + mbs |= MouseButtonState.ButtonWheeledRight; + break; + case MouseFlags.Button4Pressed: + mbs |= MouseButtonState.Button4Pressed; + break; + case MouseFlags.Button4Released: + mbs |= MouseButtonState.Button4Released; + break; + case MouseFlags.Button4Clicked: + mbs |= MouseButtonState.Button4Clicked; + break; + case MouseFlags.Button4DoubleClicked: + mbs |= MouseButtonState.Button4DoubleClicked; + break; + case MouseFlags.Button4TripleClicked: + mbs |= MouseButtonState.Button4TripleClicked; + break; + case MouseFlags.ButtonShift: + mbs |= MouseButtonState.ButtonShift; + break; + case MouseFlags.ButtonCtrl: + mbs |= MouseButtonState.ButtonCtrl; + break; + case MouseFlags.ButtonAlt: + mbs |= MouseButtonState.ButtonAlt; + break; + case MouseFlags.ReportMousePosition: + mbs |= MouseButtonState.ReportMousePosition; + break; + case MouseFlags.AllEvents: + mbs |= MouseButtonState.AllEvents; + break; + } + } + } + return mbs; + } + + Point _lastCursorPosition; + + void HandleRequestResponseEvent(string c1Control, string code, string[] values, string terminating) + { + switch (terminating) + { + // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed. + case EscSeqUtils.CSI_RequestCursorPositionReport_Terminator: + Point point = new Point + { + X = int.Parse(values[1]) - 1, + Y = int.Parse(values[0]) - 1 + }; + if (_lastCursorPosition.Y != point.Y) + { + _lastCursorPosition = point; + var eventType = EventType.WindowPosition; + var winPositionEv = new WindowPositionEvent() + { + CursorPosition = point + }; + _inputResultQueue.Enqueue(new InputResult() + { + EventType = eventType, + WindowPositionEvent = winPositionEv + }); + } + else + { + return; + } + break; + + case EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator: + switch (values[0]) + { + case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue: + EnqueueWindowSizeEvent( + Math.Max(int.Parse(values[1]), 0), + Math.Max(int.Parse(values[2]), 0), + Math.Max(int.Parse(values[1]), 0), + Math.Max(int.Parse(values[2]), 0)); + break; + default: + EnqueueRequestResponseEvent(c1Control, code, values, terminating); + break; + } + break; + default: + EnqueueRequestResponseEvent(c1Control, code, values, terminating); + break; + } + + _inputReady.Set(); + } + + void EnqueueRequestResponseEvent(string c1Control, string code, string[] values, string terminating) + { + EventType eventType = EventType.RequestResponse; + var requestRespEv = new RequestResponseEvent() + { + ResultTuple = (c1Control, code, values, terminating) + }; + _inputResultQueue.Enqueue(new InputResult() + { + EventType = eventType, + RequestResponseEvent = requestRespEv + }); + } + + void HandleMouseEvent(MouseButtonState buttonState, Point pos) + { + MouseEvent mouseEvent = new MouseEvent() + { + Position = pos, + ButtonState = buttonState, + }; + + _inputResultQueue.Enqueue(new InputResult() + { + EventType = EventType.Mouse, + MouseEvent = mouseEvent + }); + + _inputReady.Set(); + } + + public enum EventType + { + Key = 1, + Mouse = 2, + WindowSize = 3, + WindowPosition = 4, + RequestResponse = 5 + } + + [Flags] + public enum MouseButtonState + { + Button1Pressed = 0x1, + Button1Released = 0x2, + Button1Clicked = 0x4, + Button1DoubleClicked = 0x8, + Button1TripleClicked = 0x10, + Button2Pressed = 0x20, + Button2Released = 0x40, + Button2Clicked = 0x80, + Button2DoubleClicked = 0x100, + Button2TripleClicked = 0x200, + Button3Pressed = 0x400, + Button3Released = 0x800, + Button3Clicked = 0x1000, + Button3DoubleClicked = 0x2000, + Button3TripleClicked = 0x4000, + ButtonWheeledUp = 0x8000, + ButtonWheeledDown = 0x10000, + ButtonWheeledLeft = 0x20000, + ButtonWheeledRight = 0x40000, + Button4Pressed = 0x80000, + Button4Released = 0x100000, + Button4Clicked = 0x200000, + Button4DoubleClicked = 0x400000, + Button4TripleClicked = 0x800000, + ButtonShift = 0x1000000, + ButtonCtrl = 0x2000000, + ButtonAlt = 0x4000000, + ReportMousePosition = 0x8000000, + AllEvents = -1 + } + + public struct MouseEvent + { + public Point Position; + public MouseButtonState ButtonState; + } + + public struct WindowSizeEvent + { + public Size Size; + } + + public struct WindowPositionEvent + { + public int Top; + public int Left; + public Point CursorPosition; + } + + public struct RequestResponseEvent + { + public (string c1Control, string code, string[] values, string terminating) ResultTuple; + } + + public struct InputResult + { + public EventType EventType; + public ConsoleKeyInfo ConsoleKeyInfo; + public MouseEvent MouseEvent; + public WindowSizeEvent WindowSizeEvent; + public WindowPositionEvent WindowPositionEvent; + public RequestResponseEvent RequestResponseEvent; + } + + void HandleKeyboardEvent(ConsoleKeyInfo cki) + { + InputResult inputResult = new InputResult + { + EventType = EventType.Key, + ConsoleKeyInfo = cki + }; + + _inputResultQueue.Enqueue(inputResult); + } } -internal class NetDriver : ConsoleDriver { - const int COLOR_BLACK = 30; - const int COLOR_RED = 31; - const int COLOR_GREEN = 32; - const int COLOR_YELLOW = 33; - const int COLOR_BLUE = 34; - const int COLOR_MAGENTA = 35; - const int COLOR_CYAN = 36; - const int COLOR_WHITE = 37; - const int COLOR_BRIGHT_BLACK = 90; - const int COLOR_BRIGHT_RED = 91; - const int COLOR_BRIGHT_GREEN = 92; - const int COLOR_BRIGHT_YELLOW = 93; - const int COLOR_BRIGHT_BLUE = 94; - const int COLOR_BRIGHT_MAGENTA = 95; - const int COLOR_BRIGHT_CYAN = 96; - const int COLOR_BRIGHT_WHITE = 97; - - public NetWinVTConsole NetWinConsole { get; private set; } - public bool IsWinPlatform { get; private set; } - - int _largestBufferHeight; - - public NetDriver () - { - } - - public override void End () - { - _mainLoop._netEvents.StopTasks (); - - if (IsWinPlatform) { - NetWinConsole.Cleanup (); - } - - StopReportingMouseMoves (); - Console.ResetColor (); - - //Disable alternative screen buffer. - Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndActivateAltBufferWithBackscroll); - - //Set cursor key to cursor. - Console.Out.Write (EscSeqUtils.CSI_ShowCursor); - - Console.Out.Close (); - } - - public override void Init (Action terminalResized) - { - var p = Environment.OSVersion.Platform; - if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) { - IsWinPlatform = true; - try { - NetWinConsole = new NetWinVTConsole (); - } catch (ApplicationException) { - // Likely running as a unit test, or in a non-interactive session. - } - } - if (IsWinPlatform) { - Clipboard = new WindowsClipboard (); - } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - Clipboard = new MacOSXClipboard (); - } else { - if (CursesDriver.Is_WSL_Platform ()) { - Clipboard = new WSLClipboard (); - } else { - Clipboard = new CursesClipboard (); - } - } - - TerminalResized = terminalResized; - - if (NetWinConsole != null) { - //Enable alternative screen buffer. - Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll); - - //Set cursor key to application. - Console.Out.Write (EscSeqUtils.CSI_HideCursor); - - Console.TreatControlCAsInput = true; - - if (EnableConsoleScrolling) { - _largestBufferHeight = Console.BufferHeight; - } else { - _largestBufferHeight = Console.WindowHeight; - } - - Cols = Console.WindowWidth; - Rows = _largestBufferHeight; - } else { - // Simluate - Cols = 80; - Rows = 25; - _largestBufferHeight = Rows; - } - - ResizeScreen (); - ClearContents(); - CurrentAttribute = MakeColor (Color.White, Color.Black); - InitializeColorSchemes (); - - StartReportingMouseMoves (); - } - - public virtual void ResizeScreen () - { - if (NetWinConsole == null) { - return; - } - - if (!EnableConsoleScrolling && Console.WindowHeight > 0) { - // Not supported on Unix. - if (IsWinPlatform) { - // Can raise an exception while is still resizing. - try { +internal class NetDriver : ConsoleDriver +{ + const int COLOR_BLACK = 30; + const int COLOR_RED = 31; + const int COLOR_GREEN = 32; + const int COLOR_YELLOW = 33; + const int COLOR_BLUE = 34; + const int COLOR_MAGENTA = 35; + const int COLOR_CYAN = 36; + const int COLOR_WHITE = 37; + const int COLOR_BRIGHT_BLACK = 90; + const int COLOR_BRIGHT_RED = 91; + const int COLOR_BRIGHT_GREEN = 92; + const int COLOR_BRIGHT_YELLOW = 93; + const int COLOR_BRIGHT_BLUE = 94; + const int COLOR_BRIGHT_MAGENTA = 95; + const int COLOR_BRIGHT_CYAN = 96; + const int COLOR_BRIGHT_WHITE = 97; + + public NetWinVTConsole NetWinConsole { get; private set; } + public bool IsWinPlatform { get; private set; } + + int _largestBufferHeight; + + public NetDriver() + { + } + + public override void End() + { + _mainLoop._netEvents.StopTasks(); + + if (IsWinPlatform) + { + NetWinConsole.Cleanup(); + } + + StopReportingMouseMoves(); + Console.ResetColor(); + + //Disable alternative screen buffer. + Console.Out.Write(EscSeqUtils.CSI_RestoreCursorAndActivateAltBufferWithBackscroll); + + //Set cursor key to cursor. + Console.Out.Write(EscSeqUtils.CSI_ShowCursor); + + Console.Out.Close(); + } + + public override void Init(Action terminalResized) + { + var p = Environment.OSVersion.Platform; + if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) + { + IsWinPlatform = true; + try + { + NetWinConsole = new NetWinVTConsole(); + } + catch (ApplicationException) + { + // Likely running as a unit test, or in a non-interactive session. + } + } + if (IsWinPlatform) + { + Clipboard = new WindowsClipboard(); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + Clipboard = new MacOSXClipboard(); + } + else + { + if (CursesDriver.Is_WSL_Platform()) + { + Clipboard = new WSLClipboard(); + } + else + { + Clipboard = new CursesClipboard(); + } + } + + TerminalResized = terminalResized; + + if (NetWinConsole != null) + { + //Enable alternative screen buffer. + Console.Out.Write(EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll); + + //Set cursor key to application. + Console.Out.Write(EscSeqUtils.CSI_HideCursor); + + Console.TreatControlCAsInput = true; + + Cols = Console.WindowWidth; + Rows = Console.WindowHeight; + } + else + { + // Simluate + Cols = 80; + Rows = 25; + _largestBufferHeight = Rows; + } + + ResizeScreen(); + ClearContents(); + CurrentAttribute = MakeColor(Color.White, Color.Black); + InitializeColorSchemes(); + + StartReportingMouseMoves(); + } + + public virtual void ResizeScreen() + { + if (NetWinConsole == null) + { + return; + } + + if (Console.WindowHeight > 0) + { + // Not supported on Unix. + if (IsWinPlatform) + { + // Can raise an exception while is still resizing. + try + { #pragma warning disable CA1416 - Console.CursorTop = 0; - Console.CursorLeft = 0; - Console.WindowTop = 0; - Console.WindowLeft = 0; - if (Console.WindowHeight > Rows) { - Console.SetWindowSize (Cols, Rows); - } - Console.SetBufferSize (Cols, Rows); + Console.CursorTop = 0; + Console.CursorLeft = 0; + Console.WindowTop = 0; + Console.WindowLeft = 0; + if (Console.WindowHeight > Rows) + { + Console.SetWindowSize(Cols, Rows); + } + Console.SetBufferSize(Cols, Rows); #pragma warning restore CA1416 - } catch (System.IO.IOException) { - Clip = new Rect (0, 0, Cols, Rows); - } catch (ArgumentOutOfRangeException) { - Clip = new Rect (0, 0, Cols, Rows); - } - } else { - Console.Out.Write (EscSeqUtils.CSI_SetTerminalWindowSize (Rows, Cols)); - } - } else { - if (IsWinPlatform) { - if (Console.WindowHeight > 0) { - // Can raise an exception while is still resizing. - try { + } + catch (System.IO.IOException) + { + Clip = new Rect(0, 0, Cols, Rows); + } + catch (ArgumentOutOfRangeException) + { + Clip = new Rect(0, 0, Cols, Rows); + } + } + else + { + Console.Out.Write(EscSeqUtils.CSI_SetTerminalWindowSize(Rows, Cols)); + } + } + + Clip = new Rect(0, 0, Cols, Rows); + } + + public override void Refresh() + { + UpdateScreen(); + UpdateCursor(); + } + + public override void UpdateScreen() + { + if (_winSizeChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols || Rows != Console.WindowHeight) + { + return; + } + + var top = 0; + var left = 0; + var rows = Rows; + var cols = Cols; + System.Text.StringBuilder output = new System.Text.StringBuilder(); + Attribute redrawAttr = new Attribute(); + var lastCol = -1; + + //GetCursorVisibility (out CursorVisibility savedVisibitity); + //SetCursorVisibility (CursorVisibility.Invisible); + + for (var row = top; row < rows; row++) + { + if (Console.WindowHeight < 1) + { + return; + } + if (!_dirtyLines[row]) + { + continue; + } + if (!SetCursorPosition(0, row)) + { + return; + } + _dirtyLines[row] = false; + output.Clear(); + for (var col = left; col < cols; col++) + { + lastCol = -1; + var outputWidth = 0; + for (; col < cols; col++) + { + if (!Contents[row, col].IsDirty) + { + if (output.Length > 0) + { + WriteToConsole(output, ref lastCol, row, ref outputWidth); + } + else if (lastCol == -1) + { + lastCol = col; + } + if (lastCol + 1 < cols) + lastCol++; + continue; + } + + if (lastCol == -1) + { + lastCol = col; + } + + Attribute attr = Contents[row, col].Attribute.Value; + // Performance: Only send the escape sequence if the attribute has changed. + if (attr != redrawAttr) + { + redrawAttr = attr; + output.Append(EscSeqUtils.CSI_SetGraphicsRendition( + MapColors((ConsoleColor)attr.Background, false), MapColors((ConsoleColor)attr.Foreground, true))); + } + outputWidth++; + var rune = (Rune)Contents[row, col].Runes[0]; + output.Append(rune.ToString()); + if (rune.IsSurrogatePair() && rune.GetColumns() < 2) + { + WriteToConsole(output, ref lastCol, row, ref outputWidth); + Console.CursorLeft--; + } + Contents[row, col].IsDirty = false; + } + } + if (output.Length > 0) + { + SetCursorPosition(lastCol, row); + Console.Write(output); + } + } + SetCursorPosition(0, 0); + + //SetCursorVisibility (savedVisibitity); + + void WriteToConsole(StringBuilder output, ref int lastCol, int row, ref int outputWidth) + { + SetCursorPosition(lastCol, row); + Console.Write(output); + output.Clear(); + lastCol += outputWidth; + outputWidth = 0; + } + } + + #region Color Handling + + // Cache the list of ConsoleColor values. + private static readonly HashSet ConsoleColorValues = new HashSet( + Enum.GetValues(typeof(ConsoleColor)).OfType().Select(c => (int)c) + ); + + // Dictionary for mapping ConsoleColor values to the values used by System.Net.Console. + private static Dictionary colorMap = new Dictionary { + { ConsoleColor.Black, COLOR_BLACK }, + { ConsoleColor.DarkBlue, COLOR_BLUE }, + { ConsoleColor.DarkGreen, COLOR_GREEN }, + { ConsoleColor.DarkCyan, COLOR_CYAN }, + { ConsoleColor.DarkRed, COLOR_RED }, + { ConsoleColor.DarkMagenta, COLOR_MAGENTA }, + { ConsoleColor.DarkYellow, COLOR_YELLOW }, + { ConsoleColor.Gray, COLOR_WHITE }, + { ConsoleColor.DarkGray, COLOR_BRIGHT_BLACK }, + { ConsoleColor.Blue, COLOR_BRIGHT_BLUE }, + { ConsoleColor.Green, COLOR_BRIGHT_GREEN }, + { ConsoleColor.Cyan, COLOR_BRIGHT_CYAN }, + { ConsoleColor.Red, COLOR_BRIGHT_RED }, + { ConsoleColor.Magenta, COLOR_BRIGHT_MAGENTA }, + { ConsoleColor.Yellow, COLOR_BRIGHT_YELLOW }, + { ConsoleColor.White, COLOR_BRIGHT_WHITE } + }; + + // Map a ConsoleColor to a platform dependent value. + int MapColors(ConsoleColor color, bool isForeground = true) + { + return colorMap.TryGetValue(color, out var colorValue) ? colorValue + (isForeground ? 0 : 10) : 0; + } + + /// + /// In the NetDriver, colors are encoded as an int. + /// Extracts the foreground and background colors from the encoded value. + /// Assumes a 4-bit encoded value for both foreground and background colors. + /// + internal override void GetColors(int value, out Color foreground, out Color background) + { + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = (Color)((value >> 16) & 0xF); + background = (Color)(value & 0xF); + } + + /// + /// In the NetDriver, colors are encoded as an int. + /// However, the foreground color is stored in the most significant 16 bits, + /// and the background color is stored in the least significant 16 bits. + /// + public override Attribute MakeColor(Color foreground, Color background) + { + // Encode the colors into the int value. + return new Attribute( + value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), + foreground: foreground, + background: background + ); + } + + #endregion + + #region Cursor Handling + bool SetCursorPosition(int col, int row) + { + //if (IsWinPlatform) { + // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. + try + { + Console.SetCursorPosition(col, row); + return true; + } + catch (Exception) + { + return false; + } + // BUGBUG: This breaks -usc on WSL; not sure why. But commenting out fixes. + //} else { + // // TODO: Explain why + 1 is needed (and why we do this for non-Windows). + // Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1)); + // return true; + //} + } + + CursorVisibility? _cachedCursorVisibility; + + public override void UpdateCursor() + { + EnsureCursorVisibility(); + + if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) + { + SetCursorPosition(Col, Row); + SetWindowPosition(0, Row); + } + } + + public override bool GetCursorVisibility(out CursorVisibility visibility) + { + visibility = _cachedCursorVisibility ?? CursorVisibility.Default; + return visibility == CursorVisibility.Default; + } + + public override bool SetCursorVisibility(CursorVisibility visibility) + { + _cachedCursorVisibility = visibility; + var isVisible = Console.CursorVisible = visibility == CursorVisibility.Default; + //Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor); + return isVisible; + } + + public override bool EnsureCursorVisibility() + { + if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) + { + GetCursorVisibility(out CursorVisibility cursorVisibility); + _cachedCursorVisibility = cursorVisibility; + SetCursorVisibility(CursorVisibility.Invisible); + return false; + } + + SetCursorVisibility(_cachedCursorVisibility ?? CursorVisibility.Default); + return _cachedCursorVisibility == CursorVisibility.Default; + } + #endregion + + #region Size and Position Handling + + void SetWindowPosition(int col, int row) + { + Top = Console.WindowTop; + Left = Console.WindowLeft; + } + + private bool EnsureBufferSize() + { #pragma warning disable CA1416 - Console.CursorTop = 0; - Console.CursorLeft = 0; - if (Console.WindowHeight > Rows) { - Console.SetWindowSize (Cols, Rows); - } - Console.SetBufferSize (Cols, Rows); + if (IsWinPlatform && Console.BufferHeight < Rows) + { + try + { + Console.SetBufferSize(Console.WindowWidth, Rows); + } + catch (Exception) + { + return false; + } + } #pragma warning restore CA1416 - } catch (System.IO.IOException) { - Clip = new Rect (0, 0, Cols, Rows); - } catch (ArgumentOutOfRangeException) { - Clip = new Rect (0, 0, Cols, Rows); - } - } - } else { - Console.Out.Write (EscSeqUtils.CSI_SetTerminalWindowSize (Rows, Cols)); - } - } - Clip = new Rect (0, 0, Cols, Rows); - } - - public override void Refresh () - { - UpdateScreen (); - UpdateCursor (); - } - - public override void UpdateScreen () - { - if (_winSizeChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols - || (!EnableConsoleScrolling && Rows != Console.WindowHeight) - || (EnableConsoleScrolling && Rows != _largestBufferHeight)) { - return; - } - - var top = 0; - var left = 0; - var rows = Rows; - var cols = Cols; - System.Text.StringBuilder output = new System.Text.StringBuilder (); - Attribute redrawAttr = new Attribute (); - var lastCol = -1; - - //GetCursorVisibility (out CursorVisibility savedVisibitity); - //SetCursorVisibility (CursorVisibility.Invisible); - - for (var row = top; row < rows; row++) { - if (Console.WindowHeight < 1) { - return; - } - if (!_dirtyLines [row]) { - continue; - } - if (!SetCursorPosition (0, row)) { - return; - } - _dirtyLines [row] = false; - output.Clear (); - for (var col = left; col < cols; col++) { - lastCol = -1; - var outputWidth = 0; - for (; col < cols; col++) { - if (!Contents [row, col].IsDirty) { - if (output.Length > 0) { - WriteToConsole (output, ref lastCol, row, ref outputWidth); - } else if (lastCol == -1) { - lastCol = col; - } - if (lastCol + 1 < cols) - lastCol++; - continue; - } - - if (lastCol == -1) { - lastCol = col; - } - - Attribute attr = Contents [row, col].Attribute.Value; - // Performance: Only send the escape sequence if the attribute has changed. - if (attr != redrawAttr) { - redrawAttr = attr; - output.Append (EscSeqUtils.CSI_SetGraphicsRendition ( - MapColors ((ConsoleColor)attr.Background, false), MapColors ((ConsoleColor)attr.Foreground, true))); - } - outputWidth++; - var rune = (Rune)Contents [row, col].Runes [0]; - output.Append (rune.ToString ()); - if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { - WriteToConsole (output, ref lastCol, row, ref outputWidth); - Console.CursorLeft--; - } - Contents [row, col].IsDirty = false; - } - } - if (output.Length > 0) { - SetCursorPosition (lastCol, row); - Console.Write (output); - } - } - SetCursorPosition (0, 0); - - //SetCursorVisibility (savedVisibitity); - - void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth) - { - SetCursorPosition (lastCol, row); - Console.Write (output); - output.Clear (); - lastCol += outputWidth; - outputWidth = 0; - } - } - - #region Color Handling - - // Cache the list of ConsoleColor values. - private static readonly HashSet ConsoleColorValues = new HashSet ( - Enum.GetValues (typeof (ConsoleColor)).OfType ().Select (c => (int)c) - ); - - // Dictionary for mapping ConsoleColor values to the values used by System.Net.Console. - private static Dictionary colorMap = new Dictionary { - { ConsoleColor.Black, COLOR_BLACK }, - { ConsoleColor.DarkBlue, COLOR_BLUE }, - { ConsoleColor.DarkGreen, COLOR_GREEN }, - { ConsoleColor.DarkCyan, COLOR_CYAN }, - { ConsoleColor.DarkRed, COLOR_RED }, - { ConsoleColor.DarkMagenta, COLOR_MAGENTA }, - { ConsoleColor.DarkYellow, COLOR_YELLOW }, - { ConsoleColor.Gray, COLOR_WHITE }, - { ConsoleColor.DarkGray, COLOR_BRIGHT_BLACK }, - { ConsoleColor.Blue, COLOR_BRIGHT_BLUE }, - { ConsoleColor.Green, COLOR_BRIGHT_GREEN }, - { ConsoleColor.Cyan, COLOR_BRIGHT_CYAN }, - { ConsoleColor.Red, COLOR_BRIGHT_RED }, - { ConsoleColor.Magenta, COLOR_BRIGHT_MAGENTA }, - { ConsoleColor.Yellow, COLOR_BRIGHT_YELLOW }, - { ConsoleColor.White, COLOR_BRIGHT_WHITE } - }; - - // Map a ConsoleColor to a platform dependent value. - int MapColors (ConsoleColor color, bool isForeground = true) - { - return colorMap.TryGetValue (color, out var colorValue) ? colorValue + (isForeground ? 0 : 10) : 0; - } - - /// - /// In the NetDriver, colors are encoded as an int. - /// Extracts the foreground and background colors from the encoded value. - /// Assumes a 4-bit encoded value for both foreground and background colors. - /// - internal override void GetColors (int value, out Color foreground, out Color background) - { - // Assume a 4-bit encoded value for both foreground and background colors. - foreground = (Color)((value >> 16) & 0xF); - background = (Color)(value & 0xF); - } - - /// - /// In the NetDriver, colors are encoded as an int. - /// However, the foreground color is stored in the most significant 16 bits, - /// and the background color is stored in the least significant 16 bits. - /// - public override Attribute MakeColor (Color foreground, Color background) - { - // Encode the colors into the int value. - return new Attribute ( - value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), - foreground: foreground, - background: background - ); - } - - #endregion - - #region Cursor Handling - bool SetCursorPosition (int col, int row) - { - //if (IsWinPlatform) { - // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. - try { - Console.SetCursorPosition (col, row); - return true; - } catch (Exception) { - return false; - } - // BUGBUG: This breaks -usc on WSL; not sure why. But commenting out fixes. - //} else { - // // TODO: Explain why + 1 is needed (and why we do this for non-Windows). - // Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1)); - // return true; - //} - } - - CursorVisibility? _cachedCursorVisibility; - - public override void UpdateCursor () - { - EnsureCursorVisibility (); - - if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) { - SetCursorPosition (Col, Row); - SetWindowPosition (0, Row); - } - } - - public override bool GetCursorVisibility (out CursorVisibility visibility) - { - visibility = _cachedCursorVisibility ?? CursorVisibility.Default; - return visibility == CursorVisibility.Default; - } - - public override bool SetCursorVisibility (CursorVisibility visibility) - { - _cachedCursorVisibility = visibility; - var isVisible = Console.CursorVisible = visibility == CursorVisibility.Default; - //Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor); - return isVisible; - } - - public override bool EnsureCursorVisibility () - { - if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { - GetCursorVisibility (out CursorVisibility cursorVisibility); - _cachedCursorVisibility = cursorVisibility; - SetCursorVisibility (CursorVisibility.Invisible); - return false; - } - - SetCursorVisibility (_cachedCursorVisibility ?? CursorVisibility.Default); - return _cachedCursorVisibility == CursorVisibility.Default; - } - #endregion - - #region Size and Position Handling - - void SetWindowPosition (int col, int row) - { - if (IsWinPlatform && EnableConsoleScrolling) { - var winTop = Math.Max (Rows - Console.WindowHeight - row, 0); - winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1); - winTop = Math.Max (winTop, 0); - if (winTop != Console.WindowTop) { - try { - if (!EnsureBufferSize ()) { - return; - } -#pragma warning disable CA1416 - Console.SetWindowPosition (col, winTop); -#pragma warning restore CA1416 - } catch (System.IO.IOException) { - - } catch (System.ArgumentOutOfRangeException) { } - } - } - Top = Console.WindowTop; - Left = Console.WindowLeft; - } - - private bool EnsureBufferSize () - { -#pragma warning disable CA1416 - if (IsWinPlatform && Console.BufferHeight < Rows) { - try { - Console.SetBufferSize (Console.WindowWidth, Rows); - } catch (Exception) { - return false; - } - } -#pragma warning restore CA1416 - return true; - } - #endregion - - - public void StartReportingMouseMoves () - { - Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents); - } - - public void StopReportingMouseMoves () - { - Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents); - } - - ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) - { - if (consoleKeyInfo.Key != ConsoleKey.Packet) { - return consoleKeyInfo; - } - - var mod = consoleKeyInfo.Modifiers; - var shift = (mod & ConsoleModifiers.Shift) != 0; - var alt = (mod & ConsoleModifiers.Alt) != 0; - var control = (mod & ConsoleModifiers.Control) != 0; - - var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _); - - return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control); - } - - Key MapKey (ConsoleKeyInfo keyInfo) - { - MapKeyModifiers (keyInfo, (Key)keyInfo.Key); - switch (keyInfo.Key) { - case ConsoleKey.Escape: - return MapKeyModifiers (keyInfo, Key.Esc); - case ConsoleKey.Tab: - return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; - case ConsoleKey.Home: - return MapKeyModifiers (keyInfo, Key.Home); - case ConsoleKey.End: - return MapKeyModifiers (keyInfo, Key.End); - case ConsoleKey.LeftArrow: - return MapKeyModifiers (keyInfo, Key.CursorLeft); - case ConsoleKey.RightArrow: - return MapKeyModifiers (keyInfo, Key.CursorRight); - case ConsoleKey.UpArrow: - return MapKeyModifiers (keyInfo, Key.CursorUp); - case ConsoleKey.DownArrow: - return MapKeyModifiers (keyInfo, Key.CursorDown); - case ConsoleKey.PageUp: - return MapKeyModifiers (keyInfo, Key.PageUp); - case ConsoleKey.PageDown: - return MapKeyModifiers (keyInfo, Key.PageDown); - case ConsoleKey.Enter: - return MapKeyModifiers (keyInfo, Key.Enter); - case ConsoleKey.Spacebar: - return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); - case ConsoleKey.Backspace: - return MapKeyModifiers (keyInfo, Key.Backspace); - case ConsoleKey.Delete: - return MapKeyModifiers (keyInfo, Key.DeleteChar); - case ConsoleKey.Insert: - return MapKeyModifiers (keyInfo, Key.InsertChar); - - case ConsoleKey.Oem1: - case ConsoleKey.Oem2: - case ConsoleKey.Oem3: - case ConsoleKey.Oem4: - case ConsoleKey.Oem5: - case ConsoleKey.Oem6: - case ConsoleKey.Oem7: - case ConsoleKey.Oem8: - case ConsoleKey.Oem102: - case ConsoleKey.OemPeriod: - case ConsoleKey.OemComma: - case ConsoleKey.OemPlus: - case ConsoleKey.OemMinus: - return (Key)((uint)keyInfo.KeyChar); - } - - var key = keyInfo.Key; - if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { - var delta = key - ConsoleKey.A; - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); - } - } - return (Key)((uint)keyInfo.KeyChar); - } - if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { - var delta = key - ConsoleKey.D0; - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); - } - } - return (Key)((uint)keyInfo.KeyChar); - } - if (key is >= ConsoleKey.F1 and <= ConsoleKey.F12) { - var delta = key - ConsoleKey.F1; - if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); - } - - return (Key)((uint)Key.F1 + delta); - } - if (keyInfo.KeyChar != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); - } - - return (Key)(0xffffffff); - } - - KeyModifiers _keyModifiers; - - Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) - { - _keyModifiers ??= new KeyModifiers (); - Key keyMod = new Key (); - if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { - keyMod = Key.ShiftMask; - _keyModifiers.Shift = true; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { - keyMod |= Key.CtrlMask; - _keyModifiers.Ctrl = true; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { - keyMod |= Key.AltMask; - _keyModifiers.Alt = true; - } - - return keyMod != Key.Null ? keyMod | key : key; - } - - Action _keyHandler; - Action _keyDownHandler; - Action _keyUpHandler; - Action _mouseHandler; - NetMainLoop _mainLoop; - - public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) - { - _keyHandler = keyHandler; - _keyDownHandler = keyDownHandler; - _keyUpHandler = keyUpHandler; - _mouseHandler = mouseHandler; - - var mLoop = _mainLoop = mainLoop.MainLoopDriver as NetMainLoop; - - // Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. - mLoop.ProcessInput = (e) => ProcessInput (e); - } - - volatile bool _winSizeChanging; - - void ProcessInput (NetEvents.InputResult inputEvent) - { - switch (inputEvent.EventType) { - case NetEvents.EventType.Key: - ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo; - if (consoleKeyInfo.Key == ConsoleKey.Packet) { - consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo); - } - _keyModifiers = new KeyModifiers (); - var map = MapKey (consoleKeyInfo); - if (map == (Key)0xffffffff) { - return; - } - if (map == Key.Null) { - _keyDownHandler (new KeyEvent (map, _keyModifiers)); - _keyUpHandler (new KeyEvent (map, _keyModifiers)); - } else { - _keyDownHandler (new KeyEvent (map, _keyModifiers)); - _keyHandler (new KeyEvent (map, _keyModifiers)); - _keyUpHandler (new KeyEvent (map, _keyModifiers)); - } - break; - case NetEvents.EventType.Mouse: - _mouseHandler (ToDriverMouse (inputEvent.MouseEvent)); - break; - case NetEvents.EventType.WindowSize: - _winSizeChanging = true; - if (!EnableConsoleScrolling) { - _largestBufferHeight = Math.Max (inputEvent.WindowSizeEvent.Size.Height, 0); - } else { - _largestBufferHeight = Math.Max (inputEvent.WindowSizeEvent.Size.Height, _largestBufferHeight); - } - Top = 0; - Left = 0; - Cols = inputEvent.WindowSizeEvent.Size.Width; - Rows = _largestBufferHeight; - ResizeScreen (); - ClearContents(); - _winSizeChanging = false; - TerminalResized?.Invoke (); - break; - case NetEvents.EventType.RequestResponse: - // BUGBUG: What is this for? It does not seem to be used anywhere. - // It is also not clear what it does. View.Data is documented as "This property is not used internally" - Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; - break; - case NetEvents.EventType.WindowPosition: - break; - default: - throw new ArgumentOutOfRangeException (); - } - } - - MouseEvent ToDriverMouse (NetEvents.MouseEvent me) - { - //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}"); - - MouseFlags mouseFlag = 0; - - if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) { - mouseFlag |= MouseFlags.Button1Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) { - mouseFlag |= MouseFlags.Button1Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) { - mouseFlag |= MouseFlags.Button1Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) { - mouseFlag |= MouseFlags.Button1DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) { - mouseFlag |= MouseFlags.Button1TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) { - mouseFlag |= MouseFlags.Button2Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) { - mouseFlag |= MouseFlags.Button2Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) { - mouseFlag |= MouseFlags.Button2Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) { - mouseFlag |= MouseFlags.Button2DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) { - mouseFlag |= MouseFlags.Button2TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) { - mouseFlag |= MouseFlags.Button3Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) { - mouseFlag |= MouseFlags.Button3Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) { - mouseFlag |= MouseFlags.Button3Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) { - mouseFlag |= MouseFlags.Button3DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) { - mouseFlag |= MouseFlags.Button3TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) { - mouseFlag |= MouseFlags.WheeledUp; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) { - mouseFlag |= MouseFlags.WheeledDown; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) { - mouseFlag |= MouseFlags.WheeledLeft; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) { - mouseFlag |= MouseFlags.WheeledRight; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) { - mouseFlag |= MouseFlags.Button4Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) { - mouseFlag |= MouseFlags.Button4Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) { - mouseFlag |= MouseFlags.Button4Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) { - mouseFlag |= MouseFlags.Button4DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) { - mouseFlag |= MouseFlags.Button4TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) { - mouseFlag |= MouseFlags.ReportMousePosition; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) { - mouseFlag |= MouseFlags.ButtonShift; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) { - mouseFlag |= MouseFlags.ButtonCtrl; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) { - mouseFlag |= MouseFlags.ButtonAlt; - } - - return new MouseEvent () { - X = me.Position.X, - Y = me.Position.Y, - Flags = mouseFlag - }; - } - - public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) - { - NetEvents.InputResult input = new NetEvents.InputResult { - EventType = NetEvents.EventType.Key, - ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control) - }; - - try { - ProcessInput (input); - } catch (OverflowException) { } - } - - - #region Not Implemented - public override void Suspend () - { - throw new NotImplementedException (); - } - #endregion + return true; + } + #endregion + + + public void StartReportingMouseMoves() + { + Console.Out.Write(EscSeqUtils.CSI_EnableMouseEvents); + } + + public void StopReportingMouseMoves() + { + Console.Out.Write(EscSeqUtils.CSI_DisableMouseEvents); + } + + ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo(ConsoleKeyInfo consoleKeyInfo) + { + if (consoleKeyInfo.Key != ConsoleKey.Packet) + { + return consoleKeyInfo; + } + + var mod = consoleKeyInfo.Modifiers; + var shift = (mod & ConsoleModifiers.Shift) != 0; + var alt = (mod & ConsoleModifiers.Alt) != 0; + var control = (mod & ConsoleModifiers.Control) != 0; + + var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey(consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _); + + return new ConsoleKeyInfo((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control); + } + + Key MapKey(ConsoleKeyInfo keyInfo) + { + MapKeyModifiers(keyInfo, (Key)keyInfo.Key); + switch (keyInfo.Key) + { + case ConsoleKey.Escape: + return MapKeyModifiers(keyInfo, Key.Esc); + case ConsoleKey.Tab: + return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; + case ConsoleKey.Home: + return MapKeyModifiers(keyInfo, Key.Home); + case ConsoleKey.End: + return MapKeyModifiers(keyInfo, Key.End); + case ConsoleKey.LeftArrow: + return MapKeyModifiers(keyInfo, Key.CursorLeft); + case ConsoleKey.RightArrow: + return MapKeyModifiers(keyInfo, Key.CursorRight); + case ConsoleKey.UpArrow: + return MapKeyModifiers(keyInfo, Key.CursorUp); + case ConsoleKey.DownArrow: + return MapKeyModifiers(keyInfo, Key.CursorDown); + case ConsoleKey.PageUp: + return MapKeyModifiers(keyInfo, Key.PageUp); + case ConsoleKey.PageDown: + return MapKeyModifiers(keyInfo, Key.PageDown); + case ConsoleKey.Enter: + return MapKeyModifiers(keyInfo, Key.Enter); + case ConsoleKey.Spacebar: + return MapKeyModifiers(keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); + case ConsoleKey.Backspace: + return MapKeyModifiers(keyInfo, Key.Backspace); + case ConsoleKey.Delete: + return MapKeyModifiers(keyInfo, Key.DeleteChar); + case ConsoleKey.Insert: + return MapKeyModifiers(keyInfo, Key.InsertChar); + + case ConsoleKey.Oem1: + case ConsoleKey.Oem2: + case ConsoleKey.Oem3: + case ConsoleKey.Oem4: + case ConsoleKey.Oem5: + case ConsoleKey.Oem6: + case ConsoleKey.Oem7: + case ConsoleKey.Oem8: + case ConsoleKey.Oem102: + case ConsoleKey.OemPeriod: + case ConsoleKey.OemComma: + case ConsoleKey.OemPlus: + case ConsoleKey.OemMinus: + return (Key)((uint)keyInfo.KeyChar); + } + + var key = keyInfo.Key; + if (key >= ConsoleKey.A && key <= ConsoleKey.Z) + { + var delta = key - ConsoleKey.A; + if (keyInfo.Modifiers == ConsoleModifiers.Control) + { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Alt) + { + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) + { + if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) + { + return MapKeyModifiers(keyInfo, (Key)((uint)Key.A + delta)); + } + } + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) + { + var delta = key - ConsoleKey.D0; + if (keyInfo.Modifiers == ConsoleModifiers.Alt) + { + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Control) + { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) + { + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) + { + return MapKeyModifiers(keyInfo, (Key)((uint)Key.D0 + delta)); + } + } + return (Key)((uint)keyInfo.KeyChar); + } + if (key is >= ConsoleKey.F1 and <= ConsoleKey.F12) + { + var delta = key - ConsoleKey.F1; + if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) + { + return MapKeyModifiers(keyInfo, (Key)((uint)Key.F1 + delta)); + } + + return (Key)((uint)Key.F1 + delta); + } + if (keyInfo.KeyChar != 0) + { + return MapKeyModifiers(keyInfo, (Key)((uint)keyInfo.KeyChar)); + } + + return (Key)(0xffffffff); + } + + KeyModifiers _keyModifiers; + + Key MapKeyModifiers(ConsoleKeyInfo keyInfo, Key key) + { + _keyModifiers ??= new KeyModifiers(); + Key keyMod = new Key(); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) + { + keyMod = Key.ShiftMask; + _keyModifiers.Shift = true; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) + { + keyMod |= Key.CtrlMask; + _keyModifiers.Ctrl = true; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) + { + keyMod |= Key.AltMask; + _keyModifiers.Alt = true; + } + + return keyMod != Key.Null ? keyMod | key : key; + } + + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; + NetMainLoop _mainLoop; + + public override void PrepareToRun(MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) + { + _keyHandler = keyHandler; + _keyDownHandler = keyDownHandler; + _keyUpHandler = keyUpHandler; + _mouseHandler = mouseHandler; + + var mLoop = _mainLoop = mainLoop.MainLoopDriver as NetMainLoop; + + // Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. + mLoop.ProcessInput = (e) => ProcessInput(e); + } + + volatile bool _winSizeChanging; + + void ProcessInput(NetEvents.InputResult inputEvent) + { + switch (inputEvent.EventType) + { + case NetEvents.EventType.Key: + ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo; + if (consoleKeyInfo.Key == ConsoleKey.Packet) + { + consoleKeyInfo = FromVKPacketToKConsoleKeyInfo(consoleKeyInfo); + } + _keyModifiers = new KeyModifiers(); + var map = MapKey(consoleKeyInfo); + if (map == (Key)0xffffffff) + { + return; + } + if (map == Key.Null) + { + _keyDownHandler(new KeyEvent(map, _keyModifiers)); + _keyUpHandler(new KeyEvent(map, _keyModifiers)); + } + else + { + _keyDownHandler(new KeyEvent(map, _keyModifiers)); + _keyHandler(new KeyEvent(map, _keyModifiers)); + _keyUpHandler(new KeyEvent(map, _keyModifiers)); + } + break; + case NetEvents.EventType.Mouse: + _mouseHandler(ToDriverMouse(inputEvent.MouseEvent)); + break; + case NetEvents.EventType.WindowSize: + _winSizeChanging = true; + Top = 0; + Left = 0; + Cols = inputEvent.WindowSizeEvent.Size.Width; + Rows = Math.Max(inputEvent.WindowSizeEvent.Size.Height, 0); ; + ResizeScreen(); + ClearContents(); + _winSizeChanging = false; + TerminalResized?.Invoke(); + break; + case NetEvents.EventType.RequestResponse: + // BUGBUG: What is this for? It does not seem to be used anywhere. + // It is also not clear what it does. View.Data is documented as "This property is not used internally" + Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; + break; + case NetEvents.EventType.WindowPosition: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + MouseEvent ToDriverMouse(NetEvents.MouseEvent me) + { + //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}"); + + MouseFlags mouseFlag = 0; + + if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) + { + mouseFlag |= MouseFlags.Button1Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) + { + mouseFlag |= MouseFlags.Button1Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) + { + mouseFlag |= MouseFlags.Button1Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) + { + mouseFlag |= MouseFlags.Button1DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) + { + mouseFlag |= MouseFlags.Button1TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) + { + mouseFlag |= MouseFlags.Button2Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) + { + mouseFlag |= MouseFlags.Button2Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) + { + mouseFlag |= MouseFlags.Button2Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) + { + mouseFlag |= MouseFlags.Button2DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) + { + mouseFlag |= MouseFlags.Button2TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) + { + mouseFlag |= MouseFlags.Button3Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) + { + mouseFlag |= MouseFlags.Button3Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) + { + mouseFlag |= MouseFlags.Button3Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) + { + mouseFlag |= MouseFlags.Button3DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) + { + mouseFlag |= MouseFlags.Button3TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) + { + mouseFlag |= MouseFlags.WheeledUp; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) + { + mouseFlag |= MouseFlags.WheeledDown; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) + { + mouseFlag |= MouseFlags.WheeledLeft; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) + { + mouseFlag |= MouseFlags.WheeledRight; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) + { + mouseFlag |= MouseFlags.Button4Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) + { + mouseFlag |= MouseFlags.Button4Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) + { + mouseFlag |= MouseFlags.Button4Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) + { + mouseFlag |= MouseFlags.Button4DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) + { + mouseFlag |= MouseFlags.Button4TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) + { + mouseFlag |= MouseFlags.ReportMousePosition; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) + { + mouseFlag |= MouseFlags.ButtonShift; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) + { + mouseFlag |= MouseFlags.ButtonCtrl; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) + { + mouseFlag |= MouseFlags.ButtonAlt; + } + + return new MouseEvent() + { + X = me.Position.X, + Y = me.Position.Y, + Flags = mouseFlag + }; + } + + public override void SendKeys(char keyChar, ConsoleKey key, bool shift, bool alt, bool control) + { + NetEvents.InputResult input = new NetEvents.InputResult + { + EventType = NetEvents.EventType.Key, + ConsoleKeyInfo = new ConsoleKeyInfo(keyChar, key, shift, alt, control) + }; + + try + { + ProcessInput(input); + } + catch (OverflowException) { } + } + + + #region Not Implemented + public override void Suspend() + { + throw new NotImplementedException(); + } + #endregion } @@ -1315,125 +1423,148 @@ public override void Suspend () /// /// This implementation is used for NetDriver. /// -internal class NetMainLoop : IMainLoopDriver { - ManualResetEventSlim _keyReady = new ManualResetEventSlim (false); - ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); - Queue _inputResult = new Queue (); - MainLoop _mainLoop; - CancellationTokenSource _tokenSource = new CancellationTokenSource (); - internal NetEvents _netEvents; - - /// - /// Invoked when a Key is pressed. - /// - internal Action ProcessInput; - - /// - /// Initializes the class with the console driver. - /// - /// - /// Passing a consoleDriver is provided to capture windows resizing. - /// - /// The console driver used by this Net main loop. - /// - public NetMainLoop (ConsoleDriver consoleDriver = null) - { - if (consoleDriver == null) { - throw new ArgumentNullException (nameof (consoleDriver)); - } - _netEvents = new NetEvents (consoleDriver); - } - - void NetInputHandler () - { - while (true) { - _waitForProbe.Wait (); - _waitForProbe.Reset (); - if (_inputResult.Count == 0) { - _inputResult.Enqueue (_netEvents.ReadConsoleInput ()); - } - try { - while (_inputResult.Peek () == null) { - _inputResult.Dequeue (); - } - if (_inputResult.Count > 0) { - _keyReady.Set (); - } - } catch (InvalidOperationException) { } - } - } - - void IMainLoopDriver.Setup (MainLoop mainLoop) - { - _mainLoop = mainLoop; - Task.Run (NetInputHandler); - } - - void IMainLoopDriver.Wakeup () - { - _keyReady.Set (); - } - - bool IMainLoopDriver.EventsPending (bool wait) - { - _waitForProbe.Set (); - - if (CheckTimers (wait, out var waitTimeout)) { - return true; - } - - try { - if (!_tokenSource.IsCancellationRequested) { - _keyReady.Wait (waitTimeout, _tokenSource.Token); - } - } catch (OperationCanceledException) { - return true; - } finally { - _keyReady.Reset (); - } - - if (!_tokenSource.IsCancellationRequested) { - return _inputResult.Count > 0 || CheckTimers (wait, out _); - } - - _tokenSource.Dispose (); - _tokenSource = new CancellationTokenSource (); - return true; - } - - bool CheckTimers (bool wait, out int waitTimeout) - { - var now = DateTime.UtcNow.Ticks; - - if (_mainLoop.timeouts.Count > 0) { - waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); - if (waitTimeout < 0) { - return true; - } - } else { - waitTimeout = -1; - } - - if (!wait) { - waitTimeout = 0; - } - - int ic; - lock (_mainLoop.idleHandlers) { - ic = _mainLoop.idleHandlers.Count; - } - - return ic > 0; - } - - void IMainLoopDriver.Iteration () - { - while (_inputResult.Count > 0) { - ProcessInput?.Invoke (_inputResult.Dequeue ().Value); - } - } - public void TearDown () - { - //throw new NotImplementedException (); - } +internal class NetMainLoop : IMainLoopDriver +{ + ManualResetEventSlim _keyReady = new ManualResetEventSlim(false); + ManualResetEventSlim _waitForProbe = new ManualResetEventSlim(false); + Queue _inputResult = new Queue(); + MainLoop _mainLoop; + CancellationTokenSource _tokenSource = new CancellationTokenSource(); + internal NetEvents _netEvents; + + /// + /// Invoked when a Key is pressed. + /// + internal Action ProcessInput; + + /// + /// Initializes the class with the console driver. + /// + /// + /// Passing a consoleDriver is provided to capture windows resizing. + /// + /// The console driver used by this Net main loop. + /// + public NetMainLoop(ConsoleDriver consoleDriver = null) + { + if (consoleDriver == null) + { + throw new ArgumentNullException(nameof(consoleDriver)); + } + _netEvents = new NetEvents(consoleDriver); + } + + void NetInputHandler() + { + while (true) + { + _waitForProbe.Wait(); + _waitForProbe.Reset(); + if (_inputResult.Count == 0) + { + _inputResult.Enqueue(_netEvents.ReadConsoleInput()); + } + try + { + while (_inputResult.Peek() == null) + { + _inputResult.Dequeue(); + } + if (_inputResult.Count > 0) + { + _keyReady.Set(); + } + } + catch (InvalidOperationException) { } + } + } + + void IMainLoopDriver.Setup(MainLoop mainLoop) + { + _mainLoop = mainLoop; + Task.Run(NetInputHandler); + } + + void IMainLoopDriver.Wakeup() + { + _keyReady.Set(); + } + + bool IMainLoopDriver.EventsPending(bool wait) + { + _waitForProbe.Set(); + + if (CheckTimers(wait, out var waitTimeout)) + { + return true; + } + + try + { + if (!_tokenSource.IsCancellationRequested) + { + _keyReady.Wait(waitTimeout, _tokenSource.Token); + } + } + catch (OperationCanceledException) + { + return true; + } + finally + { + _keyReady.Reset(); + } + + if (!_tokenSource.IsCancellationRequested) + { + return _inputResult.Count > 0 || CheckTimers(wait, out _); + } + + _tokenSource.Dispose(); + _tokenSource = new CancellationTokenSource(); + return true; + } + + bool CheckTimers(bool wait, out int waitTimeout) + { + var now = DateTime.UtcNow.Ticks; + + if (_mainLoop.timeouts.Count > 0) + { + waitTimeout = (int)((_mainLoop.timeouts.Keys[0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) + { + return true; + } + } + else + { + waitTimeout = -1; + } + + if (!wait) + { + waitTimeout = 0; + } + + int ic; + lock (_mainLoop.idleHandlers) + { + ic = _mainLoop.idleHandlers.Count; + } + + return ic > 0; + } + + void IMainLoopDriver.Iteration() + { + while (_inputResult.Count > 0) + { + ProcessInput?.Invoke(_inputResult.Dequeue().Value); + } + } + public void TearDown() + { + //throw new NotImplementedException (); + } } \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 551da271c5..db8f342895 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -12,671 +12,747 @@ namespace Terminal.Gui; -internal class WindowsConsole { - public const int STD_OUTPUT_HANDLE = -11; - public const int STD_INPUT_HANDLE = -10; - public const int STD_ERROR_HANDLE = -12; - - IntPtr _inputHandle, _outputHandle; - IntPtr _screenBuffer; - readonly uint _originalConsoleMode; - CursorVisibility? _initialCursorVisibility = null; - CursorVisibility? _currentCursorVisibility = null; - CursorVisibility? _pendingCursorVisibility = null; - readonly StringBuilder _stringBuilder = new StringBuilder (256 * 1024); - - public WindowsConsole () - { - _inputHandle = GetStdHandle (STD_INPUT_HANDLE); - _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); - _originalConsoleMode = ConsoleMode; - var newConsoleMode = _originalConsoleMode; - newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags); - newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode; - newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput; - ConsoleMode = newConsoleMode; - } - - CharInfo [] _originalStdOutChars; - - public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord coords, SmallRect window, bool useTrueColor) - { - if (_screenBuffer == IntPtr.Zero) { - ReadFromConsoleOutput (size, coords, ref window); - } - - if (!useTrueColor) { - int i = 0; - CharInfo [] ci = new CharInfo [charInfoBuffer.Length]; - foreach (ExtendedCharInfo info in charInfoBuffer) { - ci [i++] = new CharInfo () { - Char = new CharUnion () { UnicodeChar = info.Char }, - Attributes = (ushort)info.Attribute.Value - }; - } - return WriteConsoleOutput (_screenBuffer, ci, coords, new Coord () { X = window.Left, Y = window.Top }, ref window); - } - - - return WriteConsoleTrueColorOutput (charInfoBuffer); - } - - private bool WriteConsoleTrueColorOutput (ExtendedCharInfo [] charInfoBuffer) - { - _stringBuilder.Clear (); - - _stringBuilder.Append (EscSeqUtils.CSI_SaveCursorPosition); - _stringBuilder.Append (EscSeqUtils.CSI_SetCursorPosition (0, 0)); - - Attribute? prev = null; - foreach (var info in charInfoBuffer) { - var attr = info.Attribute; - - if (attr != prev) { - prev = attr; - - _stringBuilder.Append (EscSeqUtils.CSI_SetForegroundColorRGB (attr.TrueColorForeground.Value.Red, attr.TrueColorForeground.Value.Green, attr.TrueColorForeground.Value.Blue)); - _stringBuilder.Append (EscSeqUtils.CSI_SetBackgroundColorRGB (attr.TrueColorBackground.Value.Red, attr.TrueColorBackground.Value.Green, attr.TrueColorBackground.Value.Blue)); - } - - if (info.Char != '\x1b') { - if (!info.Empty) { - _stringBuilder.Append (info.Char); - } - - } else { - _stringBuilder.Append (' '); - } - } - - _stringBuilder.Append (EscSeqUtils.CSI_RestoreCursorPosition); - - string s = _stringBuilder.ToString (); - - return WriteConsole (_screenBuffer, s, (uint)(s.Length), out uint _, null); - } - - public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window) - { - _screenBuffer = CreateConsoleScreenBuffer ( - DesiredAccess.GenericRead | DesiredAccess.GenericWrite, - ShareMode.FileShareRead | ShareMode.FileShareWrite, - IntPtr.Zero, - 1, - IntPtr.Zero - ); - if (_screenBuffer == INVALID_HANDLE_VALUE) { - var err = Marshal.GetLastWin32Error (); - - if (err != 0) { - throw new System.ComponentModel.Win32Exception (err); - } - } - - if (!_initialCursorVisibility.HasValue && GetCursorVisibility (out CursorVisibility visibility)) { - _initialCursorVisibility = visibility; - } - - if (!SetConsoleActiveScreenBuffer (_screenBuffer)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - - _originalStdOutChars = new CharInfo [size.Height * size.Width]; - - if (!ReadConsoleOutput (_screenBuffer, _originalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - } - - public bool SetCursorPosition (Coord position) - { - return SetConsoleCursorPosition (_screenBuffer, position); - } - - public void SetInitialCursorVisibility () - { - if (_initialCursorVisibility.HasValue == false && GetCursorVisibility (out CursorVisibility visibility)) { - _initialCursorVisibility = visibility; - } - } - - public bool GetCursorVisibility (out CursorVisibility visibility) - { - if (_screenBuffer == IntPtr.Zero) { - visibility = CursorVisibility.Invisible; - return false; - } - if (!GetConsoleCursorInfo (_screenBuffer, out ConsoleCursorInfo info)) { - var err = Marshal.GetLastWin32Error (); - if (err != 0) { - throw new System.ComponentModel.Win32Exception (err); - } - visibility = Gui.CursorVisibility.Default; - - return false; - } - - if (!info.bVisible) { - visibility = CursorVisibility.Invisible; - } else if (info.dwSize > 50) { - visibility = CursorVisibility.Box; - } else { - visibility = CursorVisibility.Underline; - } - - return true; - } - - public bool EnsureCursorVisibility () - { - if (_initialCursorVisibility.HasValue && _pendingCursorVisibility.HasValue && SetCursorVisibility (_pendingCursorVisibility.Value)) { - _pendingCursorVisibility = null; - - return true; - } - - return false; - } - - public void ForceRefreshCursorVisibility () - { - if (_currentCursorVisibility.HasValue) { - _pendingCursorVisibility = _currentCursorVisibility; - _currentCursorVisibility = null; - } - } - - public bool SetCursorVisibility (CursorVisibility visibility) - { - if (_initialCursorVisibility.HasValue == false) { - _pendingCursorVisibility = visibility; - - return false; - } - - if (_currentCursorVisibility.HasValue == false || _currentCursorVisibility.Value != visibility) { - ConsoleCursorInfo info = new ConsoleCursorInfo { - dwSize = (uint)visibility & 0x00FF, - bVisible = ((uint)visibility & 0xFF00) != 0 - }; - - if (!SetConsoleCursorInfo (_screenBuffer, ref info)) { - return false; - } - - _currentCursorVisibility = visibility; - } - - return true; - } - - public void Cleanup () - { - if (_initialCursorVisibility.HasValue) { - SetCursorVisibility (_initialCursorVisibility.Value); - } - - SetConsoleOutputWindow (out _); - - ConsoleMode = _originalConsoleMode; - //ContinueListeningForConsoleEvents = false; - if (!SetConsoleActiveScreenBuffer (_outputHandle)) { - var err = Marshal.GetLastWin32Error (); - Console.WriteLine ("Error: {0}", err); - } - - if (_screenBuffer != IntPtr.Zero) { - CloseHandle (_screenBuffer); - } - - _screenBuffer = IntPtr.Zero; - } - - internal Size GetConsoleBufferWindow (out Point position) - { - if (_screenBuffer == IntPtr.Zero) { - position = Point.Empty; - return Size.Empty; - } - - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - position = Point.Empty; - return Size.Empty; - } - var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, - csbi.srWindow.Bottom - csbi.srWindow.Top + 1); - position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); - - return sz; - } - - internal Size GetConsoleOutputWindow (out Point position) - { - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, - csbi.srWindow.Bottom - csbi.srWindow.Top + 1); - position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); - - return sz; - } - - internal Size SetConsoleWindow (short cols, short rows) - { - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - var maxWinSize = GetLargestConsoleWindowSize (_screenBuffer); - var newCols = Math.Min (cols, maxWinSize.X); - var newRows = Math.Min (rows, maxWinSize.Y); - csbi.dwSize = new Coord (newCols, Math.Max (newRows, (short)1)); - csbi.srWindow = new SmallRect (0, 0, newCols, newRows); - csbi.dwMaximumWindowSize = new Coord (newCols, newRows); - if (!SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0)); - if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { - //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - return new Size (cols, rows); - } - SetConsoleOutputWindow (csbi); - return new Size (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1); - } - - void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi) - { - if (_screenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - } - - internal Size SetConsoleOutputWindow (out Point position) - { - if (_screenBuffer == IntPtr.Zero) { - position = Point.Empty; - return Size.Empty; - } - - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); - csbi.cbSize = (uint)Marshal.SizeOf (csbi); - if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, - Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0)); - position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); - SetConsoleOutputWindow (csbi); - var winRect = new SmallRect (0, 0, (short)(sz.Width - 1), (short)Math.Max (sz.Height - 1, 0)); - if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { - throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - } - - return sz; - } - - //bool ContinueListeningForConsoleEvents = true; - - uint ConsoleMode { - get { - GetConsoleMode (_inputHandle, out uint v); - return v; - } - set { - SetConsoleMode (_inputHandle, value); - } - } - - [Flags] - public enum ConsoleModes : uint { - EnableProcessedInput = 1, - EnableMouseInput = 16, - EnableQuickEditMode = 64, - EnableExtendedFlags = 128, - } - - [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] - public struct KeyEventRecord { - [FieldOffset (0), MarshalAs (UnmanagedType.Bool)] - public bool bKeyDown; - [FieldOffset (4), MarshalAs (UnmanagedType.U2)] - public ushort wRepeatCount; - [FieldOffset (6), MarshalAs (UnmanagedType.U2)] - public ushort wVirtualKeyCode; - [FieldOffset (8), MarshalAs (UnmanagedType.U2)] - public ushort wVirtualScanCode; - [FieldOffset (10)] - public char UnicodeChar; - [FieldOffset (12), MarshalAs (UnmanagedType.U4)] - public ControlKeyState dwControlKeyState; - } - - [Flags] - public enum ButtonState { - Button1Pressed = 1, - Button2Pressed = 4, - Button3Pressed = 8, - Button4Pressed = 16, - RightmostButtonPressed = 2 - } - - [Flags] - public enum ControlKeyState { - RightAltPressed = 1, - LeftAltPressed = 2, - RightControlPressed = 4, - LeftControlPressed = 8, - ShiftPressed = 16, - NumlockOn = 32, - ScrolllockOn = 64, - CapslockOn = 128, - EnhancedKey = 256 - } - - [Flags] - public enum EventFlags { - MouseMoved = 1, - DoubleClick = 2, - MouseWheeled = 4, - MouseHorizontalWheeled = 8 - } - - [StructLayout (LayoutKind.Explicit)] - public struct MouseEventRecord { - [FieldOffset (0)] - public Coord MousePosition; - [FieldOffset (4)] - public ButtonState ButtonState; - [FieldOffset (8)] - public ControlKeyState ControlKeyState; - [FieldOffset (12)] - public EventFlags EventFlags; - - public override readonly string ToString () => $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}"; - - } - - public struct WindowBufferSizeRecord { - public Coord _size; - - public WindowBufferSizeRecord (short x, short y) - { - _size = new Coord (x, y); - } - - public override readonly string ToString () => $"[WindowBufferSize{_size}"; - } - - [StructLayout (LayoutKind.Sequential)] - public struct MenuEventRecord { - public uint dwCommandId; - } - - [StructLayout (LayoutKind.Sequential)] - public struct FocusEventRecord { - public uint bSetFocus; - } - - public enum EventType : ushort { - Focus = 0x10, - Key = 0x1, - Menu = 0x8, - Mouse = 2, - WindowBufferSize = 4 - } - - [StructLayout (LayoutKind.Explicit)] - public struct InputRecord { - [FieldOffset (0)] - public EventType EventType; - [FieldOffset (4)] - public KeyEventRecord KeyEvent; - [FieldOffset (4)] - public MouseEventRecord MouseEvent; - [FieldOffset (4)] - public WindowBufferSizeRecord WindowBufferSizeEvent; - [FieldOffset (4)] - public MenuEventRecord MenuEvent; - [FieldOffset (4)] - public FocusEventRecord FocusEvent; - - public override readonly string ToString () - { - return EventType switch { - EventType.Focus => FocusEvent.ToString (), - EventType.Key => KeyEvent.ToString (), - EventType.Menu => MenuEvent.ToString (), - EventType.Mouse => MouseEvent.ToString (), - EventType.WindowBufferSize => WindowBufferSizeEvent.ToString (), - _ => "Unknown event type: " + EventType - }; - } - }; - - [Flags] - enum ShareMode : uint { - FileShareRead = 1, - FileShareWrite = 2, - } - - [Flags] - enum DesiredAccess : uint { - GenericRead = 2147483648, - GenericWrite = 1073741824, - } - - [StructLayout (LayoutKind.Sequential)] - public struct ConsoleScreenBufferInfo { - public Coord dwSize; - public Coord dwCursorPosition; - public ushort wAttributes; - public SmallRect srWindow; - public Coord dwMaximumWindowSize; - } - - [StructLayout (LayoutKind.Sequential)] - public struct Coord { - public short X; - public short Y; - - public Coord (short x, short y) - { - X = x; - Y = y; - } - public override readonly string ToString () => $"({X},{Y})"; - }; - - [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] - public struct CharUnion { - [FieldOffset (0)] public char UnicodeChar; - [FieldOffset (0)] public byte AsciiChar; - } - - [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] - public struct CharInfo { - [FieldOffset (0)] public CharUnion Char; - [FieldOffset (2)] public ushort Attributes; - } - - public struct ExtendedCharInfo { - public char Char { get; set; } - public Attribute Attribute { get; set; } - public bool Empty { get; set; } // TODO: Temp hack until virutal terminal sequences - - public ExtendedCharInfo (char character, Attribute attribute) - { - Char = character; - Attribute = attribute; - Empty = false; - } - } - - [StructLayout (LayoutKind.Sequential)] - public struct SmallRect { - public short Left; - public short Top; - public short Right; - public short Bottom; - - public SmallRect (short left, short top, short right, short bottom) - { - Left = left; - Top = top; - Right = right; - Bottom = bottom; - } - - public static void MakeEmpty (ref SmallRect rect) - { - rect.Left = -1; - } - - public static void Update (ref SmallRect rect, short col, short row) - { - if (rect.Left == -1) { - rect.Left = rect.Right = col; - rect.Bottom = rect.Top = row; - return; - } - if (col >= rect.Left && col <= rect.Right && row >= rect.Top && row <= rect.Bottom) - return; - if (col < rect.Left) - rect.Left = col; - if (col > rect.Right) - rect.Right = col; - if (row < rect.Top) - rect.Top = row; - if (row > rect.Bottom) - rect.Bottom = row; - } - - public override readonly string ToString () => $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}"; - } - - [StructLayout (LayoutKind.Sequential)] - public struct ConsoleKeyInfoEx { - public ConsoleKeyInfo ConsoleKeyInfo; - public bool CapsLock; - public bool NumLock; - public bool ScrollLock; - - public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock) - { - ConsoleKeyInfo = consoleKeyInfo; - CapsLock = capslock; - NumLock = numlock; - ScrollLock = scrolllock; - } - } - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern IntPtr GetStdHandle (int nStdHandle); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool CloseHandle (IntPtr handle); - - [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)] - public static extern bool ReadConsoleInput ( - IntPtr hConsoleInput, - IntPtr lpBuffer, - uint nLength, - out uint lpNumberOfEventsRead); - - [DllImport ("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool ReadConsoleOutput ( - IntPtr hConsoleOutput, - [Out] CharInfo [] lpBuffer, - Coord dwBufferSize, - Coord dwBufferCoord, - ref SmallRect lpReadRegion - ); - - // TODO: This API is obsolete. See https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput - [DllImport ("kernel32.dll", EntryPoint = "WriteConsoleOutputW", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool WriteConsoleOutput ( - IntPtr hConsoleOutput, - CharInfo [] lpBuffer, - Coord dwBufferSize, - Coord dwBufferCoord, - ref SmallRect lpWriteRegion - ); - - [DllImport ("kernel32.dll", EntryPoint = "WriteConsole", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool WriteConsole ( - IntPtr hConsoleOutput, - String lpbufer, - UInt32 NumberOfCharsToWriten, - out UInt32 lpNumberOfCharsWritten, - object lpReserved - ); - - [DllImport ("kernel32.dll")] - static extern bool SetConsoleCursorPosition (IntPtr hConsoleOutput, Coord dwCursorPosition); - - [StructLayout (LayoutKind.Sequential)] - public struct ConsoleCursorInfo { - public uint dwSize; - public bool bVisible; - } - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleCursorInfo (IntPtr hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool GetConsoleCursorInfo (IntPtr hConsoleOutput, out ConsoleCursorInfo lpConsoleCursorInfo); - - [DllImport ("kernel32.dll")] - static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode); - - [DllImport ("kernel32.dll")] - static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern IntPtr CreateConsoleScreenBuffer ( - DesiredAccess dwDesiredAccess, - ShareMode dwShareMode, - IntPtr secutiryAttributes, - uint flags, - IntPtr screenBufferData - ); - - internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr (-1); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleActiveScreenBuffer (IntPtr Handle); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents); - - public InputRecord [] ReadConsoleInput () - { - const int bufferSize = 1; - var pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * bufferSize); - try { - ReadConsoleInput (_inputHandle, pRecord, bufferSize, - out var numberEventsRead); - - return numberEventsRead == 0 - ? null - : new [] { Marshal.PtrToStructure (pRecord) }; - } catch (Exception) { - return null; - } finally { - Marshal.FreeHGlobal (pRecord); - } - } +internal class WindowsConsole +{ + public const int STD_OUTPUT_HANDLE = -11; + public const int STD_INPUT_HANDLE = -10; + public const int STD_ERROR_HANDLE = -12; + + IntPtr _inputHandle, _outputHandle; + IntPtr _screenBuffer; + readonly uint _originalConsoleMode; + CursorVisibility? _initialCursorVisibility = null; + CursorVisibility? _currentCursorVisibility = null; + CursorVisibility? _pendingCursorVisibility = null; + readonly StringBuilder _stringBuilder = new StringBuilder(256 * 1024); + + public WindowsConsole() + { + _inputHandle = GetStdHandle(STD_INPUT_HANDLE); + _outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); + _originalConsoleMode = ConsoleMode; + var newConsoleMode = _originalConsoleMode; + newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags); + newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode; + newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput; + ConsoleMode = newConsoleMode; + } + + CharInfo[] _originalStdOutChars; + + public bool WriteToConsole(Size size, ExtendedCharInfo[] charInfoBuffer, Coord coords, SmallRect window, bool useTrueColor) + { + if (_screenBuffer == IntPtr.Zero) + { + ReadFromConsoleOutput(size, coords, ref window); + } + + if (!useTrueColor) + { + int i = 0; + CharInfo[] ci = new CharInfo[charInfoBuffer.Length]; + foreach (ExtendedCharInfo info in charInfoBuffer) + { + ci[i++] = new CharInfo() + { + Char = new CharUnion() { UnicodeChar = info.Char }, + Attributes = (ushort)info.Attribute.Value + }; + } + return WriteConsoleOutput(_screenBuffer, ci, coords, new Coord() { X = window.Left, Y = window.Top }, ref window); + } + + + return WriteConsoleTrueColorOutput(charInfoBuffer); + } + + private bool WriteConsoleTrueColorOutput(ExtendedCharInfo[] charInfoBuffer) + { + _stringBuilder.Clear(); + + _stringBuilder.Append(EscSeqUtils.CSI_SaveCursorPosition); + _stringBuilder.Append(EscSeqUtils.CSI_SetCursorPosition(0, 0)); + + Attribute? prev = null; + foreach (var info in charInfoBuffer) + { + var attr = info.Attribute; + + if (attr != prev) + { + prev = attr; + + _stringBuilder.Append(EscSeqUtils.CSI_SetForegroundColorRGB(attr.TrueColorForeground.Value.Red, attr.TrueColorForeground.Value.Green, attr.TrueColorForeground.Value.Blue)); + _stringBuilder.Append(EscSeqUtils.CSI_SetBackgroundColorRGB(attr.TrueColorBackground.Value.Red, attr.TrueColorBackground.Value.Green, attr.TrueColorBackground.Value.Blue)); + } + + if (info.Char != '\x1b') + { + if (!info.Empty) + { + _stringBuilder.Append(info.Char); + } + + } + else + { + _stringBuilder.Append(' '); + } + } + + _stringBuilder.Append(EscSeqUtils.CSI_RestoreCursorPosition); + + string s = _stringBuilder.ToString(); + + return WriteConsole(_screenBuffer, s, (uint)(s.Length), out uint _, null); + } + + public void ReadFromConsoleOutput(Size size, Coord coords, ref SmallRect window) + { + _screenBuffer = CreateConsoleScreenBuffer( + DesiredAccess.GenericRead | DesiredAccess.GenericWrite, + ShareMode.FileShareRead | ShareMode.FileShareWrite, + IntPtr.Zero, + 1, + IntPtr.Zero + ); + if (_screenBuffer == INVALID_HANDLE_VALUE) + { + var err = Marshal.GetLastWin32Error(); + + if (err != 0) + { + throw new System.ComponentModel.Win32Exception(err); + } + } + + if (!_initialCursorVisibility.HasValue && GetCursorVisibility(out CursorVisibility visibility)) + { + _initialCursorVisibility = visibility; + } + + if (!SetConsoleActiveScreenBuffer(_screenBuffer)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + + _originalStdOutChars = new CharInfo[size.Height * size.Width]; + + if (!ReadConsoleOutput(_screenBuffer, _originalStdOutChars, coords, new Coord() { X = 0, Y = 0 }, ref window)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + } + + public bool SetCursorPosition(Coord position) + { + return SetConsoleCursorPosition(_screenBuffer, position); + } + + public void SetInitialCursorVisibility() + { + if (_initialCursorVisibility.HasValue == false && GetCursorVisibility(out CursorVisibility visibility)) + { + _initialCursorVisibility = visibility; + } + } + + public bool GetCursorVisibility(out CursorVisibility visibility) + { + if (_screenBuffer == IntPtr.Zero) + { + visibility = CursorVisibility.Invisible; + return false; + } + if (!GetConsoleCursorInfo(_screenBuffer, out ConsoleCursorInfo info)) + { + var err = Marshal.GetLastWin32Error(); + if (err != 0) + { + throw new System.ComponentModel.Win32Exception(err); + } + visibility = Gui.CursorVisibility.Default; + + return false; + } + + if (!info.bVisible) + { + visibility = CursorVisibility.Invisible; + } + else if (info.dwSize > 50) + { + visibility = CursorVisibility.Box; + } + else + { + visibility = CursorVisibility.Underline; + } + + return true; + } + + public bool EnsureCursorVisibility() + { + if (_initialCursorVisibility.HasValue && _pendingCursorVisibility.HasValue && SetCursorVisibility(_pendingCursorVisibility.Value)) + { + _pendingCursorVisibility = null; + + return true; + } + + return false; + } + + public void ForceRefreshCursorVisibility() + { + if (_currentCursorVisibility.HasValue) + { + _pendingCursorVisibility = _currentCursorVisibility; + _currentCursorVisibility = null; + } + } + + public bool SetCursorVisibility(CursorVisibility visibility) + { + if (_initialCursorVisibility.HasValue == false) + { + _pendingCursorVisibility = visibility; + + return false; + } + + if (_currentCursorVisibility.HasValue == false || _currentCursorVisibility.Value != visibility) + { + ConsoleCursorInfo info = new ConsoleCursorInfo + { + dwSize = (uint)visibility & 0x00FF, + bVisible = ((uint)visibility & 0xFF00) != 0 + }; + + if (!SetConsoleCursorInfo(_screenBuffer, ref info)) + { + return false; + } + + _currentCursorVisibility = visibility; + } + + return true; + } + + public void Cleanup() + { + if (_initialCursorVisibility.HasValue) + { + SetCursorVisibility(_initialCursorVisibility.Value); + } + + SetConsoleOutputWindow(out _); + + ConsoleMode = _originalConsoleMode; + //ContinueListeningForConsoleEvents = false; + if (!SetConsoleActiveScreenBuffer(_outputHandle)) + { + var err = Marshal.GetLastWin32Error(); + Console.WriteLine("Error: {0}", err); + } + + if (_screenBuffer != IntPtr.Zero) + { + CloseHandle(_screenBuffer); + } + + _screenBuffer = IntPtr.Zero; + } + + internal Size GetConsoleBufferWindow(out Point position) + { + if (_screenBuffer == IntPtr.Zero) + { + position = Point.Empty; + return Size.Empty; + } + + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX(); + csbi.cbSize = (uint)Marshal.SizeOf(csbi); + if (!GetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) + { + //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + position = Point.Empty; + return Size.Empty; + } + var sz = new Size(csbi.srWindow.Right - csbi.srWindow.Left + 1, + csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + position = new Point(csbi.srWindow.Left, csbi.srWindow.Top); + + return sz; + } + + internal Size GetConsoleOutputWindow(out Point position) + { + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX(); + csbi.cbSize = (uint)Marshal.SizeOf(csbi); + if (!GetConsoleScreenBufferInfoEx(_outputHandle, ref csbi)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + var sz = new Size(csbi.srWindow.Right - csbi.srWindow.Left + 1, + csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + position = new Point(csbi.srWindow.Left, csbi.srWindow.Top); + + return sz; + } + + internal Size SetConsoleWindow(short cols, short rows) + { + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX(); + csbi.cbSize = (uint)Marshal.SizeOf(csbi); + if (!GetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + var maxWinSize = GetLargestConsoleWindowSize(_screenBuffer); + var newCols = Math.Min(cols, maxWinSize.X); + var newRows = Math.Min(rows, maxWinSize.Y); + csbi.dwSize = new Coord(newCols, Math.Max(newRows, (short)1)); + csbi.srWindow = new SmallRect(0, 0, newCols, newRows); + csbi.dwMaximumWindowSize = new Coord(newCols, newRows); + if (!SetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + var winRect = new SmallRect(0, 0, (short)(newCols - 1), (short)Math.Max(newRows - 1, 0)); + if (!SetConsoleWindowInfo(_outputHandle, true, ref winRect)) + { + //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + return new Size(cols, rows); + } + SetConsoleOutputWindow(csbi); + return new Size(winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1); + } + + void SetConsoleOutputWindow(CONSOLE_SCREEN_BUFFER_INFOEX csbi) + { + if (_screenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + } + + internal Size SetConsoleOutputWindow(out Point position) + { + if (_screenBuffer == IntPtr.Zero) + { + position = Point.Empty; + return Size.Empty; + } + + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX(); + csbi.cbSize = (uint)Marshal.SizeOf(csbi); + if (!GetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + var sz = new Size(csbi.srWindow.Right - csbi.srWindow.Left + 1, + Math.Max(csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0)); + position = new Point(csbi.srWindow.Left, csbi.srWindow.Top); + SetConsoleOutputWindow(csbi); + var winRect = new SmallRect(0, 0, (short)(sz.Width - 1), (short)Math.Max(sz.Height - 1, 0)); + if (!SetConsoleScreenBufferInfoEx(_outputHandle, ref csbi)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + if (!SetConsoleWindowInfo(_outputHandle, true, ref winRect)) + { + throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); + } + + return sz; + } + + //bool ContinueListeningForConsoleEvents = true; + + uint ConsoleMode + { + get + { + GetConsoleMode(_inputHandle, out uint v); + return v; + } + set + { + SetConsoleMode(_inputHandle, value); + } + } + + [Flags] + public enum ConsoleModes : uint + { + EnableProcessedInput = 1, + EnableMouseInput = 16, + EnableQuickEditMode = 64, + EnableExtendedFlags = 128, + } + + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] + public struct KeyEventRecord + { + [FieldOffset(0), MarshalAs(UnmanagedType.Bool)] + public bool bKeyDown; + [FieldOffset(4), MarshalAs(UnmanagedType.U2)] + public ushort wRepeatCount; + [FieldOffset(6), MarshalAs(UnmanagedType.U2)] + public ushort wVirtualKeyCode; + [FieldOffset(8), MarshalAs(UnmanagedType.U2)] + public ushort wVirtualScanCode; + [FieldOffset(10)] + public char UnicodeChar; + [FieldOffset(12), MarshalAs(UnmanagedType.U4)] + public ControlKeyState dwControlKeyState; + } + + [Flags] + public enum ButtonState + { + Button1Pressed = 1, + Button2Pressed = 4, + Button3Pressed = 8, + Button4Pressed = 16, + RightmostButtonPressed = 2 + } + + [Flags] + public enum ControlKeyState + { + RightAltPressed = 1, + LeftAltPressed = 2, + RightControlPressed = 4, + LeftControlPressed = 8, + ShiftPressed = 16, + NumlockOn = 32, + ScrolllockOn = 64, + CapslockOn = 128, + EnhancedKey = 256 + } + + [Flags] + public enum EventFlags + { + MouseMoved = 1, + DoubleClick = 2, + MouseWheeled = 4, + MouseHorizontalWheeled = 8 + } + + [StructLayout(LayoutKind.Explicit)] + public struct MouseEventRecord + { + [FieldOffset(0)] + public Coord MousePosition; + [FieldOffset(4)] + public ButtonState ButtonState; + [FieldOffset(8)] + public ControlKeyState ControlKeyState; + [FieldOffset(12)] + public EventFlags EventFlags; + + public override readonly string ToString() => $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}"; + + } + + public struct WindowBufferSizeRecord + { + public Coord _size; + + public WindowBufferSizeRecord(short x, short y) + { + _size = new Coord(x, y); + } + + public override readonly string ToString() => $"[WindowBufferSize{_size}"; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MenuEventRecord + { + public uint dwCommandId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FocusEventRecord + { + public uint bSetFocus; + } + + public enum EventType : ushort + { + Focus = 0x10, + Key = 0x1, + Menu = 0x8, + Mouse = 2, + WindowBufferSize = 4 + } + + [StructLayout(LayoutKind.Explicit)] + public struct InputRecord + { + [FieldOffset(0)] + public EventType EventType; + [FieldOffset(4)] + public KeyEventRecord KeyEvent; + [FieldOffset(4)] + public MouseEventRecord MouseEvent; + [FieldOffset(4)] + public WindowBufferSizeRecord WindowBufferSizeEvent; + [FieldOffset(4)] + public MenuEventRecord MenuEvent; + [FieldOffset(4)] + public FocusEventRecord FocusEvent; + + public override readonly string ToString() + { + return EventType switch + { + EventType.Focus => FocusEvent.ToString(), + EventType.Key => KeyEvent.ToString(), + EventType.Menu => MenuEvent.ToString(), + EventType.Mouse => MouseEvent.ToString(), + EventType.WindowBufferSize => WindowBufferSizeEvent.ToString(), + _ => "Unknown event type: " + EventType + }; + } + }; + + [Flags] + enum ShareMode : uint + { + FileShareRead = 1, + FileShareWrite = 2, + } + + [Flags] + enum DesiredAccess : uint + { + GenericRead = 2147483648, + GenericWrite = 1073741824, + } + + [StructLayout(LayoutKind.Sequential)] + public struct ConsoleScreenBufferInfo + { + public Coord dwSize; + public Coord dwCursorPosition; + public ushort wAttributes; + public SmallRect srWindow; + public Coord dwMaximumWindowSize; + } + + [StructLayout(LayoutKind.Sequential)] + public struct Coord + { + public short X; + public short Y; + + public Coord(short x, short y) + { + X = x; + Y = y; + } + public override readonly string ToString() => $"({X},{Y})"; + }; + + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] + public struct CharUnion + { + [FieldOffset(0)] public char UnicodeChar; + [FieldOffset(0)] public byte AsciiChar; + } + + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] + public struct CharInfo + { + [FieldOffset(0)] public CharUnion Char; + [FieldOffset(2)] public ushort Attributes; + } + + public struct ExtendedCharInfo + { + public char Char { get; set; } + public Attribute Attribute { get; set; } + public bool Empty { get; set; } // TODO: Temp hack until virutal terminal sequences + + public ExtendedCharInfo(char character, Attribute attribute) + { + Char = character; + Attribute = attribute; + Empty = false; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct SmallRect + { + public short Left; + public short Top; + public short Right; + public short Bottom; + + public SmallRect(short left, short top, short right, short bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public static void MakeEmpty(ref SmallRect rect) + { + rect.Left = -1; + } + + public static void Update(ref SmallRect rect, short col, short row) + { + if (rect.Left == -1) + { + rect.Left = rect.Right = col; + rect.Bottom = rect.Top = row; + return; + } + if (col >= rect.Left && col <= rect.Right && row >= rect.Top && row <= rect.Bottom) + return; + if (col < rect.Left) + rect.Left = col; + if (col > rect.Right) + rect.Right = col; + if (row < rect.Top) + rect.Top = row; + if (row > rect.Bottom) + rect.Bottom = row; + } + + public override readonly string ToString() => $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}"; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ConsoleKeyInfoEx + { + public ConsoleKeyInfo ConsoleKeyInfo; + public bool CapsLock; + public bool NumLock; + public bool ScrollLock; + + public ConsoleKeyInfoEx(ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock) + { + ConsoleKeyInfo = consoleKeyInfo; + CapsLock = capslock; + NumLock = numlock; + ScrollLock = scrolllock; + } + } + + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr GetStdHandle(int nStdHandle); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool CloseHandle(IntPtr handle); + + [DllImport("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)] + public static extern bool ReadConsoleInput( + IntPtr hConsoleInput, + IntPtr lpBuffer, + uint nLength, + out uint lpNumberOfEventsRead); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool ReadConsoleOutput( + IntPtr hConsoleOutput, + [Out] CharInfo[] lpBuffer, + Coord dwBufferSize, + Coord dwBufferCoord, + ref SmallRect lpReadRegion + ); + + // TODO: This API is obsolete. See https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput + [DllImport("kernel32.dll", EntryPoint = "WriteConsoleOutputW", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool WriteConsoleOutput( + IntPtr hConsoleOutput, + CharInfo[] lpBuffer, + Coord dwBufferSize, + Coord dwBufferCoord, + ref SmallRect lpWriteRegion + ); + + [DllImport("kernel32.dll", EntryPoint = "WriteConsole", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool WriteConsole( + IntPtr hConsoleOutput, + String lpbufer, + UInt32 NumberOfCharsToWriten, + out UInt32 lpNumberOfCharsWritten, + object lpReserved + ); + + [DllImport("kernel32.dll")] + static extern bool SetConsoleCursorPosition(IntPtr hConsoleOutput, Coord dwCursorPosition); + + [StructLayout(LayoutKind.Sequential)] + public struct ConsoleCursorInfo + { + public uint dwSize; + public bool bVisible; + } + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleCursorInfo(IntPtr hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool GetConsoleCursorInfo(IntPtr hConsoleOutput, out ConsoleCursorInfo lpConsoleCursorInfo); + + [DllImport("kernel32.dll")] + static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); + + [DllImport("kernel32.dll")] + static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr CreateConsoleScreenBuffer( + DesiredAccess dwDesiredAccess, + ShareMode dwShareMode, + IntPtr secutiryAttributes, + uint flags, + IntPtr screenBufferData + ); + + internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleActiveScreenBuffer(IntPtr Handle); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool GetNumberOfConsoleInputEvents(IntPtr handle, out uint lpcNumberOfEvents); + + public InputRecord[] ReadConsoleInput() + { + const int bufferSize = 1; + var pRecord = Marshal.AllocHGlobal(Marshal.SizeOf() * bufferSize); + try + { + ReadConsoleInput(_inputHandle, pRecord, bufferSize, + out var numberEventsRead); + + return numberEventsRead == 0 + ? null + : new[] { Marshal.PtrToStructure(pRecord) }; + } + catch (Exception) + { + return null; + } + finally + { + Marshal.FreeHGlobal(pRecord); + } + } #if false // Not needed on the constructor. Perhaps could be used on resizing. To study. [DllImport ("kernel32.dll", ExactSpelling = true)] @@ -696,994 +772,1116 @@ internal void ShowWindow (int state) ShowWindow (thisConsole, state); } #endif - // See: https://github.com/gui-cs/Terminal.Gui/issues/357 - - [StructLayout (LayoutKind.Sequential)] - public struct CONSOLE_SCREEN_BUFFER_INFOEX { - public uint cbSize; - public Coord dwSize; - public Coord dwCursorPosition; - public ushort wAttributes; - public SmallRect srWindow; - public Coord dwMaximumWindowSize; - public ushort wPopupAttributes; - public bool bFullscreenSupported; - - [MarshalAs (UnmanagedType.ByValArray, SizeConst = 16)] - public COLORREF [] ColorTable; - } - - [StructLayout (LayoutKind.Explicit, Size = 4)] - public struct COLORREF { - public COLORREF (byte r, byte g, byte b) - { - Value = 0; - R = r; - G = g; - B = b; - } - - public COLORREF (uint value) - { - R = 0; - G = 0; - B = 0; - Value = value & 0x00FFFFFF; - } - - [FieldOffset (0)] - public byte R; - [FieldOffset (1)] - public byte G; - [FieldOffset (2)] - public byte B; - - [FieldOffset (0)] - public uint Value; - } - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool GetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX ConsoleScreenBufferInfo); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleWindowInfo ( - IntPtr hConsoleOutput, - bool bAbsolute, - [In] ref SmallRect lpConsoleWindow); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern Coord GetLargestConsoleWindowSize ( - IntPtr hConsoleOutput); + // See: https://github.com/gui-cs/Terminal.Gui/issues/357 + + [StructLayout(LayoutKind.Sequential)] + public struct CONSOLE_SCREEN_BUFFER_INFOEX + { + public uint cbSize; + public Coord dwSize; + public Coord dwCursorPosition; + public ushort wAttributes; + public SmallRect srWindow; + public Coord dwMaximumWindowSize; + public ushort wPopupAttributes; + public bool bFullscreenSupported; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public COLORREF[] ColorTable; + } + + [StructLayout(LayoutKind.Explicit, Size = 4)] + public struct COLORREF + { + public COLORREF(byte r, byte g, byte b) + { + Value = 0; + R = r; + G = g; + B = b; + } + + public COLORREF(uint value) + { + R = 0; + G = 0; + B = 0; + Value = value & 0x00FFFFFF; + } + + [FieldOffset(0)] + public byte R; + [FieldOffset(1)] + public byte G; + [FieldOffset(2)] + public byte B; + + [FieldOffset(0)] + public uint Value; + } + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool GetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX ConsoleScreenBufferInfo); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleWindowInfo( + IntPtr hConsoleOutput, + bool bAbsolute, + [In] ref SmallRect lpConsoleWindow); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern Coord GetLargestConsoleWindowSize( + IntPtr hConsoleOutput); } -internal class WindowsDriver : ConsoleDriver { - WindowsConsole.ExtendedCharInfo [] _outputBuffer; - WindowsConsole.SmallRect _damageRegion; - Action _keyHandler; - Action _keyDownHandler; - Action _keyUpHandler; - Action _mouseHandler; - - public WindowsConsole WinConsole { get; private set; } - - public override bool SupportsTrueColor => Environment.OSVersion.Version.Build >= 14931; - - public WindowsDriver () - { - WinConsole = new WindowsConsole (); - Clipboard = new WindowsClipboard (); - } - - public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) - { - _keyHandler = keyHandler; - _keyDownHandler = keyDownHandler; - _keyUpHandler = keyUpHandler; - _mouseHandler = mouseHandler; - - var mLoop = mainLoop.MainLoopDriver as WindowsMainLoop; - - mLoop.ProcessInput = (e) => ProcessInput (e); - - mLoop.WinChanged = (s, e) => { - ChangeWin (e.Size); - }; - } - - private void ChangeWin (Size e) - { - if (!EnableConsoleScrolling) { - var w = e.Width; - if (w == Cols - 3 && e.Height < Rows) { - w += 3; - } - var newSize = WinConsole.SetConsoleWindow ( - (short)Math.Max (w, 16), (short)Math.Max (e.Height, 0)); - Left = 0; - Top = 0; - Cols = newSize.Width; - Rows = newSize.Height; - ResizeScreen (); - ClearContents (); - TerminalResized.Invoke (); - } - } - - void ProcessInput (WindowsConsole.InputRecord inputEvent) - { - switch (inputEvent.EventType) { - case WindowsConsole.EventType.Key: - var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet; - if (fromPacketKey) { - inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent); - } - var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent)); - //var ke = inputEvent.KeyEvent; - //System.Diagnostics.Debug.WriteLine ($"fromPacketKey: {fromPacketKey}"); - //if (ke.UnicodeChar == '\0') { - // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 0'\\0'"); - //} else if (ke.UnicodeChar == 13) { - // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 13'\\n'"); - //} else { - // System.Diagnostics.Debug.WriteLine ($"UnicodeChar: {(uint)ke.UnicodeChar}'{ke.UnicodeChar}'"); - //} - //System.Diagnostics.Debug.WriteLine ($"bKeyDown: {ke.bKeyDown}"); - //System.Diagnostics.Debug.WriteLine ($"dwControlKeyState: {ke.dwControlKeyState}"); - //System.Diagnostics.Debug.WriteLine ($"wRepeatCount: {ke.wRepeatCount}"); - //System.Diagnostics.Debug.WriteLine ($"wVirtualKeyCode: {ke.wVirtualKeyCode}"); - //System.Diagnostics.Debug.WriteLine ($"wVirtualScanCode: {ke.wVirtualScanCode}"); - - if (map == (Key)0xffffffff) { - KeyEvent key = new KeyEvent (); - - // Shift = VK_SHIFT = 0x10 - // Ctrl = VK_CONTROL = 0x11 - // Alt = VK_MENU = 0x12 - - if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.CapslockOn)) { - inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn; - } - - if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ScrolllockOn)) { - inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn; - } - - if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.NumlockOn)) { - inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn; - } - - switch (inputEvent.KeyEvent.dwControlKeyState) { - case WindowsConsole.ControlKeyState.RightAltPressed: - case WindowsConsole.ControlKeyState.RightAltPressed | - WindowsConsole.ControlKeyState.LeftControlPressed | - WindowsConsole.ControlKeyState.EnhancedKey: - case WindowsConsole.ControlKeyState.EnhancedKey: - key = new KeyEvent (Key.CtrlMask | Key.AltMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.LeftAltPressed: - key = new KeyEvent (Key.AltMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.RightControlPressed: - case WindowsConsole.ControlKeyState.LeftControlPressed: - key = new KeyEvent (Key.CtrlMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.ShiftPressed: - key = new KeyEvent (Key.ShiftMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.NumlockOn: - break; - case WindowsConsole.ControlKeyState.ScrolllockOn: - break; - case WindowsConsole.ControlKeyState.CapslockOn: - break; - default: - key = inputEvent.KeyEvent.wVirtualKeyCode switch { - 0x10 => new KeyEvent (Key.ShiftMask, _keyModifiers), - 0x11 => new KeyEvent (Key.CtrlMask, _keyModifiers), - 0x12 => new KeyEvent (Key.AltMask, _keyModifiers), - _ => new KeyEvent (Key.Unknown, _keyModifiers) - }; - break; - } - - if (inputEvent.KeyEvent.bKeyDown) { - _keyDownHandler (key); - } else { - _keyUpHandler (key); - } - } else { - if (inputEvent.KeyEvent.bKeyDown) { - // May occurs using SendKeys - _keyModifiers ??= new KeyModifiers (); - // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event - _keyDownHandler (new KeyEvent (map, _keyModifiers)); - _keyHandler (new KeyEvent (map, _keyModifiers)); - } else { - _keyUpHandler (new KeyEvent (map, _keyModifiers)); - } - } - if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) { - _keyModifiers = null; - } - break; - - case WindowsConsole.EventType.Mouse: - var me = ToDriverMouse (inputEvent.MouseEvent); - _mouseHandler (me); - if (_processButtonClick) { - _mouseHandler ( - new MouseEvent () { - X = me.X, - Y = me.Y, - Flags = ProcessButtonClick (inputEvent.MouseEvent) - }); - } - break; - - case WindowsConsole.EventType.WindowBufferSize: - var winSize = WinConsole.GetConsoleBufferWindow (out Point pos); - Left = pos.X; - Top = pos.Y; - Cols = inputEvent.WindowBufferSizeEvent._size.X; - if (EnableConsoleScrolling) { - Rows = Math.Max (inputEvent.WindowBufferSizeEvent._size.Y, Rows); - } else { - Rows = inputEvent.WindowBufferSizeEvent._size.Y; - } - //System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}"); - ResizeScreen (); - ClearContents (); - TerminalResized?.Invoke (); - break; - - case WindowsConsole.EventType.Focus: - break; - } - } - - WindowsConsole.ButtonState? _lastMouseButtonPressed = null; - bool _isButtonPressed = false; - bool _isButtonReleased = false; - bool _isButtonDoubleClicked = false; - Point? _point; - Point _pointMove; - //int _buttonPressedCount; - bool _isOneFingerDoubleClicked = false; - bool _processButtonClick; - - MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) - { - MouseFlags mouseFlag = MouseFlags.AllEvents; - - //System.Diagnostics.Debug.WriteLine ( - // $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}"); - - if (_isButtonDoubleClicked || _isOneFingerDoubleClicked) { - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); - return false; - }); - } - - // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button. - // This will tell when a mouse button is pressed. When the button is released this event will - // be fired with it's bit set to 0. So when the button is up ButtonState will be 0. - // To map to the correct driver events we save the last pressed mouse button so we can - // map to the correct clicked event. - if ((_lastMouseButtonPressed != null || _isButtonReleased) && mouseEvent.ButtonState != 0) { - _lastMouseButtonPressed = null; - //isButtonPressed = false; - _isButtonReleased = false; - } - - var p = new Point () { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y - }; - - if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && _lastMouseButtonPressed == null && !_isButtonDoubleClicked) || - (_lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.MouseMoved) && - mouseEvent.ButtonState != 0 && !_isButtonReleased && !_isButtonDoubleClicked)) { - switch (mouseEvent.ButtonState) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Pressed; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Pressed; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Pressed; - break; - } - - if (_point == null) { - _point = p; - } - - if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { - mouseFlag |= MouseFlags.ReportMousePosition; - _isButtonReleased = false; - _processButtonClick = false; - } - _lastMouseButtonPressed = mouseEvent.ButtonState; - _isButtonPressed = true; - - if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag)); - return false; - }); - } - - } else if (_lastMouseButtonPressed != null && mouseEvent.EventFlags == 0 - && !_isButtonReleased && !_isButtonDoubleClicked && !_isOneFingerDoubleClicked) { - switch (_lastMouseButtonPressed) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Released; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Released; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Released; - break; - } - _isButtonPressed = false; - _isButtonReleased = true; - if (_point != null && (((Point)_point).X == mouseEvent.MousePosition.X && ((Point)_point).Y == mouseEvent.MousePosition.Y)) { - _processButtonClick = true; - } else { - _point = null; - } - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved - && !_isOneFingerDoubleClicked && _isButtonReleased && p == _point) { - - mouseFlag = ProcessButtonClick (mouseEvent); - - } else if (mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.DoubleClick)) { - switch (mouseEvent.ButtonState) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1DoubleClicked; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2DoubleClicked; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3DoubleClicked; - break; - } - _isButtonDoubleClicked = true; - } else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && _isButtonDoubleClicked) { - switch (mouseEvent.ButtonState) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1TripleClicked; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2TripleClicked; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3TripleClicked; - break; - } - _isButtonDoubleClicked = false; - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) { - switch ((int)mouseEvent.ButtonState) { - case int v when v > 0: - mouseFlag = MouseFlags.WheeledUp; - break; - - case int v when v < 0: - mouseFlag = MouseFlags.WheeledDown; - break; - } - - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled && - mouseEvent.ControlKeyState == WindowsConsole.ControlKeyState.ShiftPressed) { - switch ((int)mouseEvent.ButtonState) { - case int v when v > 0: - mouseFlag = MouseFlags.WheeledLeft; - break; - - case int v when v < 0: - mouseFlag = MouseFlags.WheeledRight; - break; - } - - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseHorizontalWheeled) { - switch ((int)mouseEvent.ButtonState) { - case int v when v < 0: - mouseFlag = MouseFlags.WheeledLeft; - break; - - case int v when v > 0: - mouseFlag = MouseFlags.WheeledRight; - break; - } - - } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { - mouseFlag = MouseFlags.ReportMousePosition; - if (mouseEvent.MousePosition.X != _pointMove.X || mouseEvent.MousePosition.Y != _pointMove.Y) { - _pointMove = new Point (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y); - } - } else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) { - mouseFlag = 0; - } - - mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag); - - //System.Diagnostics.Debug.WriteLine ( - // $"point.X:{(point != null ? ((Point)point).X : -1)};point.Y:{(point != null ? ((Point)point).Y : -1)}"); - - return new MouseEvent () { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y, - Flags = mouseFlag - }; - } - - MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent) - { - MouseFlags mouseFlag = 0; - switch (_lastMouseButtonPressed) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Clicked; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Clicked; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Clicked; - break; - } - _point = new Point () { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y - }; - _lastMouseButtonPressed = null; - _isButtonReleased = false; - _processButtonClick = false; - _point = null; - return mouseFlag; - } - - async Task ProcessButtonDoubleClickedAsync () - { - await Task.Delay (300); - _isButtonDoubleClicked = false; - _isOneFingerDoubleClicked = false; - //buttonPressedCount = 0; - } - - async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag) - { - while (_isButtonPressed) { - await Task.Delay (100); - var me = new MouseEvent () { - X = _pointMove.X, - Y = _pointMove.Y, - Flags = mouseFlag - }; - - var view = Application.WantContinuousButtonPressedView; - if (view == null) { - break; - } - if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.Invoke (() => _mouseHandler (me)); - } - } - } - - static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag) - { - if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed) || - mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed)) { - mouseFlag |= MouseFlags.ButtonCtrl; - } - - if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) { - mouseFlag |= MouseFlags.ButtonShift; - } - - if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) || - mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) { - mouseFlag |= MouseFlags.ButtonAlt; - } - return mouseFlag; - } - - KeyModifiers _keyModifiers; - - public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent) - { - var state = keyEvent.dwControlKeyState; - - var shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0; - var alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0; - var control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0; - var capsLock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0; - var numLock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0; - var scrollLock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0; - - _keyModifiers ??= new KeyModifiers (); - if (shift) { - _keyModifiers.Shift = true; - } - if (alt) { - _keyModifiers.Alt = true; - } - if (control) { - _keyModifiers.Ctrl = true; - } - if (capsLock) { - _keyModifiers.Capslock = true; - } - if (numLock) { - _keyModifiers.Numlock = true; - } - if (scrollLock) { - _keyModifiers.Scrolllock = true; - } - - var consoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); - - return new WindowsConsole.ConsoleKeyInfoEx (consoleKeyInfo, capsLock, numLock, scrollLock); - } - - public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent) - { - if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) { - return keyEvent; - } - - var mod = new ConsoleModifiers (); - if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) { - mod |= ConsoleModifiers.Shift; - } - if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) || - keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) { - mod |= ConsoleModifiers.Alt; - } - if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed) || - keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed)) { - mod |= ConsoleModifiers.Control; - } - var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyEvent.UnicodeChar, mod, out uint virtualKey, out uint scanCode); - - return new WindowsConsole.KeyEventRecord { - UnicodeChar = (char)keyChar, - bKeyDown = keyEvent.bKeyDown, - dwControlKeyState = keyEvent.dwControlKeyState, - wRepeatCount = keyEvent.wRepeatCount, - wVirtualKeyCode = (ushort)virtualKey, - wVirtualScanCode = (ushort)scanCode - }; - } - - public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx) - { - var keyInfo = keyInfoEx.ConsoleKeyInfo; - switch (keyInfo.Key) { - case ConsoleKey.Escape: - return MapKeyModifiers (keyInfo, Key.Esc); - case ConsoleKey.Tab: - return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; - case ConsoleKey.Clear: - return MapKeyModifiers (keyInfo, Key.Clear); - case ConsoleKey.Home: - return MapKeyModifiers (keyInfo, Key.Home); - case ConsoleKey.End: - return MapKeyModifiers (keyInfo, Key.End); - case ConsoleKey.LeftArrow: - return MapKeyModifiers (keyInfo, Key.CursorLeft); - case ConsoleKey.RightArrow: - return MapKeyModifiers (keyInfo, Key.CursorRight); - case ConsoleKey.UpArrow: - return MapKeyModifiers (keyInfo, Key.CursorUp); - case ConsoleKey.DownArrow: - return MapKeyModifiers (keyInfo, Key.CursorDown); - case ConsoleKey.PageUp: - return MapKeyModifiers (keyInfo, Key.PageUp); - case ConsoleKey.PageDown: - return MapKeyModifiers (keyInfo, Key.PageDown); - case ConsoleKey.Enter: - return MapKeyModifiers (keyInfo, Key.Enter); - case ConsoleKey.Spacebar: - return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); - case ConsoleKey.Backspace: - return MapKeyModifiers (keyInfo, Key.Backspace); - case ConsoleKey.Delete: - return MapKeyModifiers (keyInfo, Key.DeleteChar); - case ConsoleKey.Insert: - return MapKeyModifiers (keyInfo, Key.InsertChar); - case ConsoleKey.PrintScreen: - return MapKeyModifiers (keyInfo, Key.PrintScreen); - - case ConsoleKey.NumPad0: - return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar; - case ConsoleKey.NumPad1: - return keyInfoEx.NumLock ? Key.D1 : Key.End; - case ConsoleKey.NumPad2: - return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown; - case ConsoleKey.NumPad3: - return keyInfoEx.NumLock ? Key.D3 : Key.PageDown; - case ConsoleKey.NumPad4: - return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft; - case ConsoleKey.NumPad5: - return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar); - case ConsoleKey.NumPad6: - return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight; - case ConsoleKey.NumPad7: - return keyInfoEx.NumLock ? Key.D7 : Key.Home; - case ConsoleKey.NumPad8: - return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp; - case ConsoleKey.NumPad9: - return keyInfoEx.NumLock ? Key.D9 : Key.PageUp; - - case ConsoleKey.Oem1: - case ConsoleKey.Oem2: - case ConsoleKey.Oem3: - case ConsoleKey.Oem4: - case ConsoleKey.Oem5: - case ConsoleKey.Oem6: - case ConsoleKey.Oem7: - case ConsoleKey.Oem8: - case ConsoleKey.Oem102: - case ConsoleKey.OemPeriod: - case ConsoleKey.OemComma: - case ConsoleKey.OemPlus: - case ConsoleKey.OemMinus: - if (keyInfo.KeyChar == 0) { - return Key.Unknown; - } - - return (Key)((uint)keyInfo.KeyChar); - } - - var key = keyInfo.Key; - //var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a'; - - if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { - var delta = key - ConsoleKey.A; - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); - } - } - //return (Key)((uint)alphaBase + delta); - return (Key)((uint)keyInfo.KeyChar); - } - if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { - var delta = key - ConsoleKey.D0; - if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); - } - } - return (Key)((uint)keyInfo.KeyChar); - } - if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { - var delta = key - ConsoleKey.F1; - if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); - } - - return (Key)((uint)Key.F1 + delta); - } - if (keyInfo.KeyChar != 0) { - return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); - } - - return (Key)(0xffffffff); - } - - private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) - { - Key keyMod = new Key (); - if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { - keyMod = Key.ShiftMask; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { - keyMod |= Key.CtrlMask; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { - keyMod |= Key.AltMask; - } - - return keyMod != Key.Null ? keyMod | key : key; - } - - int GetOutputBufferPosition () - { - return Row * Cols + Col; - } - - public override bool IsRuneSupported (Rune rune) - { - return base.IsRuneSupported (rune) && rune.IsBmp; - } - - public override void Init (Action terminalResized) - { - TerminalResized = terminalResized; - - try { - // Needed for Windows Terminal - // Per Issue #2264 using the alternative screen buffer is required for Windows Terminal to not - // wipe out the backscroll buffer when the application exits. - Console.Out.Write (EscSeqUtils.CSI_ActivateAltBufferNoBackscroll); - - var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); - Cols = winSize.Width; - Rows = winSize.Height; - WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - - } catch (Win32Exception) { - // Likely running unit tests. Set WinConsole to null so we can test it elsewhere. - WinConsole = null; - //throw new InvalidOperationException ("The Windows Console output window is not available.", e); - } - - CurrentAttribute = MakeColor (Color.White, Color.Black); - InitializeColorSchemes (); - - ResizeScreen (); - ClearContents (); - } - - public virtual void ResizeScreen () - { - if (WinConsole == null) { - return; - } - - _outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols]; - Clip = new Rect (0, 0, Cols, Rows); - _damageRegion = new WindowsConsole.SmallRect () { - Top = 0, - Left = 0, - Bottom = (short)Rows, - Right = (short)Cols - }; - _dirtyLines = new bool [Rows]; - - WinConsole.ForceRefreshCursorVisibility (); - if (!EnableConsoleScrolling) { - Console.Out.Write (EscSeqUtils.CSI_ClearScreen (EscSeqUtils.ClearScreenOptions.CursorToEndOfScreen)); - } - } - - - public override void UpdateScreen () - { - if (!EnableConsoleScrolling) { - var windowSize = WinConsole.GetConsoleBufferWindow (out _); - if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) { - return; - } - } - - var bufferCoords = new WindowsConsole.Coord () { - X = (short)Clip.Width, - Y = (short)Clip.Height - }; - - for (int row = 0; row < Rows; row++) { - if (!_dirtyLines [row]) { - continue; - } - _dirtyLines [row] = false; - - for (int col = 0; col < Cols; col++) { - int position = row * Cols + col; - _outputBuffer [position].Attribute = Contents [row, col].Attribute.GetValueOrDefault (); - if (Contents [row, col].IsDirty == false) { - _outputBuffer [position].Empty = true; - _outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; - continue; - } - _outputBuffer [position].Empty = false; - if (Contents [row, col].Runes [0].IsBmp) { - _outputBuffer [position].Char = (char)Contents [row, col].Runes [0].Value; - } else { - //_outputBuffer [position].Empty = true; - _outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; - if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { - // TODO: This is a hack to deal with non-BMP and wide characters. - col++; - position = row * Cols + col; - _outputBuffer [position].Empty = false; - _outputBuffer [position].Char = ' '; - } - } - } - } - - WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion, UseTrueColor); - WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - } - - public override void Refresh () - { - UpdateScreen (); - WinConsole.SetInitialCursorVisibility (); - UpdateCursor (); - } - - #region Color Handling - - /// - /// In the WindowsDriver, colors are encoded as an int. - /// The background color is stored in the least significant 4 bits, - /// and the foreground color is stored in the next 4 bits. - /// - public override Attribute MakeColor (Color foreground, Color background) - { - // Encode the colors into the int value. - return new Attribute ( - value: (((int)foreground) | ((int)background << 4)), - foreground: foreground, - background: background - ); - } - - /// - /// Extracts the foreground and background colors from the encoded value. - /// Assumes a 4-bit encoded value for both foreground and background colors. - /// - internal override void GetColors (int value, out Color foreground, out Color background) - { - // Assume a 4-bit encoded value for both foreground and background colors. - foreground = (Color)((value >> 16) & 0xF); - background = (Color)(value & 0xF); - } - - #endregion - - CursorVisibility savedCursorVisibility; - - public override void UpdateCursor () - { - if (Col < 0 || Row < 0 || Col > Cols || Row > Rows) { - GetCursorVisibility (out CursorVisibility cursorVisibility); - savedCursorVisibility = cursorVisibility; - SetCursorVisibility (CursorVisibility.Invisible); - return; - } - - SetCursorVisibility (savedCursorVisibility); - var position = new WindowsConsole.Coord () { - X = (short)Col, - Y = (short)Row - }; - WinConsole.SetCursorPosition (position); - } - - /// - public override bool GetCursorVisibility (out CursorVisibility visibility) - { - return WinConsole.GetCursorVisibility (out visibility); - } - - /// - public override bool SetCursorVisibility (CursorVisibility visibility) - { - savedCursorVisibility = visibility; - return WinConsole.SetCursorVisibility (visibility); - } - - /// - public override bool EnsureCursorVisibility () - { - return WinConsole.EnsureCursorVisibility (); - } - - public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) - { - WindowsConsole.InputRecord input = new WindowsConsole.InputRecord { - EventType = WindowsConsole.EventType.Key - }; - - WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord { - bKeyDown = true - }; - WindowsConsole.ControlKeyState controlKey = new WindowsConsole.ControlKeyState (); - if (shift) { - controlKey |= WindowsConsole.ControlKeyState.ShiftPressed; - keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = 16; - } - if (alt) { - controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed; - controlKey |= WindowsConsole.ControlKeyState.RightAltPressed; - keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = 18; - } - if (control) { - controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed; - controlKey |= WindowsConsole.ControlKeyState.RightControlPressed; - keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = 17; - } - keyEvent.dwControlKeyState = controlKey; - - input.KeyEvent = keyEvent; - - if (shift || alt || control) { - ProcessInput (input); - } - - keyEvent.UnicodeChar = keyChar; - if ((uint)key < 255) { - keyEvent.wVirtualKeyCode = (ushort)key; - } else { - keyEvent.wVirtualKeyCode = '\0'; - } - - input.KeyEvent = keyEvent; - - try { - ProcessInput (input); - } catch (OverflowException) { } finally { - keyEvent.bKeyDown = false; - input.KeyEvent = keyEvent; - ProcessInput (input); - } - } - - public override void End () - { - WinConsole?.Cleanup (); - WinConsole = null; - - // Needed for Windows Terminal - // Clear the alternative screen buffer from the cursor to the - // end of the screen. - // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE - // backbuffer! So we need to use [0J instead. - Console.Out.Write (EscSeqUtils.CSI_ClearScreen (0)); - - // Disable alternative screen buffer. - Console.Out.Write (EscSeqUtils.CSI_RestoreAltBufferWithBackscroll); - } - - #region Not Implemented - public override void Suspend () - { - throw new NotImplementedException (); - } - #endregion +internal class WindowsDriver : ConsoleDriver +{ + WindowsConsole.ExtendedCharInfo[] _outputBuffer; + WindowsConsole.SmallRect _damageRegion; + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; + + public WindowsConsole WinConsole { get; private set; } + + public override bool SupportsTrueColor => Environment.OSVersion.Version.Build >= 14931; + + public WindowsDriver() + { + WinConsole = new WindowsConsole(); + Clipboard = new WindowsClipboard(); + } + + public override void PrepareToRun(MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) + { + _keyHandler = keyHandler; + _keyDownHandler = keyDownHandler; + _keyUpHandler = keyUpHandler; + _mouseHandler = mouseHandler; + + var mLoop = mainLoop.MainLoopDriver as WindowsMainLoop; + + mLoop.ProcessInput = (e) => ProcessInput(e); + + mLoop.WinChanged = (s, e) => + { + ChangeWin(e.Size); + }; + } + + private void ChangeWin(Size e) + { + var w = e.Width; + if (w == Cols - 3 && e.Height < Rows) + { + w += 3; + } + var newSize = WinConsole.SetConsoleWindow( + (short)Math.Max(w, 16), (short)Math.Max(e.Height, 0)); + Left = 0; + Top = 0; + Cols = newSize.Width; + Rows = newSize.Height; + ResizeScreen(); + ClearContents(); + TerminalResized.Invoke(); + } + + void ProcessInput(WindowsConsole.InputRecord inputEvent) + { + switch (inputEvent.EventType) + { + case WindowsConsole.EventType.Key: + var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet; + if (fromPacketKey) + { + inputEvent.KeyEvent = FromVKPacketToKeyEventRecord(inputEvent.KeyEvent); + } + var map = MapKey(ToConsoleKeyInfoEx(inputEvent.KeyEvent)); + //var ke = inputEvent.KeyEvent; + //System.Diagnostics.Debug.WriteLine ($"fromPacketKey: {fromPacketKey}"); + //if (ke.UnicodeChar == '\0') { + // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 0'\\0'"); + //} else if (ke.UnicodeChar == 13) { + // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 13'\\n'"); + //} else { + // System.Diagnostics.Debug.WriteLine ($"UnicodeChar: {(uint)ke.UnicodeChar}'{ke.UnicodeChar}'"); + //} + //System.Diagnostics.Debug.WriteLine ($"bKeyDown: {ke.bKeyDown}"); + //System.Diagnostics.Debug.WriteLine ($"dwControlKeyState: {ke.dwControlKeyState}"); + //System.Diagnostics.Debug.WriteLine ($"wRepeatCount: {ke.wRepeatCount}"); + //System.Diagnostics.Debug.WriteLine ($"wVirtualKeyCode: {ke.wVirtualKeyCode}"); + //System.Diagnostics.Debug.WriteLine ($"wVirtualScanCode: {ke.wVirtualScanCode}"); + + if (map == (Key)0xffffffff) + { + KeyEvent key = new KeyEvent(); + + // Shift = VK_SHIFT = 0x10 + // Ctrl = VK_CONTROL = 0x11 + // Alt = VK_MENU = 0x12 + + if (inputEvent.KeyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.CapslockOn)) + { + inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn; + } + + if (inputEvent.KeyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.ScrolllockOn)) + { + inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn; + } + + if (inputEvent.KeyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.NumlockOn)) + { + inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn; + } + + switch (inputEvent.KeyEvent.dwControlKeyState) + { + case WindowsConsole.ControlKeyState.RightAltPressed: + case WindowsConsole.ControlKeyState.RightAltPressed | + WindowsConsole.ControlKeyState.LeftControlPressed | + WindowsConsole.ControlKeyState.EnhancedKey: + case WindowsConsole.ControlKeyState.EnhancedKey: + key = new KeyEvent(Key.CtrlMask | Key.AltMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.LeftAltPressed: + key = new KeyEvent(Key.AltMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.RightControlPressed: + case WindowsConsole.ControlKeyState.LeftControlPressed: + key = new KeyEvent(Key.CtrlMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.ShiftPressed: + key = new KeyEvent(Key.ShiftMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.NumlockOn: + break; + case WindowsConsole.ControlKeyState.ScrolllockOn: + break; + case WindowsConsole.ControlKeyState.CapslockOn: + break; + default: + key = inputEvent.KeyEvent.wVirtualKeyCode switch + { + 0x10 => new KeyEvent(Key.ShiftMask, _keyModifiers), + 0x11 => new KeyEvent(Key.CtrlMask, _keyModifiers), + 0x12 => new KeyEvent(Key.AltMask, _keyModifiers), + _ => new KeyEvent(Key.Unknown, _keyModifiers) + }; + break; + } + + if (inputEvent.KeyEvent.bKeyDown) + { + _keyDownHandler(key); + } + else + { + _keyUpHandler(key); + } + } + else + { + if (inputEvent.KeyEvent.bKeyDown) + { + // May occurs using SendKeys + _keyModifiers ??= new KeyModifiers(); + // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event + _keyDownHandler(new KeyEvent(map, _keyModifiers)); + _keyHandler(new KeyEvent(map, _keyModifiers)); + } + else + { + _keyUpHandler(new KeyEvent(map, _keyModifiers)); + } + } + if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) + { + _keyModifiers = null; + } + break; + + case WindowsConsole.EventType.Mouse: + var me = ToDriverMouse(inputEvent.MouseEvent); + _mouseHandler(me); + if (_processButtonClick) + { + _mouseHandler( + new MouseEvent() + { + X = me.X, + Y = me.Y, + Flags = ProcessButtonClick(inputEvent.MouseEvent) + }); + } + break; + + case WindowsConsole.EventType.WindowBufferSize: + var winSize = WinConsole.GetConsoleBufferWindow(out Point pos); + Left = pos.X; + Top = pos.Y; + Cols = inputEvent.WindowBufferSizeEvent._size.X; + Rows = inputEvent.WindowBufferSizeEvent._size.Y; + ResizeScreen(); + ClearContents(); + TerminalResized?.Invoke(); + break; + + case WindowsConsole.EventType.Focus: + break; + } + } + + WindowsConsole.ButtonState? _lastMouseButtonPressed = null; + bool _isButtonPressed = false; + bool _isButtonReleased = false; + bool _isButtonDoubleClicked = false; + Point? _point; + Point _pointMove; + //int _buttonPressedCount; + bool _isOneFingerDoubleClicked = false; + bool _processButtonClick; + + MouseEvent ToDriverMouse(WindowsConsole.MouseEventRecord mouseEvent) + { + MouseFlags mouseFlag = MouseFlags.AllEvents; + + //System.Diagnostics.Debug.WriteLine ( + // $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}"); + + if (_isButtonDoubleClicked || _isOneFingerDoubleClicked) + { + Application.MainLoop.AddIdle(() => + { + Task.Run(async () => await ProcessButtonDoubleClickedAsync()); + return false; + }); + } + + // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button. + // This will tell when a mouse button is pressed. When the button is released this event will + // be fired with it's bit set to 0. So when the button is up ButtonState will be 0. + // To map to the correct driver events we save the last pressed mouse button so we can + // map to the correct clicked event. + if ((_lastMouseButtonPressed != null || _isButtonReleased) && mouseEvent.ButtonState != 0) + { + _lastMouseButtonPressed = null; + //isButtonPressed = false; + _isButtonReleased = false; + } + + var p = new Point() + { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y + }; + + if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && _lastMouseButtonPressed == null && !_isButtonDoubleClicked) || + (_lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag(WindowsConsole.EventFlags.MouseMoved) && + mouseEvent.ButtonState != 0 && !_isButtonReleased && !_isButtonDoubleClicked)) + { + switch (mouseEvent.ButtonState) + { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Pressed; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Pressed; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Pressed; + break; + } + + if (_point == null) + { + _point = p; + } + + if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) + { + mouseFlag |= MouseFlags.ReportMousePosition; + _isButtonReleased = false; + _processButtonClick = false; + } + _lastMouseButtonPressed = mouseEvent.ButtonState; + _isButtonPressed = true; + + if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) + { + Application.MainLoop.AddIdle(() => + { + Task.Run(async () => await ProcessContinuousButtonPressedAsync(mouseFlag)); + return false; + }); + } + + } + else if (_lastMouseButtonPressed != null && mouseEvent.EventFlags == 0 + && !_isButtonReleased && !_isButtonDoubleClicked && !_isOneFingerDoubleClicked) + { + switch (_lastMouseButtonPressed) + { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Released; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Released; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Released; + break; + } + _isButtonPressed = false; + _isButtonReleased = true; + if (_point != null && (((Point)_point).X == mouseEvent.MousePosition.X && ((Point)_point).Y == mouseEvent.MousePosition.Y)) + { + _processButtonClick = true; + } + else + { + _point = null; + } + } + else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved + && !_isOneFingerDoubleClicked && _isButtonReleased && p == _point) + { + + mouseFlag = ProcessButtonClick(mouseEvent); + + } + else if (mouseEvent.EventFlags.HasFlag(WindowsConsole.EventFlags.DoubleClick)) + { + switch (mouseEvent.ButtonState) + { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1DoubleClicked; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2DoubleClicked; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3DoubleClicked; + break; + } + _isButtonDoubleClicked = true; + } + else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && _isButtonDoubleClicked) + { + switch (mouseEvent.ButtonState) + { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1TripleClicked; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2TripleClicked; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3TripleClicked; + break; + } + _isButtonDoubleClicked = false; + } + else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) + { + switch ((int)mouseEvent.ButtonState) + { + case int v when v > 0: + mouseFlag = MouseFlags.WheeledUp; + break; + + case int v when v < 0: + mouseFlag = MouseFlags.WheeledDown; + break; + } + + } + else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled && + mouseEvent.ControlKeyState == WindowsConsole.ControlKeyState.ShiftPressed) + { + switch ((int)mouseEvent.ButtonState) + { + case int v when v > 0: + mouseFlag = MouseFlags.WheeledLeft; + break; + + case int v when v < 0: + mouseFlag = MouseFlags.WheeledRight; + break; + } + + } + else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseHorizontalWheeled) + { + switch ((int)mouseEvent.ButtonState) + { + case int v when v < 0: + mouseFlag = MouseFlags.WheeledLeft; + break; + + case int v when v > 0: + mouseFlag = MouseFlags.WheeledRight; + break; + } + + } + else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) + { + mouseFlag = MouseFlags.ReportMousePosition; + if (mouseEvent.MousePosition.X != _pointMove.X || mouseEvent.MousePosition.Y != _pointMove.Y) + { + _pointMove = new Point(mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y); + } + } + else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) + { + mouseFlag = 0; + } + + mouseFlag = SetControlKeyStates(mouseEvent, mouseFlag); + + //System.Diagnostics.Debug.WriteLine ( + // $"point.X:{(point != null ? ((Point)point).X : -1)};point.Y:{(point != null ? ((Point)point).Y : -1)}"); + + return new MouseEvent() + { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y, + Flags = mouseFlag + }; + } + + MouseFlags ProcessButtonClick(WindowsConsole.MouseEventRecord mouseEvent) + { + MouseFlags mouseFlag = 0; + switch (_lastMouseButtonPressed) + { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Clicked; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Clicked; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Clicked; + break; + } + _point = new Point() + { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y + }; + _lastMouseButtonPressed = null; + _isButtonReleased = false; + _processButtonClick = false; + _point = null; + return mouseFlag; + } + + async Task ProcessButtonDoubleClickedAsync() + { + await Task.Delay(300); + _isButtonDoubleClicked = false; + _isOneFingerDoubleClicked = false; + //buttonPressedCount = 0; + } + + async Task ProcessContinuousButtonPressedAsync(MouseFlags mouseFlag) + { + while (_isButtonPressed) + { + await Task.Delay(100); + var me = new MouseEvent() + { + X = _pointMove.X, + Y = _pointMove.Y, + Flags = mouseFlag + }; + + var view = Application.WantContinuousButtonPressedView; + if (view == null) + { + break; + } + if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) + { + Application.MainLoop.Invoke(() => _mouseHandler(me)); + } + } + } + + static MouseFlags SetControlKeyStates(WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag) + { + if (mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.RightControlPressed) || + mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.LeftControlPressed)) + { + mouseFlag |= MouseFlags.ButtonCtrl; + } + + if (mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.ShiftPressed)) + { + mouseFlag |= MouseFlags.ButtonShift; + } + + if (mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.RightAltPressed) || + mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.LeftAltPressed)) + { + mouseFlag |= MouseFlags.ButtonAlt; + } + return mouseFlag; + } + + KeyModifiers _keyModifiers; + + public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx(WindowsConsole.KeyEventRecord keyEvent) + { + var state = keyEvent.dwControlKeyState; + + var shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0; + var alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0; + var control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0; + var capsLock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0; + var numLock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0; + var scrollLock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0; + + _keyModifiers ??= new KeyModifiers(); + if (shift) + { + _keyModifiers.Shift = true; + } + if (alt) + { + _keyModifiers.Alt = true; + } + if (control) + { + _keyModifiers.Ctrl = true; + } + if (capsLock) + { + _keyModifiers.Capslock = true; + } + if (numLock) + { + _keyModifiers.Numlock = true; + } + if (scrollLock) + { + _keyModifiers.Scrolllock = true; + } + + var consoleKeyInfo = new ConsoleKeyInfo(keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); + + return new WindowsConsole.ConsoleKeyInfoEx(consoleKeyInfo, capsLock, numLock, scrollLock); + } + + public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord(WindowsConsole.KeyEventRecord keyEvent) + { + if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) + { + return keyEvent; + } + + var mod = new ConsoleModifiers(); + if (keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.ShiftPressed)) + { + mod |= ConsoleModifiers.Shift; + } + if (keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.RightAltPressed) || + keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.LeftAltPressed)) + { + mod |= ConsoleModifiers.Alt; + } + if (keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.LeftControlPressed) || + keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.RightControlPressed)) + { + mod |= ConsoleModifiers.Control; + } + var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey(keyEvent.UnicodeChar, mod, out uint virtualKey, out uint scanCode); + + return new WindowsConsole.KeyEventRecord + { + UnicodeChar = (char)keyChar, + bKeyDown = keyEvent.bKeyDown, + dwControlKeyState = keyEvent.dwControlKeyState, + wRepeatCount = keyEvent.wRepeatCount, + wVirtualKeyCode = (ushort)virtualKey, + wVirtualScanCode = (ushort)scanCode + }; + } + + public Key MapKey(WindowsConsole.ConsoleKeyInfoEx keyInfoEx) + { + var keyInfo = keyInfoEx.ConsoleKeyInfo; + switch (keyInfo.Key) + { + case ConsoleKey.Escape: + return MapKeyModifiers(keyInfo, Key.Esc); + case ConsoleKey.Tab: + return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; + case ConsoleKey.Clear: + return MapKeyModifiers(keyInfo, Key.Clear); + case ConsoleKey.Home: + return MapKeyModifiers(keyInfo, Key.Home); + case ConsoleKey.End: + return MapKeyModifiers(keyInfo, Key.End); + case ConsoleKey.LeftArrow: + return MapKeyModifiers(keyInfo, Key.CursorLeft); + case ConsoleKey.RightArrow: + return MapKeyModifiers(keyInfo, Key.CursorRight); + case ConsoleKey.UpArrow: + return MapKeyModifiers(keyInfo, Key.CursorUp); + case ConsoleKey.DownArrow: + return MapKeyModifiers(keyInfo, Key.CursorDown); + case ConsoleKey.PageUp: + return MapKeyModifiers(keyInfo, Key.PageUp); + case ConsoleKey.PageDown: + return MapKeyModifiers(keyInfo, Key.PageDown); + case ConsoleKey.Enter: + return MapKeyModifiers(keyInfo, Key.Enter); + case ConsoleKey.Spacebar: + return MapKeyModifiers(keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); + case ConsoleKey.Backspace: + return MapKeyModifiers(keyInfo, Key.Backspace); + case ConsoleKey.Delete: + return MapKeyModifiers(keyInfo, Key.DeleteChar); + case ConsoleKey.Insert: + return MapKeyModifiers(keyInfo, Key.InsertChar); + case ConsoleKey.PrintScreen: + return MapKeyModifiers(keyInfo, Key.PrintScreen); + + case ConsoleKey.NumPad0: + return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar; + case ConsoleKey.NumPad1: + return keyInfoEx.NumLock ? Key.D1 : Key.End; + case ConsoleKey.NumPad2: + return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown; + case ConsoleKey.NumPad3: + return keyInfoEx.NumLock ? Key.D3 : Key.PageDown; + case ConsoleKey.NumPad4: + return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft; + case ConsoleKey.NumPad5: + return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar); + case ConsoleKey.NumPad6: + return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight; + case ConsoleKey.NumPad7: + return keyInfoEx.NumLock ? Key.D7 : Key.Home; + case ConsoleKey.NumPad8: + return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp; + case ConsoleKey.NumPad9: + return keyInfoEx.NumLock ? Key.D9 : Key.PageUp; + + case ConsoleKey.Oem1: + case ConsoleKey.Oem2: + case ConsoleKey.Oem3: + case ConsoleKey.Oem4: + case ConsoleKey.Oem5: + case ConsoleKey.Oem6: + case ConsoleKey.Oem7: + case ConsoleKey.Oem8: + case ConsoleKey.Oem102: + case ConsoleKey.OemPeriod: + case ConsoleKey.OemComma: + case ConsoleKey.OemPlus: + case ConsoleKey.OemMinus: + if (keyInfo.KeyChar == 0) + { + return Key.Unknown; + } + + return (Key)((uint)keyInfo.KeyChar); + } + + var key = keyInfo.Key; + //var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a'; + + if (key >= ConsoleKey.A && key <= ConsoleKey.Z) + { + var delta = key - ConsoleKey.A; + if (keyInfo.Modifiers == ConsoleModifiers.Control) + { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Alt) + { + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) + { + return MapKeyModifiers(keyInfo, (Key)((uint)Key.A + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) + { + if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) + { + return MapKeyModifiers(keyInfo, (Key)((uint)Key.A + delta)); + } + } + //return (Key)((uint)alphaBase + delta); + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) + { + var delta = key - ConsoleKey.D0; + if (keyInfo.Modifiers == ConsoleModifiers.Alt) + { + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Control) + { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) + { + return MapKeyModifiers(keyInfo, (Key)((uint)Key.D0 + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) + { + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) + { + return MapKeyModifiers(keyInfo, (Key)((uint)Key.D0 + delta)); + } + } + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) + { + var delta = key - ConsoleKey.F1; + if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) + { + return MapKeyModifiers(keyInfo, (Key)((uint)Key.F1 + delta)); + } + + return (Key)((uint)Key.F1 + delta); + } + if (keyInfo.KeyChar != 0) + { + return MapKeyModifiers(keyInfo, (Key)((uint)keyInfo.KeyChar)); + } + + return (Key)(0xffffffff); + } + + private Key MapKeyModifiers(ConsoleKeyInfo keyInfo, Key key) + { + Key keyMod = new Key(); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) + { + keyMod = Key.ShiftMask; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) + { + keyMod |= Key.CtrlMask; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) + { + keyMod |= Key.AltMask; + } + + return keyMod != Key.Null ? keyMod | key : key; + } + + int GetOutputBufferPosition() + { + return Row * Cols + Col; + } + + public override bool IsRuneSupported(Rune rune) + { + return base.IsRuneSupported(rune) && rune.IsBmp; + } + + public override void Init(Action terminalResized) + { + TerminalResized = terminalResized; + + try + { + // Needed for Windows Terminal + // Per Issue #2264 using the alternative screen buffer is required for Windows Terminal to not + // wipe out the backscroll buffer when the application exits. + Console.Out.Write(EscSeqUtils.CSI_ActivateAltBufferNoBackscroll); + + var winSize = WinConsole.GetConsoleOutputWindow(out Point pos); + Cols = winSize.Width; + Rows = winSize.Height; + WindowsConsole.SmallRect.MakeEmpty(ref _damageRegion); + + } + catch (Win32Exception) + { + // Likely running unit tests. Set WinConsole to null so we can test it elsewhere. + WinConsole = null; + //throw new InvalidOperationException ("The Windows Console output window is not available.", e); + } + + CurrentAttribute = MakeColor(Color.White, Color.Black); + InitializeColorSchemes(); + + ResizeScreen(); + ClearContents(); + } + + public virtual void ResizeScreen() + { + if (WinConsole == null) + { + return; + } + + _outputBuffer = new WindowsConsole.ExtendedCharInfo[Rows * Cols]; + Clip = new Rect(0, 0, Cols, Rows); + _damageRegion = new WindowsConsole.SmallRect() + { + Top = 0, + Left = 0, + Bottom = (short)Rows, + Right = (short)Cols + }; + _dirtyLines = new bool[Rows]; + + WinConsole.ForceRefreshCursorVisibility(); + Console.Out.Write(EscSeqUtils.CSI_ClearScreen(EscSeqUtils.ClearScreenOptions.CursorToEndOfScreen)); + } + + + public override void UpdateScreen() + { + var windowSize = WinConsole.GetConsoleBufferWindow(out _); + if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) + { + return; + } + + var bufferCoords = new WindowsConsole.Coord() + { + X = (short)Clip.Width, + Y = (short)Clip.Height + }; + + for (int row = 0; row < Rows; row++) + { + if (!_dirtyLines[row]) + { + continue; + } + _dirtyLines[row] = false; + + for (int col = 0; col < Cols; col++) + { + int position = row * Cols + col; + _outputBuffer[position].Attribute = Contents[row, col].Attribute.GetValueOrDefault(); + if (Contents[row, col].IsDirty == false) + { + _outputBuffer[position].Empty = true; + _outputBuffer[position].Char = (char)Rune.ReplacementChar.Value; + continue; + } + _outputBuffer[position].Empty = false; + if (Contents[row, col].Runes[0].IsBmp) + { + _outputBuffer[position].Char = (char)Contents[row, col].Runes[0].Value; + } + else + { + //_outputBuffer [position].Empty = true; + _outputBuffer[position].Char = (char)Rune.ReplacementChar.Value; + if (Contents[row, col].Runes[0].GetColumns() > 1 && col + 1 < Cols) + { + // TODO: This is a hack to deal with non-BMP and wide characters. + col++; + position = row * Cols + col; + _outputBuffer[position].Empty = false; + _outputBuffer[position].Char = ' '; + } + } + } + } + + WinConsole.WriteToConsole(new Size(Cols, Rows), _outputBuffer, bufferCoords, _damageRegion, UseTrueColor); + WindowsConsole.SmallRect.MakeEmpty(ref _damageRegion); + } + + public override void Refresh() + { + UpdateScreen(); + WinConsole.SetInitialCursorVisibility(); + UpdateCursor(); + } + + #region Color Handling + + /// + /// In the WindowsDriver, colors are encoded as an int. + /// The background color is stored in the least significant 4 bits, + /// and the foreground color is stored in the next 4 bits. + /// + public override Attribute MakeColor(Color foreground, Color background) + { + // Encode the colors into the int value. + return new Attribute( + value: (((int)foreground) | ((int)background << 4)), + foreground: foreground, + background: background + ); + } + + /// + /// Extracts the foreground and background colors from the encoded value. + /// Assumes a 4-bit encoded value for both foreground and background colors. + /// + internal override void GetColors(int value, out Color foreground, out Color background) + { + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = (Color)((value >> 16) & 0xF); + background = (Color)(value & 0xF); + } + + #endregion + + CursorVisibility savedCursorVisibility; + + public override void UpdateCursor() + { + if (Col < 0 || Row < 0 || Col > Cols || Row > Rows) + { + GetCursorVisibility(out CursorVisibility cursorVisibility); + savedCursorVisibility = cursorVisibility; + SetCursorVisibility(CursorVisibility.Invisible); + return; + } + + SetCursorVisibility(savedCursorVisibility); + var position = new WindowsConsole.Coord() + { + X = (short)Col, + Y = (short)Row + }; + WinConsole.SetCursorPosition(position); + } + + /// + public override bool GetCursorVisibility(out CursorVisibility visibility) + { + return WinConsole.GetCursorVisibility(out visibility); + } + + /// + public override bool SetCursorVisibility(CursorVisibility visibility) + { + savedCursorVisibility = visibility; + return WinConsole.SetCursorVisibility(visibility); + } + + /// + public override bool EnsureCursorVisibility() + { + return WinConsole.EnsureCursorVisibility(); + } + + public override void SendKeys(char keyChar, ConsoleKey key, bool shift, bool alt, bool control) + { + WindowsConsole.InputRecord input = new WindowsConsole.InputRecord + { + EventType = WindowsConsole.EventType.Key + }; + + WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord + { + bKeyDown = true + }; + WindowsConsole.ControlKeyState controlKey = new WindowsConsole.ControlKeyState(); + if (shift) + { + controlKey |= WindowsConsole.ControlKeyState.ShiftPressed; + keyEvent.UnicodeChar = '\0'; + keyEvent.wVirtualKeyCode = 16; + } + if (alt) + { + controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed; + controlKey |= WindowsConsole.ControlKeyState.RightAltPressed; + keyEvent.UnicodeChar = '\0'; + keyEvent.wVirtualKeyCode = 18; + } + if (control) + { + controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed; + controlKey |= WindowsConsole.ControlKeyState.RightControlPressed; + keyEvent.UnicodeChar = '\0'; + keyEvent.wVirtualKeyCode = 17; + } + keyEvent.dwControlKeyState = controlKey; + + input.KeyEvent = keyEvent; + + if (shift || alt || control) + { + ProcessInput(input); + } + + keyEvent.UnicodeChar = keyChar; + if ((uint)key < 255) + { + keyEvent.wVirtualKeyCode = (ushort)key; + } + else + { + keyEvent.wVirtualKeyCode = '\0'; + } + + input.KeyEvent = keyEvent; + + try + { + ProcessInput(input); + } + catch (OverflowException) { } + finally + { + keyEvent.bKeyDown = false; + input.KeyEvent = keyEvent; + ProcessInput(input); + } + } + + public override void End() + { + WinConsole?.Cleanup(); + WinConsole = null; + + // Needed for Windows Terminal + // Clear the alternative screen buffer from the cursor to the + // end of the screen. + // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE + // backbuffer! So we need to use [0J instead. + Console.Out.Write(EscSeqUtils.CSI_ClearScreen(0)); + + // Disable alternative screen buffer. + Console.Out.Write(EscSeqUtils.CSI_RestoreAltBufferWithBackscroll); + } + + #region Not Implemented + public override void Suspend() + { + throw new NotImplementedException(); + } + #endregion } /// @@ -1693,295 +1891,337 @@ public override void Suspend () /// /// This implementation is used for WindowsDriver. /// -internal class WindowsMainLoop : IMainLoopDriver { - ManualResetEventSlim _eventReady = new ManualResetEventSlim (false); - ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); - ManualResetEventSlim _winChange = new ManualResetEventSlim (false); - MainLoop _mainLoop; - ConsoleDriver _consoleDriver; - WindowsConsole _winConsole; - bool _winChanged; - Size _windowSize; - CancellationTokenSource _tokenSource = new CancellationTokenSource (); - - // The records that we keep fetching - Queue _resultQueue = new Queue (); - - /// - /// Invoked when a Key is pressed or released. - /// - public Action ProcessInput; - - /// - /// Invoked when the window is changed. - /// - public EventHandler WinChanged; - - public WindowsMainLoop (ConsoleDriver consoleDriver = null) - { - _consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided."); - _winConsole = ((WindowsDriver)consoleDriver).WinConsole; - } - - void IMainLoopDriver.Setup (MainLoop mainLoop) - { - _mainLoop = mainLoop; - Task.Run (WindowsInputHandler); - Task.Run (CheckWinChange); - } - - void WindowsInputHandler () - { - while (true) { - _waitForProbe.Wait (); - _waitForProbe.Reset (); - - if (_resultQueue?.Count == 0) { - _resultQueue.Enqueue (_winConsole.ReadConsoleInput ()); - } - - _eventReady.Set (); - } - } - - void CheckWinChange () - { - while (true) { - _winChange.Wait (); - _winChange.Reset (); - WaitWinChange (); - _winChanged = true; - _eventReady.Set (); - } - } - - void WaitWinChange () - { - while (true) { - Thread.Sleep (100); - if (!_consoleDriver.EnableConsoleScrolling) { - _windowSize = _winConsole.GetConsoleBufferWindow (out _); - //System.Diagnostics.Debug.WriteLine ($"{consoleDriver.EnableConsoleScrolling},{windowSize.Width},{windowSize.Height}"); - if (_windowSize != Size.Empty && _windowSize.Width != _consoleDriver.Cols - || _windowSize.Height != _consoleDriver.Rows) { - return; - } - } - } - } - - void IMainLoopDriver.Wakeup () - { - //tokenSource.Cancel (); - _eventReady.Set (); - } - - bool IMainLoopDriver.EventsPending (bool wait) - { - _waitForProbe.Set (); - _winChange.Set (); - - if (CheckTimers (wait, out var waitTimeout)) { - return true; - } - - try { - if (!_tokenSource.IsCancellationRequested) { - _eventReady.Wait (waitTimeout, _tokenSource.Token); - } - } catch (OperationCanceledException) { - return true; - } finally { - _eventReady.Reset (); - } - - if (!_tokenSource.IsCancellationRequested) { - return _resultQueue.Count > 0 || CheckTimers (wait, out _) || _winChanged; - } - - _tokenSource.Dispose (); - _tokenSource = new CancellationTokenSource (); - return true; - } - - bool CheckTimers (bool wait, out int waitTimeout) - { - long now = DateTime.UtcNow.Ticks; - - if (_mainLoop.timeouts.Count > 0) { - waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); - if (waitTimeout < 0) - return true; - } else { - waitTimeout = -1; - } - - if (!wait) - waitTimeout = 0; - - int ic; - lock (_mainLoop.idleHandlers) { - ic = _mainLoop.idleHandlers.Count; - } - - return ic > 0; - } - - void IMainLoopDriver.Iteration () - { - while (_resultQueue.Count > 0) { - var inputRecords = _resultQueue.Dequeue (); - if (inputRecords != null && inputRecords.Length > 0) { - var inputEvent = inputRecords [0]; - ProcessInput?.Invoke (inputEvent); - } - } - if (_winChanged) { - _winChanged = false; - WinChanged?.Invoke (this, new SizeChangedEventArgs (_windowSize)); - } - } - public void TearDown () - { - //throw new NotImplementedException (); - } +internal class WindowsMainLoop : IMainLoopDriver +{ + ManualResetEventSlim _eventReady = new ManualResetEventSlim(false); + ManualResetEventSlim _waitForProbe = new ManualResetEventSlim(false); + ManualResetEventSlim _winChange = new ManualResetEventSlim(false); + MainLoop _mainLoop; + ConsoleDriver _consoleDriver; + WindowsConsole _winConsole; + bool _winChanged; + Size _windowSize; + CancellationTokenSource _tokenSource = new CancellationTokenSource(); + + // The records that we keep fetching + Queue _resultQueue = new Queue(); + + /// + /// Invoked when a Key is pressed or released. + /// + public Action ProcessInput; + + /// + /// Invoked when the window is changed. + /// + public EventHandler WinChanged; + + public WindowsMainLoop(ConsoleDriver consoleDriver = null) + { + _consoleDriver = consoleDriver ?? throw new ArgumentNullException("Console driver instance must be provided."); + _winConsole = ((WindowsDriver)consoleDriver).WinConsole; + } + + void IMainLoopDriver.Setup(MainLoop mainLoop) + { + _mainLoop = mainLoop; + Task.Run(WindowsInputHandler); + Task.Run(CheckWinChange); + } + + void WindowsInputHandler() + { + while (true) + { + _waitForProbe.Wait(); + _waitForProbe.Reset(); + + if (_resultQueue?.Count == 0) + { + _resultQueue.Enqueue(_winConsole.ReadConsoleInput()); + } + + _eventReady.Set(); + } + } + + void CheckWinChange() + { + while (true) + { + _winChange.Wait(); + _winChange.Reset(); + WaitWinChange(); + _winChanged = true; + _eventReady.Set(); + } + } + + void WaitWinChange() + { + while (true) + { + Thread.Sleep(100); + _windowSize = _winConsole.GetConsoleBufferWindow(out _); + if (_windowSize != Size.Empty && _windowSize.Width != _consoleDriver.Cols + || _windowSize.Height != _consoleDriver.Rows) + { + return; + } + } + } + + void IMainLoopDriver.Wakeup() + { + //tokenSource.Cancel (); + _eventReady.Set(); + } + + bool IMainLoopDriver.EventsPending(bool wait) + { + _waitForProbe.Set(); + _winChange.Set(); + + if (CheckTimers(wait, out var waitTimeout)) + { + return true; + } + + try + { + if (!_tokenSource.IsCancellationRequested) + { + _eventReady.Wait(waitTimeout, _tokenSource.Token); + } + } + catch (OperationCanceledException) + { + return true; + } + finally + { + _eventReady.Reset(); + } + + if (!_tokenSource.IsCancellationRequested) + { + return _resultQueue.Count > 0 || CheckTimers(wait, out _) || _winChanged; + } + + _tokenSource.Dispose(); + _tokenSource = new CancellationTokenSource(); + return true; + } + + bool CheckTimers(bool wait, out int waitTimeout) + { + long now = DateTime.UtcNow.Ticks; + + if (_mainLoop.timeouts.Count > 0) + { + waitTimeout = (int)((_mainLoop.timeouts.Keys[0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) + return true; + } + else + { + waitTimeout = -1; + } + + if (!wait) + waitTimeout = 0; + + int ic; + lock (_mainLoop.idleHandlers) + { + ic = _mainLoop.idleHandlers.Count; + } + + return ic > 0; + } + + void IMainLoopDriver.Iteration() + { + while (_resultQueue.Count > 0) + { + var inputRecords = _resultQueue.Dequeue(); + if (inputRecords != null && inputRecords.Length > 0) + { + var inputEvent = inputRecords[0]; + ProcessInput?.Invoke(inputEvent); + } + } + if (_winChanged) + { + _winChanged = false; + WinChanged?.Invoke(this, new SizeChangedEventArgs(_windowSize)); + } + } + public void TearDown() + { + //throw new NotImplementedException (); + } } -class WindowsClipboard : ClipboardBase { - public WindowsClipboard () - { - IsSupported = IsClipboardFormatAvailable (_cfUnicodeText); - } - - public override bool IsSupported { get; } - - protected override string GetClipboardDataImpl () - { - try { - if (!OpenClipboard (IntPtr.Zero)) { - return string.Empty; - } - - IntPtr handle = GetClipboardData (_cfUnicodeText); - if (handle == IntPtr.Zero) { - return string.Empty; - } - - IntPtr pointer = IntPtr.Zero; - - try { - pointer = GlobalLock (handle); - if (pointer == IntPtr.Zero) { - return string.Empty; - } - - int size = GlobalSize (handle); - byte [] buff = new byte [size]; - - Marshal.Copy (pointer, buff, 0, size); - - return System.Text.Encoding.Unicode.GetString (buff).TrimEnd ('\0'); - } finally { - if (pointer != IntPtr.Zero) { - GlobalUnlock (handle); - } - } - } finally { - CloseClipboard (); - } - } - - protected override void SetClipboardDataImpl (string text) - { - OpenClipboard (); - - EmptyClipboard (); - IntPtr hGlobal = default; - try { - var bytes = (text.Length + 1) * 2; - hGlobal = Marshal.AllocHGlobal (bytes); - - if (hGlobal == default) { - ThrowWin32 (); - } - - var target = GlobalLock (hGlobal); - - if (target == default) { - ThrowWin32 (); - } - - try { - Marshal.Copy (text.ToCharArray (), 0, target, text.Length); - } finally { - GlobalUnlock (target); - } - - if (SetClipboardData (_cfUnicodeText, hGlobal) == default) { - ThrowWin32 (); - } - - hGlobal = default; - } finally { - if (hGlobal != default) { - Marshal.FreeHGlobal (hGlobal); - } - - CloseClipboard (); - } - } - - void OpenClipboard () - { - var num = 10; - while (true) { - if (OpenClipboard (default)) { - break; - } - - if (--num == 0) { - ThrowWin32 (); - } - - Thread.Sleep (100); - } - } - - const uint _cfUnicodeText = 13; - - void ThrowWin32 () - { - throw new Win32Exception (Marshal.GetLastWin32Error ()); - } - - [DllImport ("User32.dll", SetLastError = true)] - [return: MarshalAs (UnmanagedType.Bool)] - static extern bool IsClipboardFormatAvailable (uint format); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern int GlobalSize (IntPtr handle); - - [DllImport ("kernel32.dll", SetLastError = true)] - static extern IntPtr GlobalLock (IntPtr hMem); - - [DllImport ("kernel32.dll", SetLastError = true)] - [return: MarshalAs (UnmanagedType.Bool)] - static extern bool GlobalUnlock (IntPtr hMem); - - [DllImport ("user32.dll", SetLastError = true)] - [return: MarshalAs (UnmanagedType.Bool)] - static extern bool OpenClipboard (IntPtr hWndNewOwner); - - [DllImport ("user32.dll", SetLastError = true)] - [return: MarshalAs (UnmanagedType.Bool)] - static extern bool CloseClipboard (); - - [DllImport ("user32.dll", SetLastError = true)] - static extern IntPtr SetClipboardData (uint uFormat, IntPtr data); - - [DllImport ("user32.dll")] - static extern bool EmptyClipboard (); - - [DllImport ("user32.dll", SetLastError = true)] - static extern IntPtr GetClipboardData (uint uFormat); +class WindowsClipboard : ClipboardBase +{ + public WindowsClipboard() + { + IsSupported = IsClipboardFormatAvailable(_cfUnicodeText); + } + + public override bool IsSupported { get; } + + protected override string GetClipboardDataImpl() + { + try + { + if (!OpenClipboard(IntPtr.Zero)) + { + return string.Empty; + } + + IntPtr handle = GetClipboardData(_cfUnicodeText); + if (handle == IntPtr.Zero) + { + return string.Empty; + } + + IntPtr pointer = IntPtr.Zero; + + try + { + pointer = GlobalLock(handle); + if (pointer == IntPtr.Zero) + { + return string.Empty; + } + + int size = GlobalSize(handle); + byte[] buff = new byte[size]; + + Marshal.Copy(pointer, buff, 0, size); + + return System.Text.Encoding.Unicode.GetString(buff).TrimEnd('\0'); + } + finally + { + if (pointer != IntPtr.Zero) + { + GlobalUnlock(handle); + } + } + } + finally + { + CloseClipboard(); + } + } + + protected override void SetClipboardDataImpl(string text) + { + OpenClipboard(); + + EmptyClipboard(); + IntPtr hGlobal = default; + try + { + var bytes = (text.Length + 1) * 2; + hGlobal = Marshal.AllocHGlobal(bytes); + + if (hGlobal == default) + { + ThrowWin32(); + } + + var target = GlobalLock(hGlobal); + + if (target == default) + { + ThrowWin32(); + } + + try + { + Marshal.Copy(text.ToCharArray(), 0, target, text.Length); + } + finally + { + GlobalUnlock(target); + } + + if (SetClipboardData(_cfUnicodeText, hGlobal) == default) + { + ThrowWin32(); + } + + hGlobal = default; + } + finally + { + if (hGlobal != default) + { + Marshal.FreeHGlobal(hGlobal); + } + + CloseClipboard(); + } + } + + void OpenClipboard() + { + var num = 10; + while (true) + { + if (OpenClipboard(default)) + { + break; + } + + if (--num == 0) + { + ThrowWin32(); + } + + Thread.Sleep(100); + } + } + + const uint _cfUnicodeText = 13; + + void ThrowWin32() + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + [DllImport("User32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool IsClipboardFormatAvailable(uint format); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern int GlobalSize(IntPtr handle); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr GlobalLock(IntPtr hMem); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GlobalUnlock(IntPtr hMem); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool OpenClipboard(IntPtr hWndNewOwner); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CloseClipboard(); + + [DllImport("user32.dll", SetLastError = true)] + static extern IntPtr SetClipboardData(uint uFormat, IntPtr data); + + [DllImport("user32.dll")] + static extern bool EmptyClipboard(); + + [DllImport("user32.dll", SetLastError = true)] + static extern IntPtr GetClipboardData(uint uFormat); } \ No newline at end of file diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json index 49e669d53c..d1c69247d8 100644 --- a/Terminal.Gui/Resources/config.json +++ b/Terminal.Gui/Resources/config.json @@ -28,7 +28,6 @@ "Ctrl" ] }, - "Application.EnableConsoleScrolling": false, "Application.QuitKey": { "Key": "Q", "Modifiers": [ diff --git a/Terminal.sln b/Terminal.sln index cd242ab364..b0837d7501 100644 --- a/Terminal.sln +++ b/Terminal.sln @@ -14,15 +14,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "Example\Example. EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E143FB1F-0B88-48CB-9086-72CDCECFCD22}" ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig .gitignore = .gitignore + .github\CODEOWNERS = .github\CODEOWNERS CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md CONTRIBUTING.md = CONTRIBUTING.md .github\workflows\dotnet-core.yml = .github\workflows\dotnet-core.yml - .github\GitVersion.yml = .github\GitVersion.yml .github\workflows\publish.yml = .github\workflows\publish.yml README.md = README.md - Release.ps1 = Release.ps1 testenvironments.json = testenvironments.json EndProjectSection EndProject diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 96610af998..06960b4c04 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -214,8 +214,6 @@ static Scenario RunUICatalogTopLevel () CM.Apply (); } - //Application.EnableConsoleScrolling = _enableConsoleScrolling; - Application.Run (); Application.Shutdown (); @@ -239,7 +237,6 @@ static Scenario RunUICatalogTopLevel () static bool _useSystemConsole = false; static ConsoleDriver.DiagnosticFlags _diagnosticFlags; - //static bool _enableConsoleScrolling = false; static bool _isFirstRunning = true; static string _topLevelColorScheme = string.Empty; @@ -254,7 +251,6 @@ public class UICatalogTopLevel : Toplevel { public MenuItem? miUseSubMenusSingleFrame; public MenuItem? miIsMenuBorderDisabled; public MenuItem? miIsMouseDisabled; - public MenuItem? miEnableConsoleScrolling; public ListView CategoryList; @@ -426,7 +422,6 @@ void LoadedHandler (object? sender, EventArgs? args) ConfigChanged (); miIsMouseDisabled!.Checked = Application.IsMouseDisabled; - miEnableConsoleScrolling!.Checked = Application.EnableConsoleScrolling; DriverName.Title = $"Driver: {Driver.GetType ().Name}"; OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}"; @@ -490,7 +485,6 @@ List CreateDiagnosticMenuItems () List menuItems = new List { CreateDiagnosticFlagsMenuItems (), Array.Empty (), - CreateEnableConsoleScrollingMenuItems (), CreateDisabledEnabledMouseItems (), CreateDisabledEnabledMenuBorder (), CreateDisabledEnableUseSubMenusSingleFrame (), @@ -567,23 +561,6 @@ MenuItem [] CreateKeybindingsMenuItems () return menuItems.ToArray (); } - MenuItem [] CreateEnableConsoleScrollingMenuItems () - { - List menuItems = new List (); - miEnableConsoleScrolling = new MenuItem { - Title = "_Enable Console Scrolling" - }; - miEnableConsoleScrolling.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miEnableConsoleScrolling.Title.ToString ()!.Substring (1, 1) [0]; - miEnableConsoleScrolling.CheckType |= MenuItemCheckStyle.Checked; - miEnableConsoleScrolling.Action += () => { - miEnableConsoleScrolling.Checked = !miEnableConsoleScrolling.Checked; - Application.EnableConsoleScrolling = (bool)miEnableConsoleScrolling.Checked!; - }; - menuItems.Add (miEnableConsoleScrolling); - - return menuItems.ToArray (); - } - MenuItem [] CreateDiagnosticFlagsMenuItems () { const string OFF = "Diagnostics: _Off"; @@ -760,7 +737,6 @@ public void ConfigChanged () StatusBar.Items [0].Title = $"~{Application.QuitKey} to quit"; miIsMouseDisabled!.Checked = Application.IsMouseDisabled; - miEnableConsoleScrolling!.Checked = Application.EnableConsoleScrolling; var height = (UICatalogApp.ShowStatusBar ? 1 : 0);// + (MenuBar.Visible ? 1 : 0); //ContentPane.Height = Dim.Fill (height); diff --git a/UnitTests/Application/ApplicationTests.cs b/UnitTests/Application/ApplicationTests.cs index 96951dbef2..8ece3526fe 100644 --- a/UnitTests/Application/ApplicationTests.cs +++ b/UnitTests/Application/ApplicationTests.cs @@ -24,7 +24,6 @@ void Pre_Init_State () Assert.Null (Application.Driver); Assert.Null (Application.Top); Assert.Null (Application.Current); - Assert.False (Application.EnableConsoleScrolling); Assert.Null (Application.MainLoop); Assert.Null (Application.Iteration); Assert.Null (Application.RootMouseEvent); @@ -36,7 +35,6 @@ void Post_Init_State () Assert.NotNull (Application.Driver); Assert.NotNull (Application.Top); Assert.NotNull (Application.Current); - Assert.False (Application.EnableConsoleScrolling); Assert.NotNull (Application.MainLoop); Assert.Null (Application.Iteration); Assert.Null (Application.RootMouseEvent); diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index 70a1d50a89..662cd61f73 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -224,7 +224,6 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Apply (); // assert apply worked @@ -232,7 +231,6 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () Assert.Equal (Key.F, Application.AlternateForwardKey); Assert.Equal (Key.B, Application.AlternateBackwardKey); Assert.True (Application.IsMouseDisabled); - Assert.True (Application.EnableConsoleScrolling); //act ConfigurationManager.Reset (); @@ -244,14 +242,12 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () Assert.Equal (Key.PageDown | Key.CtrlMask, Application.AlternateForwardKey); Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey); Assert.False (Application.IsMouseDisabled); - Assert.False (Application.EnableConsoleScrolling); // arrange ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q; ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Apply (); ConfigurationManager.Locations = ConfigLocations.DefaultOnly; @@ -267,7 +263,6 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () Assert.Equal (Key.PageDown | Key.CtrlMask, Application.AlternateForwardKey); Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey); Assert.False (Application.IsMouseDisabled); - Assert.False (Application.EnableConsoleScrolling); } @@ -747,7 +742,6 @@ public void Load_FiresUpdated () ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Updated += ConfigurationManager_Updated; bool fired = false; @@ -759,7 +753,6 @@ void ConfigurationManager_Updated (object sender, ConfigurationManagerEventArgs Assert.Equal (Key.PageDown | Key.CtrlMask, ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue); Assert.Equal (Key.PageUp | Key.CtrlMask, ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue); - Assert.False ((bool)ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue); } ConfigurationManager.Load (true); @@ -784,7 +777,6 @@ void ConfigurationManager_Applied (object sender, ConfigurationManagerEventArgs Assert.Equal (Key.F, Application.AlternateForwardKey); Assert.Equal (Key.B, Application.AlternateBackwardKey); Assert.True (Application.IsMouseDisabled); - Assert.True (Application.EnableConsoleScrolling); } // act @@ -792,7 +784,6 @@ void ConfigurationManager_Applied (object sender, ConfigurationManagerEventArgs ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Apply (); diff --git a/UnitTests/Configuration/SettingsScopeTests.cs b/UnitTests/Configuration/SettingsScopeTests.cs index 4e1fc28f18..b7d89f258d 100644 --- a/UnitTests/Configuration/SettingsScopeTests.cs +++ b/UnitTests/Configuration/SettingsScopeTests.cs @@ -25,7 +25,6 @@ public void GetHardCodedDefaults_ShouldSetProperties () Assert.True (ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue is Key); Assert.True (ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue is Key); Assert.True (ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue is bool); - Assert.True (ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue is bool); Assert.True (ConfigurationManager.Settings ["Theme"].PropertyValue is string); Assert.Equal ("Default", ConfigurationManager.Settings ["Theme"].PropertyValue as string); @@ -43,14 +42,12 @@ public void Apply_ShouldApplyProperties () Assert.Equal (Key.PageDown | Key.CtrlMask, (Key)ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue); Assert.Equal (Key.PageUp | Key.CtrlMask, (Key)ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue); Assert.False ((bool)ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue); - Assert.False ((bool)ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue); // act ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue = Key.Q; ConfigurationManager.Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F; ConfigurationManager.Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B; ConfigurationManager.Settings ["Application.IsMouseDisabled"].PropertyValue = true; - ConfigurationManager.Settings ["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Apply (); @@ -59,7 +56,6 @@ public void Apply_ShouldApplyProperties () Assert.Equal (Key.F, Application.AlternateForwardKey); Assert.Equal (Key.B, Application.AlternateBackwardKey); Assert.True (Application.IsMouseDisabled); - Assert.True (Application.EnableConsoleScrolling); } [Fact, AutoInitShutdown] @@ -73,14 +69,12 @@ public void CopyUpdatedProperitesFrom_ShouldCopyChangedPropertiesOnly () updatedSettings["Application.AlternateForwardKey"].PropertyValue = Key.F; updatedSettings["Application.AlternateBackwardKey"].PropertyValue = Key.B; updatedSettings["Application.IsMouseDisabled"].PropertyValue = true; - updatedSettings["Application.EnableConsoleScrolling"].PropertyValue = true; ConfigurationManager.Settings.Update (updatedSettings); Assert.Equal (Key.End, ConfigurationManager.Settings ["Application.QuitKey"].PropertyValue); Assert.Equal (Key.F, updatedSettings ["Application.AlternateForwardKey"].PropertyValue); Assert.Equal (Key.B, updatedSettings ["Application.AlternateBackwardKey"].PropertyValue); Assert.True ((bool)updatedSettings ["Application.IsMouseDisabled"].PropertyValue); - Assert.True ((bool)updatedSettings ["Application.EnableConsoleScrolling"].PropertyValue); } } } \ No newline at end of file diff --git a/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs b/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs index 6f43bc0ae1..9779c79b4f 100644 --- a/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs +++ b/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs @@ -223,17 +223,6 @@ public void TerminalResized_Simulation (Type driverType) Assert.Equal (40, Application.Driver.Rows); Assert.True (wasTerminalResized); - // MockDriver will still be 120x40 - wasTerminalResized = false; - Application.EnableConsoleScrolling = true; - driver.SetWindowSize (40, 20); - Assert.Equal (120, Application.Driver.Cols); - Assert.Equal (40, Application.Driver.Rows); - Assert.Equal (120, Console.BufferWidth); - Assert.Equal (40, Console.BufferHeight); - Assert.Equal (40, Console.WindowWidth); - Assert.Equal (20, Console.WindowHeight); - Assert.True (wasTerminalResized); Application.Shutdown (); } diff --git a/UnitTests/ConsoleDrivers/ConsoleScrolllingTests.cs b/UnitTests/ConsoleDrivers/ConsoleScrolllingTests.cs index 6dd461af69..9bd550ca77 100644 --- a/UnitTests/ConsoleDrivers/ConsoleScrolllingTests.cs +++ b/UnitTests/ConsoleDrivers/ConsoleScrolllingTests.cs @@ -19,12 +19,11 @@ public ConsoleScrollingTests (ITestOutputHelper output) [Theory] [InlineData (typeof (FakeDriver))] - public void EnableConsoleScrolling_Is_False_Left_And_Top_Is_Always_Zero (Type driverType) + public void Left_And_Top_Is_Always_Zero (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Assert.False (Application.EnableConsoleScrolling); Assert.Equal (0, Console.WindowLeft); Assert.Equal (0, Console.WindowTop); @@ -34,123 +33,6 @@ public void EnableConsoleScrolling_Is_False_Left_And_Top_Is_Always_Zero (Type dr Application.Shutdown (); } - - [Theory] - [InlineData (typeof (FakeDriver))] - public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type driverType) - { - var driver = (FakeDriver)Activator.CreateInstance (driverType); - Application.Init (driver); - - Application.EnableConsoleScrolling = true; - Assert.True (Application.EnableConsoleScrolling); - - driver.SetWindowPosition (81, 25); - Assert.Equal (0, Console.WindowLeft); - Assert.Equal (0, Console.WindowTop); - - Application.Shutdown (); - } - - [Theory] - [InlineData (typeof (FakeDriver))] - public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth (Type driverType) - { - var driver = (FakeDriver)Activator.CreateInstance (driverType); - Application.Init (driver); - - Application.EnableConsoleScrolling = true; - Assert.True (Application.EnableConsoleScrolling); - - driver.SetWindowPosition (81, 25); - Assert.Equal (0, Console.WindowLeft); - Assert.Equal (0, Console.WindowTop); - - // MockDriver will now be set to 120x25 - driver.SetBufferSize (120, 25); - Assert.Equal (120, Application.Driver.Cols); - Assert.Equal (25, Application.Driver.Rows); - Assert.Equal (120, Console.BufferWidth); - Assert.Equal (25, Console.BufferHeight); - Assert.Equal (80, Console.WindowWidth); - Assert.Equal (25, Console.WindowHeight); - driver.SetWindowPosition (121, 25); - Assert.Equal (40, Console.WindowLeft); - Assert.Equal (0, Console.WindowTop); - - driver.SetWindowSize (90, 25); - Assert.Equal (120, Application.Driver.Cols); - Assert.Equal (25, Application.Driver.Rows); - Assert.Equal (120, Console.BufferWidth); - Assert.Equal (25, Console.BufferHeight); - Assert.Equal (90, Console.WindowWidth); - Assert.Equal (25, Console.WindowHeight); - driver.SetWindowPosition (121, 25); - Assert.Equal (30, Console.WindowLeft); - Assert.Equal (0, Console.WindowTop); - - Application.Shutdown (); - } - - [Theory] - [InlineData (typeof (FakeDriver))] - public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type driverType) - { - var driver = (FakeDriver)Activator.CreateInstance (driverType); - Application.Init (driver); - - Application.EnableConsoleScrolling = true; - Assert.True (Application.EnableConsoleScrolling); - - driver.SetWindowPosition (80, 26); - Assert.Equal (0, Console.WindowLeft); - Assert.Equal (0, Console.WindowTop); - - Application.Shutdown (); - } - - [Theory] - [InlineData (typeof (FakeDriver))] - public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight (Type driverType) - { - var driver = (FakeDriver)Activator.CreateInstance (driverType); - Application.Init (driver); - - Application.EnableConsoleScrolling = true; - Assert.True (Application.EnableConsoleScrolling); - - driver.SetWindowPosition (80, 26); - Assert.Equal (0, Console.WindowLeft); - Assert.Equal (0, Console.WindowTop); - - // MockDriver will now be sets to 80x40 - driver.SetBufferSize (80, 40); - Assert.Equal (80, Application.Driver.Cols); - Assert.Equal (40, Application.Driver.Rows); - Assert.Equal (80, Console.BufferWidth); - Assert.Equal (40, Console.BufferHeight); - Assert.Equal (80, Console.WindowWidth); - Assert.Equal (25, Console.WindowHeight); - Assert.Equal (0, Console.WindowLeft); - Assert.Equal (0, Console.WindowTop); - driver.SetWindowPosition (80, 40); - Assert.Equal (0, Console.WindowLeft); - Assert.Equal (15, Console.WindowTop); - - driver.SetWindowSize (80, 20); - Assert.Equal (80, Application.Driver.Cols); - Assert.Equal (40, Application.Driver.Rows); - Assert.Equal (80, Console.BufferWidth); - Assert.Equal (40, Console.BufferHeight); - Assert.Equal (80, Console.WindowWidth); - Assert.Equal (20, Console.WindowHeight); - Assert.Equal (0, Console.WindowLeft); - Assert.Equal (15, Console.WindowTop); - driver.SetWindowPosition (80, 41); - Assert.Equal (0, Console.WindowLeft); - Assert.Equal (20, Console.WindowTop); - - Application.Shutdown (); - } + } } From b2f9b922c350401267dee31761a9b1db2782a406 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 26 Jul 2023 08:59:10 -0600 Subject: [PATCH 87/99] Backport #2764 from develop (clear last line) --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 45 ++++++-------------- Terminal.Gui/Terminal.Gui.csproj | 5 --- 2 files changed, 12 insertions(+), 38 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index db8f342895..99513bf814 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -251,7 +251,6 @@ public void Cleanup() SetConsoleOutputWindow(out _); ConsoleMode = _originalConsoleMode; - //ContinueListeningForConsoleEvents = false; if (!SetConsoleActiveScreenBuffer(_outputHandle)) { var err = Marshal.GetLastWin32Error(); @@ -308,6 +307,7 @@ internal Size SetConsoleWindow(short cols, short rows) { var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX(); csbi.cbSize = (uint)Marshal.SizeOf(csbi); + if (!GetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); @@ -371,8 +371,6 @@ internal Size SetConsoleOutputWindow(out Point position) return sz; } - //bool ContinueListeningForConsoleEvents = true; - uint ConsoleMode { get @@ -1021,17 +1019,6 @@ void ProcessInput(WindowsConsole.InputRecord inputEvent) } break; - case WindowsConsole.EventType.WindowBufferSize: - var winSize = WinConsole.GetConsoleBufferWindow(out Point pos); - Left = pos.X; - Top = pos.Y; - Cols = inputEvent.WindowBufferSizeEvent._size.X; - Rows = inputEvent.WindowBufferSizeEvent._size.Y; - ResizeScreen(); - ClearContents(); - TerminalResized?.Invoke(); - break; - case WindowsConsole.EventType.Focus: break; } @@ -1043,7 +1030,6 @@ void ProcessInput(WindowsConsole.InputRecord inputEvent) bool _isButtonDoubleClicked = false; Point? _point; Point _pointMove; - //int _buttonPressedCount; bool _isOneFingerDoubleClicked = false; bool _processButtonClick; @@ -1596,11 +1582,6 @@ private Key MapKeyModifiers(ConsoleKeyInfo keyInfo, Key key) return keyMod != Key.Null ? keyMod | key : key; } - int GetOutputBufferPosition() - { - return Row * Cols + Col; - } - public override bool IsRuneSupported(Rune rune) { return base.IsRuneSupported(rune) && rune.IsBmp; @@ -1613,8 +1594,6 @@ public override void Init(Action terminalResized) try { // Needed for Windows Terminal - // Per Issue #2264 using the alternative screen buffer is required for Windows Terminal to not - // wipe out the backscroll buffer when the application exits. Console.Out.Write(EscSeqUtils.CSI_ActivateAltBufferNoBackscroll); var winSize = WinConsole.GetConsoleOutputWindow(out Point pos); @@ -1627,13 +1606,21 @@ public override void Init(Action terminalResized) { // Likely running unit tests. Set WinConsole to null so we can test it elsewhere. WinConsole = null; - //throw new InvalidOperationException ("The Windows Console output window is not available.", e); } CurrentAttribute = MakeColor(Color.White, Color.Black); InitializeColorSchemes(); - ResizeScreen(); + _outputBuffer = new WindowsConsole.ExtendedCharInfo[Rows * Cols]; + Clip = new Rect(0, 0, Cols, Rows); + _damageRegion = new WindowsConsole.SmallRect() + { + Top = 0, + Left = 0, + Bottom = (short)Rows, + Right = (short)Cols + }; + ClearContents(); } @@ -1656,7 +1643,6 @@ public virtual void ResizeScreen() _dirtyLines = new bool[Rows]; WinConsole.ForceRefreshCursorVisibility(); - Console.Out.Write(EscSeqUtils.CSI_ClearScreen(EscSeqUtils.ClearScreenOptions.CursorToEndOfScreen)); } @@ -1865,13 +1851,6 @@ public override void End() WinConsole?.Cleanup(); WinConsole = null; - // Needed for Windows Terminal - // Clear the alternative screen buffer from the cursor to the - // end of the screen. - // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE - // backbuffer! So we need to use [0J instead. - Console.Out.Write(EscSeqUtils.CSI_ClearScreen(0)); - // Disable alternative screen buffer. Console.Out.Write(EscSeqUtils.CSI_RestoreAltBufferWithBackscroll); } @@ -1961,7 +1940,7 @@ void WaitWinChange() { while (true) { - Thread.Sleep(100); + Task.Delay(500).Wait(); _windowSize = _winConsole.GetConsoleBufferWindow(out _); if (_windowSize != Size.Empty && _windowSize.Width != _consoleDriver.Cols || _windowSize.Height != _consoleDriver.Rows) diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index f487887ce6..8e6a488821 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -32,11 +32,6 @@ - - - - - From 45da757c3deaa04d89b2c7d908a7a485cbbe2f48 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 26 Jul 2023 09:35:03 -0600 Subject: [PATCH 88/99] Remove uneeded usings --- Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index f489c47c5a..bf283f5229 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -3,14 +3,9 @@ // using System; using System.Collections.Generic; -using System.ComponentModel.Design.Serialization; -using System.Linq; using System.Runtime.InteropServices; -using System.Threading.Tasks; using System.Text; using Unix.Terminal; -using System.Buffers; -using System.Data; namespace Terminal.Gui; From 06a9ba9b394be6b420bac07f43f740f391cb071d Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Sun, 6 Aug 2023 06:33:43 -0600 Subject: [PATCH 89/99] Progress on unicode --- Terminal.Gui/Application.cs | 3 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 59 +- .../CursesDriver/CursesDriver.cs | 106 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 2855 ++++++------ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 4063 ++++++++--------- Terminal.Gui/View/ViewSubViews.cs | 15 +- UICatalog/Properties/launchSettings.json | 3 + UICatalog/Scenarios/CharacterMap.cs | 21 +- 8 files changed, 3372 insertions(+), 3753 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index cb513aff04..b124927820 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -517,7 +517,7 @@ public static void Run (Toplevel view, Func errorHandler = null /// public static void Refresh () { - Driver.ClearContents(); + //Driver.ClearContents(); View last = null; foreach (var v in _toplevels.Reverse ()) { if (v.Visible) { @@ -976,7 +976,6 @@ static void OnTerminalResized () { var full = new Rect (0, 0, Driver.Cols, Driver.Rows); TerminalResized?.Invoke (new ResizedEventArgs () { Cols = full.Width, Rows = full.Height }); - Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows); foreach (var t in _toplevels) { t.SetRelativeLayout (full); t.LayoutSubviews (); diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 15bf166be8..8a1178225d 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -150,7 +150,7 @@ public void AddRune (Rune rune) if (validLocation) { rune = rune.MakePrintable (); runeWidth = rune.GetColumns (); - if (runeWidth == 0 && Col > 0) { + if (runeWidth == 0 && rune.IsCombiningMark() && Col > 0) { // This is a combining character, and we are not at the beginning of the line. // TODO: Remove hard-coded [0] once combining pairs is supported @@ -163,30 +163,57 @@ public void AddRune (Rune rune) Contents [Row, Col - 1].Runes [0] = (Rune)normalized [0]; Contents [Row, Col - 1].Attribute = CurrentAttribute; Contents [Row, Col - 1].IsDirty = true; + + Col--; } else { Contents [Row, Col].Attribute = CurrentAttribute; Contents [Row, Col].IsDirty = true; - if (runeWidth < 2 && Col > 0 && Contents [Row, Col - 1].Runes [0].GetColumns () > 1) { - // This is a single-width character, and we are not at the beginning of the line. - Contents [Row, Col - 1].Runes [0] = Rune.ReplacementChar; - Contents [Row, Col - 1].IsDirty = true; - } else if (runeWidth < 2 && Col <= Clip.Right - 1 && Contents [Row, Col].Runes [0].GetColumns () > 1) { - // This is a single-width character, and we are not at the end of the line. - Contents [Row, Col + 1].Runes [0] = Rune.ReplacementChar; - Contents [Row, Col + 1].IsDirty = true; + if (Col > 0) { + // Check if cell to left has a wide glyph + if (Contents [Row, Col - 1].Runes [0].GetColumns () > 1) { + // Invalidate cell to left + Contents [Row, Col - 1].Runes [0] = Rune.ReplacementChar; + Contents [Row, Col - 1].IsDirty = true; + } } - if (runeWidth > 1 && Col == Clip.Right - 1) { - // This is a double-width character, and we are at the end of the line. + + // Check if we're at right edge of clip + if (runeWidth < 1 || (runeWidth == 2 && Col == Clip.Right - 1)) { + // Can't put a double width glyph here Contents [Row, Col].Runes [0] = Rune.ReplacementChar; } else { - //if (runeWidth == 1) { + + if (runeWidth >= 1) { // This is a single-width character, or we are not at the end of the line. Contents [Row, Col].Runes [0] = rune; - //} else { - //Contents [Row, Col].Runes [0] = (Rune)'*'; - //} + } else { + Contents [Row, Col].Runes [0] = (Rune)'^'; + Contents [Row, Col].IsDirty = false; + } } + //if (runeWidth < 2 && Col > 0 && Contents [Row, Col - 1].Runes [0].GetColumns () > 1) { + // // This is a single-width character, and we are not at the beginning of the line. + // Contents [Row, Col - 1].Runes [0] = Rune.ReplacementChar; + // Contents [Row, Col - 1].IsDirty = true; + //} else if (runeWidth < 2 && Col <= Clip.Right - 1 && Contents [Row, Col].Runes [0].GetColumns () > 1) { + // // This is a single-width character, and we are not at the end of the line. + // Contents [Row, Col + 1].Runes [0] = Rune.ReplacementChar; + // Contents [Row, Col + 1].IsDirty = true; + //} + //if (runeWidth > 1 && Col == Clip.Right - 1) { + // // This is a double-width character, and we are at the end of the line. + // Contents [Row, Col].Runes [0] = Rune.ReplacementChar; + //} else { + // if (runeWidth > 0) { + // // This is a single-width character, or we are not at the end of the line. + // Contents [Row, Col].Runes [0] = rune; + // } + // else { + // Contents [Row, Col].Runes [0] = (Rune)'^'; + // Contents [Row, Col].IsDirty = false; + // } + //} _dirtyLines [Row] = true; } } @@ -301,6 +328,7 @@ public void ClearContents () { // TODO: This method is really "Clear Contents" now and should not be abstract (or virtual) Contents = new Cell [Rows, Cols]; + Clip = new Rect (0, 0, Cols, Rows); _dirtyLines = new bool [Rows]; lock (Contents) { @@ -318,7 +346,6 @@ public void ClearContents () } } catch (IndexOutOfRangeException) { } } - } /// diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index bf283f5229..6a77aff560 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -40,17 +40,13 @@ public override bool IsRuneSupported (Rune rune) public override void Refresh () { - Curses.raw (); - Curses.noecho (); - Curses.refresh (); - ProcessWinChange (); UpdateScreen (); + UpdateCursor (); } private void ProcessWinChange () { if (Curses.CheckWinChange ()) { - ResizeScreen (); ClearContents (); TerminalResized?.Invoke (); } @@ -179,7 +175,14 @@ internal override void GetColors (int value, out Color foreground, out Color bac #endregion - public override void UpdateCursor () => Refresh (); + public override void UpdateCursor () + { + EnsureCursorVisibility (); + + if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) { + Curses.move (Row, Col); + } + } public override void End () { @@ -198,40 +201,67 @@ public override void UpdateScreen () _dirtyLines [row] = false; for (int col = 0; col < Cols; col++) { - //Curses.mvaddch (row, col, '+'); - if (Contents [row, col].IsDirty == false) { - //Curses.mvaddch (row, col, (char)Rune.ReplacementChar.Value); continue; } Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().Value); if (Contents [row, col].Runes [0].IsBmp) { - Curses.mvaddch (row, col, Contents [row, col].Runes [0].Value); + //_outputBuffer [position].Char = (char)Contents [row, col].Runes [0].Value; + Curses.mvaddwstr (row, col, Contents [row, col].Runes [0].ToString()); + if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { + col++; + //Curses.mvaddwstr (row, col, ' '); + } + } else { - Curses.mvaddch (row, col, Contents [row, col].Runes [0].Value); + //_outputBuffer [position].Empty = true; //_outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; + Curses.mvaddch (row, col, '#'); if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { // TODO: This is a hack to deal with non-BMP and wide characters. //col++; - //_outputBuffer [position].Empty = false; - //_outputBuffer [position].Char = ' '; - //Curses.mvaddch (row, col, '*'); + Curses.mvaddch (row, ++col, '*'); } } - - if (Contents [row, col].Runes [0].IsSurrogatePair () && Contents [row, col].Runes [0].GetColumns () < 2) { - col--; - } - - //if (col < Cols && Contents [row, col].Runes [0].GetColumns () > 1) { - // col++; - // Curses.mvaddch (row, col, '='); - //} } + + //for (int col = 0; col < Cols; col++) { + // //Curses.mvaddch (row, col, '+'); + + // if (Contents [row, col].IsDirty == false) { + // //Curses.mvaddch (row, col, (char)Rune.ReplacementChar.Value); + // continue; + // } + + // Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().Value); + + // if (Contents [row, col].Runes [0].IsBmp) { + // Curses.mvaddch (row, col, Contents [row, col].Runes [0].Value); + // } else { + // //_outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; + // if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { + // // TODO: This is a hack to deal with non-BMP and wide characters. + // //col++; + // //_outputBuffer [position].Empty = false; + // //_outputBuffer [position].Char = ' '; + // //Curses.mvaddch (row, col, '*'); + // } + // } + + // if (Contents [row, col].Runes [0].IsSurrogatePair () && Contents [row, col].Runes [0].GetColumns () < 2) { + // col--; + // } + + // //if (col < Cols && Contents [row, col].Runes [0].GetColumns () > 1) { + // // col++; + // // Curses.mvaddch (row, col, '='); + // //} + //} } + Curses.move (Row, Col); - _window.redrawwin (); + _window.wrefresh (); } public Curses.Window _window; @@ -596,33 +626,27 @@ public override void Init (Action terminalResized) } } + if (!Curses.HasColors) { + throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does."); + } + Curses.raw (); Curses.noecho (); Curses.Window.Standard.keypad (true); + TerminalResized = terminalResized; - StartReportingMouseMoves (); + Curses.StartColor (); + Curses.UseDefaultColors (); CurrentAttribute = MakeColor (Color.White, Color.Black); + InitializeColorSchemes (); - if (Curses.HasColors) { - Curses.StartColor (); - Curses.UseDefaultColors (); - - InitializeColorSchemes (); - } else { - throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does."); - } - - ResizeScreen (); + Curses.CheckWinChange (); ClearContents (); - - } - - public virtual void ResizeScreen () - { - Clip = new Rect (0, 0, Cols, Rows); Curses.refresh (); + + StartReportingMouseMoves (); } public static bool Is_WSL_Platform () diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index d6fbd2481c..2253765275 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -14,1404 +14,1230 @@ using System.Text; namespace Terminal.Gui; -internal class NetWinVTConsole -{ - IntPtr _inputHandle, _outputHandle, _errorHandle; - uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode; - - public NetWinVTConsole() - { - _inputHandle = GetStdHandle(STD_INPUT_HANDLE); - if (!GetConsoleMode(_inputHandle, out uint mode)) - { - throw new ApplicationException($"Failed to get input console mode, error code: {GetLastError()}."); - } - _originalInputConsoleMode = mode; - if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) - { - mode |= ENABLE_VIRTUAL_TERMINAL_INPUT; - if (!SetConsoleMode(_inputHandle, mode)) - { - throw new ApplicationException($"Failed to set input console mode, error code: {GetLastError()}."); - } - } - - _outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); - if (!GetConsoleMode(_outputHandle, out mode)) - { - throw new ApplicationException($"Failed to get output console mode, error code: {GetLastError()}."); - } - _originalOutputConsoleMode = mode; - if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) - { - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; - if (!SetConsoleMode(_outputHandle, mode)) - { - throw new ApplicationException($"Failed to set output console mode, error code: {GetLastError()}."); - } - } - - _errorHandle = GetStdHandle(STD_ERROR_HANDLE); - if (!GetConsoleMode(_errorHandle, out mode)) - { - throw new ApplicationException($"Failed to get error console mode, error code: {GetLastError()}."); - } - _originalErrorConsoleMode = mode; - if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) - { - mode |= DISABLE_NEWLINE_AUTO_RETURN; - if (!SetConsoleMode(_errorHandle, mode)) - { - throw new ApplicationException($"Failed to set error console mode, error code: {GetLastError()}."); - } - } - } - - public void Cleanup() - { - if (!SetConsoleMode(_inputHandle, _originalInputConsoleMode)) - { - throw new ApplicationException($"Failed to restore input console mode, error code: {GetLastError()}."); - } - if (!SetConsoleMode(_outputHandle, _originalOutputConsoleMode)) - { - throw new ApplicationException($"Failed to restore output console mode, error code: {GetLastError()}."); - } - if (!SetConsoleMode(_errorHandle, _originalErrorConsoleMode)) - { - throw new ApplicationException($"Failed to restore error console mode, error code: {GetLastError()}."); - } - } - - const int STD_INPUT_HANDLE = -10; - const int STD_OUTPUT_HANDLE = -11; - const int STD_ERROR_HANDLE = -12; - - // Input modes. - const uint ENABLE_PROCESSED_INPUT = 1; - const uint ENABLE_LINE_INPUT = 2; - const uint ENABLE_ECHO_INPUT = 4; - const uint ENABLE_WINDOW_INPUT = 8; - const uint ENABLE_MOUSE_INPUT = 16; - const uint ENABLE_INSERT_MODE = 32; - const uint ENABLE_QUICK_EDIT_MODE = 64; - const uint ENABLE_EXTENDED_FLAGS = 128; - const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512; - - // Output modes. - const uint ENABLE_PROCESSED_OUTPUT = 1; - const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2; - const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4; - const uint DISABLE_NEWLINE_AUTO_RETURN = 8; - const uint ENABLE_LVB_GRID_WORLDWIDE = 10; - - [DllImport("kernel32.dll", SetLastError = true)] - static extern IntPtr GetStdHandle(int nStdHandle); - - [DllImport("kernel32.dll")] - static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); - - [DllImport("kernel32.dll")] - static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); - - [DllImport("kernel32.dll")] - static extern uint GetLastError(); +internal class NetWinVTConsole { + IntPtr _inputHandle, _outputHandle, _errorHandle; + uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode; + + public NetWinVTConsole () + { + _inputHandle = GetStdHandle (STD_INPUT_HANDLE); + if (!GetConsoleMode (_inputHandle, out uint mode)) { + throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}."); + } + _originalInputConsoleMode = mode; + if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) { + mode |= ENABLE_VIRTUAL_TERMINAL_INPUT; + if (!SetConsoleMode (_inputHandle, mode)) { + throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}."); + } + } + + _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); + if (!GetConsoleMode (_outputHandle, out mode)) { + throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}."); + } + _originalOutputConsoleMode = mode; + if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; + if (!SetConsoleMode (_outputHandle, mode)) { + throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}."); + } + } + + _errorHandle = GetStdHandle (STD_ERROR_HANDLE); + if (!GetConsoleMode (_errorHandle, out mode)) { + throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}."); + } + _originalErrorConsoleMode = mode; + if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) { + mode |= DISABLE_NEWLINE_AUTO_RETURN; + if (!SetConsoleMode (_errorHandle, mode)) { + throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}."); + } + } + } + + public void Cleanup () + { + if (!SetConsoleMode (_inputHandle, _originalInputConsoleMode)) { + throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}."); + } + if (!SetConsoleMode (_outputHandle, _originalOutputConsoleMode)) { + throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}."); + } + if (!SetConsoleMode (_errorHandle, _originalErrorConsoleMode)) { + throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}."); + } + } + + const int STD_INPUT_HANDLE = -10; + const int STD_OUTPUT_HANDLE = -11; + const int STD_ERROR_HANDLE = -12; + + // Input modes. + const uint ENABLE_PROCESSED_INPUT = 1; + const uint ENABLE_LINE_INPUT = 2; + const uint ENABLE_ECHO_INPUT = 4; + const uint ENABLE_WINDOW_INPUT = 8; + const uint ENABLE_MOUSE_INPUT = 16; + const uint ENABLE_INSERT_MODE = 32; + const uint ENABLE_QUICK_EDIT_MODE = 64; + const uint ENABLE_EXTENDED_FLAGS = 128; + const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512; + + // Output modes. + const uint ENABLE_PROCESSED_OUTPUT = 1; + const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2; + const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4; + const uint DISABLE_NEWLINE_AUTO_RETURN = 8; + const uint ENABLE_LVB_GRID_WORLDWIDE = 10; + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern IntPtr GetStdHandle (int nStdHandle); + + [DllImport ("kernel32.dll")] + static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode); + + [DllImport ("kernel32.dll")] + static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode); + + [DllImport ("kernel32.dll")] + static extern uint GetLastError (); } -internal class NetEvents -{ - ManualResetEventSlim _inputReady = new ManualResetEventSlim(false); - ManualResetEventSlim _waitForStart = new ManualResetEventSlim(false); - ManualResetEventSlim _winChange = new ManualResetEventSlim(false); - Queue _inputResultQueue = new Queue(); - ConsoleDriver _consoleDriver; - volatile ConsoleKeyInfo[] _cki = null; - volatile static bool _isEscSeq; - bool _stopTasks; +internal class NetEvents { + ManualResetEventSlim _inputReady = new ManualResetEventSlim (false); + ManualResetEventSlim _waitForStart = new ManualResetEventSlim (false); + ManualResetEventSlim _winChange = new ManualResetEventSlim (false); + Queue _inputResultQueue = new Queue (); + ConsoleDriver _consoleDriver; + volatile ConsoleKeyInfo [] _cki = null; + volatile static bool _isEscSeq; + bool _stopTasks; #if PROCESS_REQUEST bool _neededProcessRequest; #endif - public EscSeqRequests EscSeqRequests { get; } = new EscSeqRequests(); - - public NetEvents(ConsoleDriver consoleDriver) - { - _consoleDriver = consoleDriver ?? throw new ArgumentNullException(nameof(consoleDriver)); - Task.Run(ProcessInputResultQueue); - Task.Run(CheckWindowSizeChange); - } - - internal void StopTasks() - { - _stopTasks = true; - } - - public InputResult? ReadConsoleInput() - { - while (true) - { - if (_stopTasks) - { - return null; - } - _waitForStart.Set(); - _winChange.Set(); - - if (_inputResultQueue.Count == 0) - { - _inputReady.Wait(); - _inputReady.Reset(); - } + public EscSeqRequests EscSeqRequests { get; } = new EscSeqRequests (); + + public NetEvents (ConsoleDriver consoleDriver) + { + _consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver)); + Task.Run (ProcessInputResultQueue); + Task.Run (CheckWindowSizeChange); + } + + internal void StopTasks () + { + _stopTasks = true; + } + + public InputResult? ReadConsoleInput () + { + while (true) { + if (_stopTasks) { + return null; + } + _waitForStart.Set (); + _winChange.Set (); + + if (_inputResultQueue.Count == 0) { + _inputReady.Wait (); + _inputReady.Reset (); + } #if PROCESS_REQUEST _neededProcessRequest = false; #endif - if (_inputResultQueue.Count > 0) - { - return _inputResultQueue.Dequeue(); - } - } - } - - void ProcessInputResultQueue() - { - while (true) - { - _waitForStart.Wait(); - _waitForStart.Reset(); - - if (_inputResultQueue.Count == 0) - { - ConsoleKey key = 0; - ConsoleModifiers mod = 0; - ConsoleKeyInfo newConsoleKeyInfo = default; - - while (true) - { - ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(true); - if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq) - || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) - { - if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) - { - _cki = EscSeqUtils.ResizeArray(new ConsoleKeyInfo((char)Key.Esc, 0, - false, false, false), _cki); - } - _isEscSeq = true; - newConsoleKeyInfo = consoleKeyInfo; - _cki = EscSeqUtils.ResizeArray(consoleKeyInfo, _cki); - if (Console.KeyAvailable) continue; - ProcessRequestResponse(ref newConsoleKeyInfo, ref key, _cki, ref mod); - _cki = null; - _isEscSeq = false; - break; - } - else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) - { - if (_cki != null) - { - ProcessRequestResponse(ref newConsoleKeyInfo, ref key, _cki, ref mod); - _cki = null; - } - break; - } - else - { - _inputResultQueue.Enqueue(new InputResult - { - EventType = EventType.Key, - ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo(consoleKeyInfo) - }); - _isEscSeq = false; - break; - } - } - } - - _inputReady.Set(); - } - } - - void CheckWindowSizeChange() - { - void RequestWindowSize() - { - while (true) - { - // Wait for a while then check if screen has changed sizes - Task.Delay(500).Wait(); - - if (_stopTasks) - { - return; - } - int buffHeight, buffWidth; - if (((NetDriver)_consoleDriver).IsWinPlatform) - { - buffHeight = Math.Max(Console.BufferHeight, 0); - buffWidth = Math.Max(Console.BufferWidth, 0); - } - else - { - buffHeight = _consoleDriver.Rows; - buffWidth = _consoleDriver.Cols; - } - if (EnqueueWindowSizeEvent( - Math.Max(Console.WindowHeight, 0), - Math.Max(Console.WindowWidth, 0), - buffHeight, - buffWidth)) - { - - return; - } - } - } - - while (true) - { - if (_stopTasks) - { - return; - } - _winChange.Wait(); - _winChange.Reset(); - RequestWindowSize(); - _inputReady.Set(); - } - } - - /// - /// Enqueue a window size event if the window size has changed. - /// - /// - /// - /// - /// - /// - bool EnqueueWindowSizeEvent(int winHeight, int winWidth, int buffHeight, int buffWidth) - { - if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) return false; - var w = Math.Max(winWidth, 0); - var h = Math.Max(winHeight, 0); - _inputResultQueue.Enqueue(new InputResult() - { - EventType = EventType.WindowSize, - WindowSizeEvent = new WindowSizeEvent() - { - Size = new Size(w, h) - } - }); - return true; - } - - // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event) - void ProcessRequestResponse(ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo[] cki, ref ConsoleModifiers mod) - { - // isMouse is true if it's CSI<, false otherwise - EscSeqUtils.DecodeEscSeq(EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod, - out var c1Control, out var code, out var values, out var terminating, - out var isMouse, out var mouseFlags, - out var pos, out var isReq, - (f, p) => HandleMouseEvent(MapMouseFlags(f), p)); - - if (isMouse) - { - foreach (var mf in mouseFlags) - { - HandleMouseEvent(MapMouseFlags(mf), pos); - } - return; - } - else if (isReq) - { - HandleRequestResponseEvent(c1Control, code, values, terminating); - return; - } - HandleKeyboardEvent(newConsoleKeyInfo); - } - - MouseButtonState MapMouseFlags(MouseFlags mouseFlags) - { - MouseButtonState mbs = default; - foreach (var flag in Enum.GetValues(mouseFlags.GetType())) - { - if (mouseFlags.HasFlag((MouseFlags)flag)) - { - switch (flag) - { - case MouseFlags.Button1Pressed: - mbs |= MouseButtonState.Button1Pressed; - break; - case MouseFlags.Button1Released: - mbs |= MouseButtonState.Button1Released; - break; - case MouseFlags.Button1Clicked: - mbs |= MouseButtonState.Button1Clicked; - break; - case MouseFlags.Button1DoubleClicked: - mbs |= MouseButtonState.Button1DoubleClicked; - break; - case MouseFlags.Button1TripleClicked: - mbs |= MouseButtonState.Button1TripleClicked; - break; - case MouseFlags.Button2Pressed: - mbs |= MouseButtonState.Button2Pressed; - break; - case MouseFlags.Button2Released: - mbs |= MouseButtonState.Button2Released; - break; - case MouseFlags.Button2Clicked: - mbs |= MouseButtonState.Button2Clicked; - break; - case MouseFlags.Button2DoubleClicked: - mbs |= MouseButtonState.Button2DoubleClicked; - break; - case MouseFlags.Button2TripleClicked: - mbs |= MouseButtonState.Button2TripleClicked; - break; - case MouseFlags.Button3Pressed: - mbs |= MouseButtonState.Button3Pressed; - break; - case MouseFlags.Button3Released: - mbs |= MouseButtonState.Button3Released; - break; - case MouseFlags.Button3Clicked: - mbs |= MouseButtonState.Button3Clicked; - break; - case MouseFlags.Button3DoubleClicked: - mbs |= MouseButtonState.Button3DoubleClicked; - break; - case MouseFlags.Button3TripleClicked: - mbs |= MouseButtonState.Button3TripleClicked; - break; - case MouseFlags.WheeledUp: - mbs |= MouseButtonState.ButtonWheeledUp; - break; - case MouseFlags.WheeledDown: - mbs |= MouseButtonState.ButtonWheeledDown; - break; - case MouseFlags.WheeledLeft: - mbs |= MouseButtonState.ButtonWheeledLeft; - break; - case MouseFlags.WheeledRight: - mbs |= MouseButtonState.ButtonWheeledRight; - break; - case MouseFlags.Button4Pressed: - mbs |= MouseButtonState.Button4Pressed; - break; - case MouseFlags.Button4Released: - mbs |= MouseButtonState.Button4Released; - break; - case MouseFlags.Button4Clicked: - mbs |= MouseButtonState.Button4Clicked; - break; - case MouseFlags.Button4DoubleClicked: - mbs |= MouseButtonState.Button4DoubleClicked; - break; - case MouseFlags.Button4TripleClicked: - mbs |= MouseButtonState.Button4TripleClicked; - break; - case MouseFlags.ButtonShift: - mbs |= MouseButtonState.ButtonShift; - break; - case MouseFlags.ButtonCtrl: - mbs |= MouseButtonState.ButtonCtrl; - break; - case MouseFlags.ButtonAlt: - mbs |= MouseButtonState.ButtonAlt; - break; - case MouseFlags.ReportMousePosition: - mbs |= MouseButtonState.ReportMousePosition; - break; - case MouseFlags.AllEvents: - mbs |= MouseButtonState.AllEvents; - break; - } - } - } - return mbs; - } - - Point _lastCursorPosition; - - void HandleRequestResponseEvent(string c1Control, string code, string[] values, string terminating) - { - switch (terminating) - { - // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed. - case EscSeqUtils.CSI_RequestCursorPositionReport_Terminator: - Point point = new Point - { - X = int.Parse(values[1]) - 1, - Y = int.Parse(values[0]) - 1 - }; - if (_lastCursorPosition.Y != point.Y) - { - _lastCursorPosition = point; - var eventType = EventType.WindowPosition; - var winPositionEv = new WindowPositionEvent() - { - CursorPosition = point - }; - _inputResultQueue.Enqueue(new InputResult() - { - EventType = eventType, - WindowPositionEvent = winPositionEv - }); - } - else - { - return; - } - break; - - case EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator: - switch (values[0]) - { - case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue: - EnqueueWindowSizeEvent( - Math.Max(int.Parse(values[1]), 0), - Math.Max(int.Parse(values[2]), 0), - Math.Max(int.Parse(values[1]), 0), - Math.Max(int.Parse(values[2]), 0)); - break; - default: - EnqueueRequestResponseEvent(c1Control, code, values, terminating); - break; - } - break; - default: - EnqueueRequestResponseEvent(c1Control, code, values, terminating); - break; - } - - _inputReady.Set(); - } - - void EnqueueRequestResponseEvent(string c1Control, string code, string[] values, string terminating) - { - EventType eventType = EventType.RequestResponse; - var requestRespEv = new RequestResponseEvent() - { - ResultTuple = (c1Control, code, values, terminating) - }; - _inputResultQueue.Enqueue(new InputResult() - { - EventType = eventType, - RequestResponseEvent = requestRespEv - }); - } - - void HandleMouseEvent(MouseButtonState buttonState, Point pos) - { - MouseEvent mouseEvent = new MouseEvent() - { - Position = pos, - ButtonState = buttonState, - }; - - _inputResultQueue.Enqueue(new InputResult() - { - EventType = EventType.Mouse, - MouseEvent = mouseEvent - }); - - _inputReady.Set(); - } - - public enum EventType - { - Key = 1, - Mouse = 2, - WindowSize = 3, - WindowPosition = 4, - RequestResponse = 5 - } - - [Flags] - public enum MouseButtonState - { - Button1Pressed = 0x1, - Button1Released = 0x2, - Button1Clicked = 0x4, - Button1DoubleClicked = 0x8, - Button1TripleClicked = 0x10, - Button2Pressed = 0x20, - Button2Released = 0x40, - Button2Clicked = 0x80, - Button2DoubleClicked = 0x100, - Button2TripleClicked = 0x200, - Button3Pressed = 0x400, - Button3Released = 0x800, - Button3Clicked = 0x1000, - Button3DoubleClicked = 0x2000, - Button3TripleClicked = 0x4000, - ButtonWheeledUp = 0x8000, - ButtonWheeledDown = 0x10000, - ButtonWheeledLeft = 0x20000, - ButtonWheeledRight = 0x40000, - Button4Pressed = 0x80000, - Button4Released = 0x100000, - Button4Clicked = 0x200000, - Button4DoubleClicked = 0x400000, - Button4TripleClicked = 0x800000, - ButtonShift = 0x1000000, - ButtonCtrl = 0x2000000, - ButtonAlt = 0x4000000, - ReportMousePosition = 0x8000000, - AllEvents = -1 - } - - public struct MouseEvent - { - public Point Position; - public MouseButtonState ButtonState; - } - - public struct WindowSizeEvent - { - public Size Size; - } - - public struct WindowPositionEvent - { - public int Top; - public int Left; - public Point CursorPosition; - } - - public struct RequestResponseEvent - { - public (string c1Control, string code, string[] values, string terminating) ResultTuple; - } - - public struct InputResult - { - public EventType EventType; - public ConsoleKeyInfo ConsoleKeyInfo; - public MouseEvent MouseEvent; - public WindowSizeEvent WindowSizeEvent; - public WindowPositionEvent WindowPositionEvent; - public RequestResponseEvent RequestResponseEvent; - } - - void HandleKeyboardEvent(ConsoleKeyInfo cki) - { - InputResult inputResult = new InputResult - { - EventType = EventType.Key, - ConsoleKeyInfo = cki - }; - - _inputResultQueue.Enqueue(inputResult); - } + if (_inputResultQueue.Count > 0) { + return _inputResultQueue.Dequeue (); + } + } + } + + void ProcessInputResultQueue () + { + while (true) { + _waitForStart.Wait (); + _waitForStart.Reset (); + + if (_inputResultQueue.Count == 0) { + ConsoleKey key = 0; + ConsoleModifiers mod = 0; + ConsoleKeyInfo newConsoleKeyInfo = default; + + while (true) { + ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true); + if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq) + || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) { + if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) { + _cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, + false, false, false), _cki); + } + _isEscSeq = true; + newConsoleKeyInfo = consoleKeyInfo; + _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki); + if (Console.KeyAvailable) continue; + ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; + _isEscSeq = false; + break; + } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) { + if (_cki != null) { + ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod); + _cki = null; + } + break; + } else { + _inputResultQueue.Enqueue (new InputResult { + EventType = EventType.Key, + ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo) + }); + _isEscSeq = false; + break; + } + } + } + + _inputReady.Set (); + } + } + + void CheckWindowSizeChange () + { + void RequestWindowSize () + { + while (true) { + // Wait for a while then check if screen has changed sizes + Task.Delay (500).Wait (); + + if (_stopTasks) { + return; + } + int buffHeight, buffWidth; + if (((NetDriver)_consoleDriver).IsWinPlatform) { + buffHeight = Math.Max (Console.BufferHeight, 0); + buffWidth = Math.Max (Console.BufferWidth, 0); + } else { + buffHeight = _consoleDriver.Rows; + buffWidth = _consoleDriver.Cols; + } + if (EnqueueWindowSizeEvent ( + Math.Max (Console.WindowHeight, 0), + Math.Max (Console.WindowWidth, 0), + buffHeight, + buffWidth)) { + + return; + } + } + } + + while (true) { + if (_stopTasks) { + return; + } + _winChange.Wait (); + _winChange.Reset (); + RequestWindowSize (); + _inputReady.Set (); + } + } + + /// + /// Enqueue a window size event if the window size has changed. + /// + /// + /// + /// + /// + /// + bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int buffWidth) + { + if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) return false; + var w = Math.Max (winWidth, 0); + var h = Math.Max (winHeight, 0); + _inputResultQueue.Enqueue (new InputResult () { + EventType = EventType.WindowSize, + WindowSizeEvent = new WindowSizeEvent () { + Size = new Size (w, h) + } + }); + return true; + } + + // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event) + void ProcessRequestResponse (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) + { + // isMouse is true if it's CSI<, false otherwise + EscSeqUtils.DecodeEscSeq (EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod, + out var c1Control, out var code, out var values, out var terminating, + out var isMouse, out var mouseFlags, + out var pos, out var isReq, + (f, p) => HandleMouseEvent (MapMouseFlags (f), p)); + + if (isMouse) { + foreach (var mf in mouseFlags) { + HandleMouseEvent (MapMouseFlags (mf), pos); + } + return; + } else if (isReq) { + HandleRequestResponseEvent (c1Control, code, values, terminating); + return; + } + HandleKeyboardEvent (newConsoleKeyInfo); + } + + MouseButtonState MapMouseFlags (MouseFlags mouseFlags) + { + MouseButtonState mbs = default; + foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) { + if (mouseFlags.HasFlag ((MouseFlags)flag)) { + switch (flag) { + case MouseFlags.Button1Pressed: + mbs |= MouseButtonState.Button1Pressed; + break; + case MouseFlags.Button1Released: + mbs |= MouseButtonState.Button1Released; + break; + case MouseFlags.Button1Clicked: + mbs |= MouseButtonState.Button1Clicked; + break; + case MouseFlags.Button1DoubleClicked: + mbs |= MouseButtonState.Button1DoubleClicked; + break; + case MouseFlags.Button1TripleClicked: + mbs |= MouseButtonState.Button1TripleClicked; + break; + case MouseFlags.Button2Pressed: + mbs |= MouseButtonState.Button2Pressed; + break; + case MouseFlags.Button2Released: + mbs |= MouseButtonState.Button2Released; + break; + case MouseFlags.Button2Clicked: + mbs |= MouseButtonState.Button2Clicked; + break; + case MouseFlags.Button2DoubleClicked: + mbs |= MouseButtonState.Button2DoubleClicked; + break; + case MouseFlags.Button2TripleClicked: + mbs |= MouseButtonState.Button2TripleClicked; + break; + case MouseFlags.Button3Pressed: + mbs |= MouseButtonState.Button3Pressed; + break; + case MouseFlags.Button3Released: + mbs |= MouseButtonState.Button3Released; + break; + case MouseFlags.Button3Clicked: + mbs |= MouseButtonState.Button3Clicked; + break; + case MouseFlags.Button3DoubleClicked: + mbs |= MouseButtonState.Button3DoubleClicked; + break; + case MouseFlags.Button3TripleClicked: + mbs |= MouseButtonState.Button3TripleClicked; + break; + case MouseFlags.WheeledUp: + mbs |= MouseButtonState.ButtonWheeledUp; + break; + case MouseFlags.WheeledDown: + mbs |= MouseButtonState.ButtonWheeledDown; + break; + case MouseFlags.WheeledLeft: + mbs |= MouseButtonState.ButtonWheeledLeft; + break; + case MouseFlags.WheeledRight: + mbs |= MouseButtonState.ButtonWheeledRight; + break; + case MouseFlags.Button4Pressed: + mbs |= MouseButtonState.Button4Pressed; + break; + case MouseFlags.Button4Released: + mbs |= MouseButtonState.Button4Released; + break; + case MouseFlags.Button4Clicked: + mbs |= MouseButtonState.Button4Clicked; + break; + case MouseFlags.Button4DoubleClicked: + mbs |= MouseButtonState.Button4DoubleClicked; + break; + case MouseFlags.Button4TripleClicked: + mbs |= MouseButtonState.Button4TripleClicked; + break; + case MouseFlags.ButtonShift: + mbs |= MouseButtonState.ButtonShift; + break; + case MouseFlags.ButtonCtrl: + mbs |= MouseButtonState.ButtonCtrl; + break; + case MouseFlags.ButtonAlt: + mbs |= MouseButtonState.ButtonAlt; + break; + case MouseFlags.ReportMousePosition: + mbs |= MouseButtonState.ReportMousePosition; + break; + case MouseFlags.AllEvents: + mbs |= MouseButtonState.AllEvents; + break; + } + } + } + return mbs; + } + + Point _lastCursorPosition; + + void HandleRequestResponseEvent (string c1Control, string code, string [] values, string terminating) + { + switch (terminating) { + // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed. + case EscSeqUtils.CSI_RequestCursorPositionReport_Terminator: + Point point = new Point { + X = int.Parse (values [1]) - 1, + Y = int.Parse (values [0]) - 1 + }; + if (_lastCursorPosition.Y != point.Y) { + _lastCursorPosition = point; + var eventType = EventType.WindowPosition; + var winPositionEv = new WindowPositionEvent () { + CursorPosition = point + }; + _inputResultQueue.Enqueue (new InputResult () { + EventType = eventType, + WindowPositionEvent = winPositionEv + }); + } else { + return; + } + break; + + case EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator: + switch (values [0]) { + case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue: + EnqueueWindowSizeEvent ( + Math.Max (int.Parse (values [1]), 0), + Math.Max (int.Parse (values [2]), 0), + Math.Max (int.Parse (values [1]), 0), + Math.Max (int.Parse (values [2]), 0)); + break; + default: + EnqueueRequestResponseEvent (c1Control, code, values, terminating); + break; + } + break; + default: + EnqueueRequestResponseEvent (c1Control, code, values, terminating); + break; + } + + _inputReady.Set (); + } + + void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating) + { + EventType eventType = EventType.RequestResponse; + var requestRespEv = new RequestResponseEvent () { + ResultTuple = (c1Control, code, values, terminating) + }; + _inputResultQueue.Enqueue (new InputResult () { + EventType = eventType, + RequestResponseEvent = requestRespEv + }); + } + + void HandleMouseEvent (MouseButtonState buttonState, Point pos) + { + MouseEvent mouseEvent = new MouseEvent () { + Position = pos, + ButtonState = buttonState, + }; + + _inputResultQueue.Enqueue (new InputResult () { + EventType = EventType.Mouse, + MouseEvent = mouseEvent + }); + + _inputReady.Set (); + } + + public enum EventType { + Key = 1, + Mouse = 2, + WindowSize = 3, + WindowPosition = 4, + RequestResponse = 5 + } + + [Flags] + public enum MouseButtonState { + Button1Pressed = 0x1, + Button1Released = 0x2, + Button1Clicked = 0x4, + Button1DoubleClicked = 0x8, + Button1TripleClicked = 0x10, + Button2Pressed = 0x20, + Button2Released = 0x40, + Button2Clicked = 0x80, + Button2DoubleClicked = 0x100, + Button2TripleClicked = 0x200, + Button3Pressed = 0x400, + Button3Released = 0x800, + Button3Clicked = 0x1000, + Button3DoubleClicked = 0x2000, + Button3TripleClicked = 0x4000, + ButtonWheeledUp = 0x8000, + ButtonWheeledDown = 0x10000, + ButtonWheeledLeft = 0x20000, + ButtonWheeledRight = 0x40000, + Button4Pressed = 0x80000, + Button4Released = 0x100000, + Button4Clicked = 0x200000, + Button4DoubleClicked = 0x400000, + Button4TripleClicked = 0x800000, + ButtonShift = 0x1000000, + ButtonCtrl = 0x2000000, + ButtonAlt = 0x4000000, + ReportMousePosition = 0x8000000, + AllEvents = -1 + } + + public struct MouseEvent { + public Point Position; + public MouseButtonState ButtonState; + } + + public struct WindowSizeEvent { + public Size Size; + } + + public struct WindowPositionEvent { + public int Top; + public int Left; + public Point CursorPosition; + } + + public struct RequestResponseEvent { + public (string c1Control, string code, string [] values, string terminating) ResultTuple; + } + + public struct InputResult { + public EventType EventType; + public ConsoleKeyInfo ConsoleKeyInfo; + public MouseEvent MouseEvent; + public WindowSizeEvent WindowSizeEvent; + public WindowPositionEvent WindowPositionEvent; + public RequestResponseEvent RequestResponseEvent; + } + + void HandleKeyboardEvent (ConsoleKeyInfo cki) + { + InputResult inputResult = new InputResult { + EventType = EventType.Key, + ConsoleKeyInfo = cki + }; + + _inputResultQueue.Enqueue (inputResult); + } } -internal class NetDriver : ConsoleDriver -{ - const int COLOR_BLACK = 30; - const int COLOR_RED = 31; - const int COLOR_GREEN = 32; - const int COLOR_YELLOW = 33; - const int COLOR_BLUE = 34; - const int COLOR_MAGENTA = 35; - const int COLOR_CYAN = 36; - const int COLOR_WHITE = 37; - const int COLOR_BRIGHT_BLACK = 90; - const int COLOR_BRIGHT_RED = 91; - const int COLOR_BRIGHT_GREEN = 92; - const int COLOR_BRIGHT_YELLOW = 93; - const int COLOR_BRIGHT_BLUE = 94; - const int COLOR_BRIGHT_MAGENTA = 95; - const int COLOR_BRIGHT_CYAN = 96; - const int COLOR_BRIGHT_WHITE = 97; - - public NetWinVTConsole NetWinConsole { get; private set; } - public bool IsWinPlatform { get; private set; } - - int _largestBufferHeight; - - public NetDriver() - { - } - - public override void End() - { - _mainLoop._netEvents.StopTasks(); - - if (IsWinPlatform) - { - NetWinConsole.Cleanup(); - } - - StopReportingMouseMoves(); - Console.ResetColor(); - - //Disable alternative screen buffer. - Console.Out.Write(EscSeqUtils.CSI_RestoreCursorAndActivateAltBufferWithBackscroll); - - //Set cursor key to cursor. - Console.Out.Write(EscSeqUtils.CSI_ShowCursor); - - Console.Out.Close(); - } - - public override void Init(Action terminalResized) - { - var p = Environment.OSVersion.Platform; - if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) - { - IsWinPlatform = true; - try - { - NetWinConsole = new NetWinVTConsole(); - } - catch (ApplicationException) - { - // Likely running as a unit test, or in a non-interactive session. - } - } - if (IsWinPlatform) - { - Clipboard = new WindowsClipboard(); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - Clipboard = new MacOSXClipboard(); - } - else - { - if (CursesDriver.Is_WSL_Platform()) - { - Clipboard = new WSLClipboard(); - } - else - { - Clipboard = new CursesClipboard(); - } - } - - TerminalResized = terminalResized; - - if (NetWinConsole != null) - { - //Enable alternative screen buffer. - Console.Out.Write(EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll); - - //Set cursor key to application. - Console.Out.Write(EscSeqUtils.CSI_HideCursor); - - Console.TreatControlCAsInput = true; - - Cols = Console.WindowWidth; - Rows = Console.WindowHeight; - } - else - { - // Simluate - Cols = 80; - Rows = 25; - _largestBufferHeight = Rows; - } - - ResizeScreen(); - ClearContents(); - CurrentAttribute = MakeColor(Color.White, Color.Black); - InitializeColorSchemes(); - - StartReportingMouseMoves(); - } - - public virtual void ResizeScreen() - { - if (NetWinConsole == null) - { - return; - } - - if (Console.WindowHeight > 0) - { - // Not supported on Unix. - if (IsWinPlatform) - { - // Can raise an exception while is still resizing. - try - { +internal class NetDriver : ConsoleDriver { + const int COLOR_BLACK = 30; + const int COLOR_RED = 31; + const int COLOR_GREEN = 32; + const int COLOR_YELLOW = 33; + const int COLOR_BLUE = 34; + const int COLOR_MAGENTA = 35; + const int COLOR_CYAN = 36; + const int COLOR_WHITE = 37; + const int COLOR_BRIGHT_BLACK = 90; + const int COLOR_BRIGHT_RED = 91; + const int COLOR_BRIGHT_GREEN = 92; + const int COLOR_BRIGHT_YELLOW = 93; + const int COLOR_BRIGHT_BLUE = 94; + const int COLOR_BRIGHT_MAGENTA = 95; + const int COLOR_BRIGHT_CYAN = 96; + const int COLOR_BRIGHT_WHITE = 97; + + public NetWinVTConsole NetWinConsole { get; private set; } + public bool IsWinPlatform { get; private set; } + + int _largestBufferHeight; + + public NetDriver () + { + } + + public override void End () + { + _mainLoop._netEvents.StopTasks (); + + if (IsWinPlatform) { + NetWinConsole.Cleanup (); + } + + StopReportingMouseMoves (); + Console.ResetColor (); + + //Disable alternative screen buffer. + Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndActivateAltBufferWithBackscroll); + + //Set cursor key to cursor. + Console.Out.Write (EscSeqUtils.CSI_ShowCursor); + + Console.Out.Close (); + } + + public override void Init (Action terminalResized) + { + var p = Environment.OSVersion.Platform; + if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) { + IsWinPlatform = true; + try { + NetWinConsole = new NetWinVTConsole (); + } catch (ApplicationException) { + // Likely running as a unit test, or in a non-interactive session. + } + } + if (IsWinPlatform) { + Clipboard = new WindowsClipboard (); + } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { + Clipboard = new MacOSXClipboard (); + } else { + if (CursesDriver.Is_WSL_Platform ()) { + Clipboard = new WSLClipboard (); + } else { + Clipboard = new CursesClipboard (); + } + } + + TerminalResized = terminalResized; + + if (NetWinConsole != null) { + //Enable alternative screen buffer. + Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll); + + //Set cursor key to application. + Console.Out.Write (EscSeqUtils.CSI_HideCursor); + + Console.TreatControlCAsInput = true; + + Cols = Console.WindowWidth; + Rows = Console.WindowHeight; + } else { + // Simluate + Cols = 80; + Rows = 25; + _largestBufferHeight = Rows; + } + + ResizeScreen (); + ClearContents (); + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitializeColorSchemes (); + + StartReportingMouseMoves (); + } + + public virtual void ResizeScreen () + { + if (NetWinConsole == null) { + return; + } + + if (Console.WindowHeight > 0) { + // Not supported on Unix. + if (IsWinPlatform) { + // Can raise an exception while is still resizing. + try { #pragma warning disable CA1416 - Console.CursorTop = 0; - Console.CursorLeft = 0; - Console.WindowTop = 0; - Console.WindowLeft = 0; - if (Console.WindowHeight > Rows) - { - Console.SetWindowSize(Cols, Rows); - } - Console.SetBufferSize(Cols, Rows); + Console.CursorTop = 0; + Console.CursorLeft = 0; + Console.WindowTop = 0; + Console.WindowLeft = 0; + if (Console.WindowHeight > Rows) { + Console.SetWindowSize (Cols, Rows); + } + Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 - } - catch (System.IO.IOException) - { - Clip = new Rect(0, 0, Cols, Rows); - } - catch (ArgumentOutOfRangeException) - { - Clip = new Rect(0, 0, Cols, Rows); - } - } - else - { - Console.Out.Write(EscSeqUtils.CSI_SetTerminalWindowSize(Rows, Cols)); - } - } - - Clip = new Rect(0, 0, Cols, Rows); - } - - public override void Refresh() - { - UpdateScreen(); - UpdateCursor(); - } - - public override void UpdateScreen() - { - if (_winSizeChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols || Rows != Console.WindowHeight) - { - return; - } - - var top = 0; - var left = 0; - var rows = Rows; - var cols = Cols; - System.Text.StringBuilder output = new System.Text.StringBuilder(); - Attribute redrawAttr = new Attribute(); - var lastCol = -1; - - //GetCursorVisibility (out CursorVisibility savedVisibitity); - //SetCursorVisibility (CursorVisibility.Invisible); - - for (var row = top; row < rows; row++) - { - if (Console.WindowHeight < 1) - { - return; - } - if (!_dirtyLines[row]) - { - continue; - } - if (!SetCursorPosition(0, row)) - { - return; - } - _dirtyLines[row] = false; - output.Clear(); - for (var col = left; col < cols; col++) - { - lastCol = -1; - var outputWidth = 0; - for (; col < cols; col++) - { - if (!Contents[row, col].IsDirty) - { - if (output.Length > 0) - { - WriteToConsole(output, ref lastCol, row, ref outputWidth); - } - else if (lastCol == -1) - { - lastCol = col; - } - if (lastCol + 1 < cols) - lastCol++; - continue; - } - - if (lastCol == -1) - { - lastCol = col; - } - - Attribute attr = Contents[row, col].Attribute.Value; - // Performance: Only send the escape sequence if the attribute has changed. - if (attr != redrawAttr) - { - redrawAttr = attr; - output.Append(EscSeqUtils.CSI_SetGraphicsRendition( - MapColors((ConsoleColor)attr.Background, false), MapColors((ConsoleColor)attr.Foreground, true))); - } - outputWidth++; - var rune = (Rune)Contents[row, col].Runes[0]; - output.Append(rune.ToString()); - if (rune.IsSurrogatePair() && rune.GetColumns() < 2) - { - WriteToConsole(output, ref lastCol, row, ref outputWidth); - Console.CursorLeft--; - } - Contents[row, col].IsDirty = false; - } - } - if (output.Length > 0) - { - SetCursorPosition(lastCol, row); - Console.Write(output); - } - } - SetCursorPosition(0, 0); - - //SetCursorVisibility (savedVisibitity); - - void WriteToConsole(StringBuilder output, ref int lastCol, int row, ref int outputWidth) - { - SetCursorPosition(lastCol, row); - Console.Write(output); - output.Clear(); - lastCol += outputWidth; - outputWidth = 0; - } - } - - #region Color Handling - - // Cache the list of ConsoleColor values. - private static readonly HashSet ConsoleColorValues = new HashSet( - Enum.GetValues(typeof(ConsoleColor)).OfType().Select(c => (int)c) - ); - - // Dictionary for mapping ConsoleColor values to the values used by System.Net.Console. - private static Dictionary colorMap = new Dictionary { - { ConsoleColor.Black, COLOR_BLACK }, - { ConsoleColor.DarkBlue, COLOR_BLUE }, - { ConsoleColor.DarkGreen, COLOR_GREEN }, - { ConsoleColor.DarkCyan, COLOR_CYAN }, - { ConsoleColor.DarkRed, COLOR_RED }, - { ConsoleColor.DarkMagenta, COLOR_MAGENTA }, - { ConsoleColor.DarkYellow, COLOR_YELLOW }, - { ConsoleColor.Gray, COLOR_WHITE }, - { ConsoleColor.DarkGray, COLOR_BRIGHT_BLACK }, - { ConsoleColor.Blue, COLOR_BRIGHT_BLUE }, - { ConsoleColor.Green, COLOR_BRIGHT_GREEN }, - { ConsoleColor.Cyan, COLOR_BRIGHT_CYAN }, - { ConsoleColor.Red, COLOR_BRIGHT_RED }, - { ConsoleColor.Magenta, COLOR_BRIGHT_MAGENTA }, - { ConsoleColor.Yellow, COLOR_BRIGHT_YELLOW }, - { ConsoleColor.White, COLOR_BRIGHT_WHITE } + } catch (System.IO.IOException) { + Clip = new Rect (0, 0, Cols, Rows); + } catch (ArgumentOutOfRangeException) { + Clip = new Rect (0, 0, Cols, Rows); + } + } else { + Console.Out.Write (EscSeqUtils.CSI_SetTerminalWindowSize (Rows, Cols)); + } + } + + Clip = new Rect (0, 0, Cols, Rows); + } + + public override void Refresh () + { + UpdateScreen (); + UpdateCursor (); + } + + public override void UpdateScreen () + { + if (_winSizeChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols || Rows != Console.WindowHeight) { + return; + } + + var top = 0; + var left = 0; + var rows = Rows; + var cols = Cols; + System.Text.StringBuilder output = new System.Text.StringBuilder (); + Attribute redrawAttr = new Attribute (); + var lastCol = -1; + + //GetCursorVisibility (out CursorVisibility savedVisibitity); + //SetCursorVisibility (CursorVisibility.Invisible); + + for (var row = top; row < rows; row++) { + if (Console.WindowHeight < 1) { + return; + } + if (!_dirtyLines [row]) { + continue; + } + if (!SetCursorPosition (0, row)) { + return; + } + _dirtyLines [row] = false; + output.Clear (); + for (var col = left; col < cols; col++) { + lastCol = -1; + var outputWidth = 0; + for (; col < cols; col++) { + if (!Contents [row, col].IsDirty) { + if (output.Length > 0) { + WriteToConsole (output, ref lastCol, row, ref outputWidth); + } else if (lastCol == -1) { + lastCol = col; + } + if (lastCol + 1 < cols) + lastCol++; + continue; + } + + if (lastCol == -1) { + lastCol = col; + } + + Attribute attr = Contents [row, col].Attribute.Value; + // Performance: Only send the escape sequence if the attribute has changed. + if (attr != redrawAttr) { + redrawAttr = attr; + output.Append (EscSeqUtils.CSI_SetGraphicsRendition ( + MapColors ((ConsoleColor)attr.Background, false), MapColors ((ConsoleColor)attr.Foreground, true))); + } + outputWidth++; + var rune = (Rune)Contents [row, col].Runes [0]; + output.Append (rune.ToString ()); + if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { + WriteToConsole (output, ref lastCol, row, ref outputWidth); + Console.CursorLeft--; + } + Contents [row, col].IsDirty = false; + } + } + if (output.Length > 0) { + SetCursorPosition (lastCol, row); + Console.Write (output); + } + } + SetCursorPosition (0, 0); + + //SetCursorVisibility (savedVisibitity); + + void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth) + { + SetCursorPosition (lastCol, row); + Console.Write (output); + output.Clear (); + lastCol += outputWidth; + outputWidth = 0; + } + } + + #region Color Handling + + // Cache the list of ConsoleColor values. + private static readonly HashSet ConsoleColorValues = new HashSet ( + Enum.GetValues (typeof (ConsoleColor)).OfType ().Select (c => (int)c) + ); + + // Dictionary for mapping ConsoleColor values to the values used by System.Net.Console. + private static Dictionary colorMap = new Dictionary { + { ConsoleColor.Black, COLOR_BLACK }, + { ConsoleColor.DarkBlue, COLOR_BLUE }, + { ConsoleColor.DarkGreen, COLOR_GREEN }, + { ConsoleColor.DarkCyan, COLOR_CYAN }, + { ConsoleColor.DarkRed, COLOR_RED }, + { ConsoleColor.DarkMagenta, COLOR_MAGENTA }, + { ConsoleColor.DarkYellow, COLOR_YELLOW }, + { ConsoleColor.Gray, COLOR_WHITE }, + { ConsoleColor.DarkGray, COLOR_BRIGHT_BLACK }, + { ConsoleColor.Blue, COLOR_BRIGHT_BLUE }, + { ConsoleColor.Green, COLOR_BRIGHT_GREEN }, + { ConsoleColor.Cyan, COLOR_BRIGHT_CYAN }, + { ConsoleColor.Red, COLOR_BRIGHT_RED }, + { ConsoleColor.Magenta, COLOR_BRIGHT_MAGENTA }, + { ConsoleColor.Yellow, COLOR_BRIGHT_YELLOW }, + { ConsoleColor.White, COLOR_BRIGHT_WHITE } }; - // Map a ConsoleColor to a platform dependent value. - int MapColors(ConsoleColor color, bool isForeground = true) - { - return colorMap.TryGetValue(color, out var colorValue) ? colorValue + (isForeground ? 0 : 10) : 0; - } - - /// - /// In the NetDriver, colors are encoded as an int. - /// Extracts the foreground and background colors from the encoded value. - /// Assumes a 4-bit encoded value for both foreground and background colors. - /// - internal override void GetColors(int value, out Color foreground, out Color background) - { - // Assume a 4-bit encoded value for both foreground and background colors. - foreground = (Color)((value >> 16) & 0xF); - background = (Color)(value & 0xF); - } - - /// - /// In the NetDriver, colors are encoded as an int. - /// However, the foreground color is stored in the most significant 16 bits, - /// and the background color is stored in the least significant 16 bits. - /// - public override Attribute MakeColor(Color foreground, Color background) - { - // Encode the colors into the int value. - return new Attribute( - value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), - foreground: foreground, - background: background - ); - } - - #endregion - - #region Cursor Handling - bool SetCursorPosition(int col, int row) - { - //if (IsWinPlatform) { - // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. - try - { - Console.SetCursorPosition(col, row); - return true; - } - catch (Exception) - { - return false; - } - // BUGBUG: This breaks -usc on WSL; not sure why. But commenting out fixes. - //} else { - // // TODO: Explain why + 1 is needed (and why we do this for non-Windows). - // Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1)); - // return true; - //} - } - - CursorVisibility? _cachedCursorVisibility; - - public override void UpdateCursor() - { - EnsureCursorVisibility(); - - if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) - { - SetCursorPosition(Col, Row); - SetWindowPosition(0, Row); - } - } - - public override bool GetCursorVisibility(out CursorVisibility visibility) - { - visibility = _cachedCursorVisibility ?? CursorVisibility.Default; - return visibility == CursorVisibility.Default; - } - - public override bool SetCursorVisibility(CursorVisibility visibility) - { - _cachedCursorVisibility = visibility; - var isVisible = Console.CursorVisible = visibility == CursorVisibility.Default; - //Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor); - return isVisible; - } - - public override bool EnsureCursorVisibility() - { - if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) - { - GetCursorVisibility(out CursorVisibility cursorVisibility); - _cachedCursorVisibility = cursorVisibility; - SetCursorVisibility(CursorVisibility.Invisible); - return false; - } - - SetCursorVisibility(_cachedCursorVisibility ?? CursorVisibility.Default); - return _cachedCursorVisibility == CursorVisibility.Default; - } - #endregion - - #region Size and Position Handling - - void SetWindowPosition(int col, int row) - { - Top = Console.WindowTop; - Left = Console.WindowLeft; - } - - private bool EnsureBufferSize() - { + // Map a ConsoleColor to a platform dependent value. + int MapColors (ConsoleColor color, bool isForeground = true) + { + return colorMap.TryGetValue (color, out var colorValue) ? colorValue + (isForeground ? 0 : 10) : 0; + } + + /// + /// In the NetDriver, colors are encoded as an int. + /// Extracts the foreground and background colors from the encoded value. + /// Assumes a 4-bit encoded value for both foreground and background colors. + /// + internal override void GetColors (int value, out Color foreground, out Color background) + { + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = (Color)((value >> 16) & 0xF); + background = (Color)(value & 0xF); + } + + /// + /// In the NetDriver, colors are encoded as an int. + /// However, the foreground color is stored in the most significant 16 bits, + /// and the background color is stored in the least significant 16 bits. + /// + public override Attribute MakeColor (Color foreground, Color background) + { + // Encode the colors into the int value. + return new Attribute ( + value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff), + foreground: foreground, + background: background + ); + } + + #endregion + + #region Cursor Handling + bool SetCursorPosition (int col, int row) + { + //if (IsWinPlatform) { + // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. + try { + Console.SetCursorPosition (col, row); + return true; + } catch (Exception) { + return false; + } + // BUGBUG: This breaks -usc on WSL; not sure why. But commenting out fixes. + //} else { + // // TODO: Explain why + 1 is needed (and why we do this for non-Windows). + // Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1)); + // return true; + //} + } + + CursorVisibility? _cachedCursorVisibility; + + public override void UpdateCursor () + { + EnsureCursorVisibility (); + + if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) { + SetCursorPosition (Col, Row); + SetWindowPosition (0, Row); + } + } + + public override bool GetCursorVisibility (out CursorVisibility visibility) + { + visibility = _cachedCursorVisibility ?? CursorVisibility.Default; + return visibility == CursorVisibility.Default; + } + + public override bool SetCursorVisibility (CursorVisibility visibility) + { + _cachedCursorVisibility = visibility; + var isVisible = Console.CursorVisible = visibility == CursorVisibility.Default; + //Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor); + return isVisible; + } + + public override bool EnsureCursorVisibility () + { + if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) { + GetCursorVisibility (out CursorVisibility cursorVisibility); + _cachedCursorVisibility = cursorVisibility; + SetCursorVisibility (CursorVisibility.Invisible); + return false; + } + + SetCursorVisibility (_cachedCursorVisibility ?? CursorVisibility.Default); + return _cachedCursorVisibility == CursorVisibility.Default; + } + #endregion + + #region Size and Position Handling + + void SetWindowPosition (int col, int row) + { + Top = Console.WindowTop; + Left = Console.WindowLeft; + } + + private bool EnsureBufferSize () + { #pragma warning disable CA1416 - if (IsWinPlatform && Console.BufferHeight < Rows) - { - try - { - Console.SetBufferSize(Console.WindowWidth, Rows); - } - catch (Exception) - { - return false; - } - } + if (IsWinPlatform && Console.BufferHeight < Rows) { + try { + Console.SetBufferSize (Console.WindowWidth, Rows); + } catch (Exception) { + return false; + } + } #pragma warning restore CA1416 - return true; - } - #endregion - - - public void StartReportingMouseMoves() - { - Console.Out.Write(EscSeqUtils.CSI_EnableMouseEvents); - } - - public void StopReportingMouseMoves() - { - Console.Out.Write(EscSeqUtils.CSI_DisableMouseEvents); - } - - ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo(ConsoleKeyInfo consoleKeyInfo) - { - if (consoleKeyInfo.Key != ConsoleKey.Packet) - { - return consoleKeyInfo; - } - - var mod = consoleKeyInfo.Modifiers; - var shift = (mod & ConsoleModifiers.Shift) != 0; - var alt = (mod & ConsoleModifiers.Alt) != 0; - var control = (mod & ConsoleModifiers.Control) != 0; - - var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey(consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _); - - return new ConsoleKeyInfo((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control); - } - - Key MapKey(ConsoleKeyInfo keyInfo) - { - MapKeyModifiers(keyInfo, (Key)keyInfo.Key); - switch (keyInfo.Key) - { - case ConsoleKey.Escape: - return MapKeyModifiers(keyInfo, Key.Esc); - case ConsoleKey.Tab: - return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; - case ConsoleKey.Home: - return MapKeyModifiers(keyInfo, Key.Home); - case ConsoleKey.End: - return MapKeyModifiers(keyInfo, Key.End); - case ConsoleKey.LeftArrow: - return MapKeyModifiers(keyInfo, Key.CursorLeft); - case ConsoleKey.RightArrow: - return MapKeyModifiers(keyInfo, Key.CursorRight); - case ConsoleKey.UpArrow: - return MapKeyModifiers(keyInfo, Key.CursorUp); - case ConsoleKey.DownArrow: - return MapKeyModifiers(keyInfo, Key.CursorDown); - case ConsoleKey.PageUp: - return MapKeyModifiers(keyInfo, Key.PageUp); - case ConsoleKey.PageDown: - return MapKeyModifiers(keyInfo, Key.PageDown); - case ConsoleKey.Enter: - return MapKeyModifiers(keyInfo, Key.Enter); - case ConsoleKey.Spacebar: - return MapKeyModifiers(keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); - case ConsoleKey.Backspace: - return MapKeyModifiers(keyInfo, Key.Backspace); - case ConsoleKey.Delete: - return MapKeyModifiers(keyInfo, Key.DeleteChar); - case ConsoleKey.Insert: - return MapKeyModifiers(keyInfo, Key.InsertChar); - - case ConsoleKey.Oem1: - case ConsoleKey.Oem2: - case ConsoleKey.Oem3: - case ConsoleKey.Oem4: - case ConsoleKey.Oem5: - case ConsoleKey.Oem6: - case ConsoleKey.Oem7: - case ConsoleKey.Oem8: - case ConsoleKey.Oem102: - case ConsoleKey.OemPeriod: - case ConsoleKey.OemComma: - case ConsoleKey.OemPlus: - case ConsoleKey.OemMinus: - return (Key)((uint)keyInfo.KeyChar); - } - - var key = keyInfo.Key; - if (key >= ConsoleKey.A && key <= ConsoleKey.Z) - { - var delta = key - ConsoleKey.A; - if (keyInfo.Modifiers == ConsoleModifiers.Control) - { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Alt) - { - return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) - { - if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) - { - return MapKeyModifiers(keyInfo, (Key)((uint)Key.A + delta)); - } - } - return (Key)((uint)keyInfo.KeyChar); - } - if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) - { - var delta = key - ConsoleKey.D0; - if (keyInfo.Modifiers == ConsoleModifiers.Alt) - { - return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Control) - { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) - { - if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) - { - return MapKeyModifiers(keyInfo, (Key)((uint)Key.D0 + delta)); - } - } - return (Key)((uint)keyInfo.KeyChar); - } - if (key is >= ConsoleKey.F1 and <= ConsoleKey.F12) - { - var delta = key - ConsoleKey.F1; - if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) - { - return MapKeyModifiers(keyInfo, (Key)((uint)Key.F1 + delta)); - } - - return (Key)((uint)Key.F1 + delta); - } - if (keyInfo.KeyChar != 0) - { - return MapKeyModifiers(keyInfo, (Key)((uint)keyInfo.KeyChar)); - } - - return (Key)(0xffffffff); - } - - KeyModifiers _keyModifiers; - - Key MapKeyModifiers(ConsoleKeyInfo keyInfo, Key key) - { - _keyModifiers ??= new KeyModifiers(); - Key keyMod = new Key(); - if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) - { - keyMod = Key.ShiftMask; - _keyModifiers.Shift = true; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) - { - keyMod |= Key.CtrlMask; - _keyModifiers.Ctrl = true; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) - { - keyMod |= Key.AltMask; - _keyModifiers.Alt = true; - } - - return keyMod != Key.Null ? keyMod | key : key; - } - - Action _keyHandler; - Action _keyDownHandler; - Action _keyUpHandler; - Action _mouseHandler; - NetMainLoop _mainLoop; - - public override void PrepareToRun(MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) - { - _keyHandler = keyHandler; - _keyDownHandler = keyDownHandler; - _keyUpHandler = keyUpHandler; - _mouseHandler = mouseHandler; - - var mLoop = _mainLoop = mainLoop.MainLoopDriver as NetMainLoop; - - // Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. - mLoop.ProcessInput = (e) => ProcessInput(e); - } - - volatile bool _winSizeChanging; - - void ProcessInput(NetEvents.InputResult inputEvent) - { - switch (inputEvent.EventType) - { - case NetEvents.EventType.Key: - ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo; - if (consoleKeyInfo.Key == ConsoleKey.Packet) - { - consoleKeyInfo = FromVKPacketToKConsoleKeyInfo(consoleKeyInfo); - } - _keyModifiers = new KeyModifiers(); - var map = MapKey(consoleKeyInfo); - if (map == (Key)0xffffffff) - { - return; - } - if (map == Key.Null) - { - _keyDownHandler(new KeyEvent(map, _keyModifiers)); - _keyUpHandler(new KeyEvent(map, _keyModifiers)); - } - else - { - _keyDownHandler(new KeyEvent(map, _keyModifiers)); - _keyHandler(new KeyEvent(map, _keyModifiers)); - _keyUpHandler(new KeyEvent(map, _keyModifiers)); - } - break; - case NetEvents.EventType.Mouse: - _mouseHandler(ToDriverMouse(inputEvent.MouseEvent)); - break; - case NetEvents.EventType.WindowSize: - _winSizeChanging = true; - Top = 0; - Left = 0; - Cols = inputEvent.WindowSizeEvent.Size.Width; - Rows = Math.Max(inputEvent.WindowSizeEvent.Size.Height, 0); ; - ResizeScreen(); - ClearContents(); - _winSizeChanging = false; - TerminalResized?.Invoke(); - break; - case NetEvents.EventType.RequestResponse: - // BUGBUG: What is this for? It does not seem to be used anywhere. - // It is also not clear what it does. View.Data is documented as "This property is not used internally" - Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; - break; - case NetEvents.EventType.WindowPosition: - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - - MouseEvent ToDriverMouse(NetEvents.MouseEvent me) - { - //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}"); - - MouseFlags mouseFlag = 0; - - if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) - { - mouseFlag |= MouseFlags.Button1Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) - { - mouseFlag |= MouseFlags.Button1Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) - { - mouseFlag |= MouseFlags.Button1Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) - { - mouseFlag |= MouseFlags.Button1DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) - { - mouseFlag |= MouseFlags.Button1TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) - { - mouseFlag |= MouseFlags.Button2Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) - { - mouseFlag |= MouseFlags.Button2Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) - { - mouseFlag |= MouseFlags.Button2Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) - { - mouseFlag |= MouseFlags.Button2DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) - { - mouseFlag |= MouseFlags.Button2TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) - { - mouseFlag |= MouseFlags.Button3Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) - { - mouseFlag |= MouseFlags.Button3Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) - { - mouseFlag |= MouseFlags.Button3Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) - { - mouseFlag |= MouseFlags.Button3DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) - { - mouseFlag |= MouseFlags.Button3TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) - { - mouseFlag |= MouseFlags.WheeledUp; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) - { - mouseFlag |= MouseFlags.WheeledDown; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) - { - mouseFlag |= MouseFlags.WheeledLeft; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) - { - mouseFlag |= MouseFlags.WheeledRight; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) - { - mouseFlag |= MouseFlags.Button4Pressed; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) - { - mouseFlag |= MouseFlags.Button4Released; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) - { - mouseFlag |= MouseFlags.Button4Clicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) - { - mouseFlag |= MouseFlags.Button4DoubleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) - { - mouseFlag |= MouseFlags.Button4TripleClicked; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) - { - mouseFlag |= MouseFlags.ReportMousePosition; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) - { - mouseFlag |= MouseFlags.ButtonShift; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) - { - mouseFlag |= MouseFlags.ButtonCtrl; - } - if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) - { - mouseFlag |= MouseFlags.ButtonAlt; - } - - return new MouseEvent() - { - X = me.Position.X, - Y = me.Position.Y, - Flags = mouseFlag - }; - } - - public override void SendKeys(char keyChar, ConsoleKey key, bool shift, bool alt, bool control) - { - NetEvents.InputResult input = new NetEvents.InputResult - { - EventType = NetEvents.EventType.Key, - ConsoleKeyInfo = new ConsoleKeyInfo(keyChar, key, shift, alt, control) - }; - - try - { - ProcessInput(input); - } - catch (OverflowException) { } - } - - - #region Not Implemented - public override void Suspend() - { - throw new NotImplementedException(); - } - #endregion + return true; + } + #endregion + + + public void StartReportingMouseMoves () + { + Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents); + } + + public void StopReportingMouseMoves () + { + Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents); + } + + ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) + { + if (consoleKeyInfo.Key != ConsoleKey.Packet) { + return consoleKeyInfo; + } + + var mod = consoleKeyInfo.Modifiers; + var shift = (mod & ConsoleModifiers.Shift) != 0; + var alt = (mod & ConsoleModifiers.Alt) != 0; + var control = (mod & ConsoleModifiers.Control) != 0; + + var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _); + + return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control); + } + + Key MapKey (ConsoleKeyInfo keyInfo) + { + MapKeyModifiers (keyInfo, (Key)keyInfo.Key); + switch (keyInfo.Key) { + case ConsoleKey.Escape: + return MapKeyModifiers (keyInfo, Key.Esc); + case ConsoleKey.Tab: + return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; + case ConsoleKey.Home: + return MapKeyModifiers (keyInfo, Key.Home); + case ConsoleKey.End: + return MapKeyModifiers (keyInfo, Key.End); + case ConsoleKey.LeftArrow: + return MapKeyModifiers (keyInfo, Key.CursorLeft); + case ConsoleKey.RightArrow: + return MapKeyModifiers (keyInfo, Key.CursorRight); + case ConsoleKey.UpArrow: + return MapKeyModifiers (keyInfo, Key.CursorUp); + case ConsoleKey.DownArrow: + return MapKeyModifiers (keyInfo, Key.CursorDown); + case ConsoleKey.PageUp: + return MapKeyModifiers (keyInfo, Key.PageUp); + case ConsoleKey.PageDown: + return MapKeyModifiers (keyInfo, Key.PageDown); + case ConsoleKey.Enter: + return MapKeyModifiers (keyInfo, Key.Enter); + case ConsoleKey.Spacebar: + return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); + case ConsoleKey.Backspace: + return MapKeyModifiers (keyInfo, Key.Backspace); + case ConsoleKey.Delete: + return MapKeyModifiers (keyInfo, Key.DeleteChar); + case ConsoleKey.Insert: + return MapKeyModifiers (keyInfo, Key.InsertChar); + + case ConsoleKey.Oem1: + case ConsoleKey.Oem2: + case ConsoleKey.Oem3: + case ConsoleKey.Oem4: + case ConsoleKey.Oem5: + case ConsoleKey.Oem6: + case ConsoleKey.Oem7: + case ConsoleKey.Oem8: + case ConsoleKey.Oem102: + case ConsoleKey.OemPeriod: + case ConsoleKey.OemComma: + case ConsoleKey.OemPlus: + case ConsoleKey.OemMinus: + return (Key)((uint)keyInfo.KeyChar); + } + + var key = keyInfo.Key; + if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { + var delta = key - ConsoleKey.A; + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); + } + } + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { + var delta = key - ConsoleKey.D0; + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); + } + } + return (Key)((uint)keyInfo.KeyChar); + } + if (key is >= ConsoleKey.F1 and <= ConsoleKey.F12) { + var delta = key - ConsoleKey.F1; + if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); + } + + return (Key)((uint)Key.F1 + delta); + } + if (keyInfo.KeyChar != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); + } + + return (Key)(0xffffffff); + } + + KeyModifiers _keyModifiers; + + Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) + { + _keyModifiers ??= new KeyModifiers (); + Key keyMod = new Key (); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { + keyMod = Key.ShiftMask; + _keyModifiers.Shift = true; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { + keyMod |= Key.CtrlMask; + _keyModifiers.Ctrl = true; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { + keyMod |= Key.AltMask; + _keyModifiers.Alt = true; + } + + return keyMod != Key.Null ? keyMod | key : key; + } + + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; + NetMainLoop _mainLoop; + + public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) + { + _keyHandler = keyHandler; + _keyDownHandler = keyDownHandler; + _keyUpHandler = keyUpHandler; + _mouseHandler = mouseHandler; + + var mLoop = _mainLoop = mainLoop.MainLoopDriver as NetMainLoop; + + // Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. + mLoop.ProcessInput = (e) => ProcessInput (e); + } + + volatile bool _winSizeChanging; + + void ProcessInput (NetEvents.InputResult inputEvent) + { + switch (inputEvent.EventType) { + case NetEvents.EventType.Key: + ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo; + if (consoleKeyInfo.Key == ConsoleKey.Packet) { + consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo); + } + _keyModifiers = new KeyModifiers (); + var map = MapKey (consoleKeyInfo); + if (map == (Key)0xffffffff) { + return; + } + if (map == Key.Null) { + _keyDownHandler (new KeyEvent (map, _keyModifiers)); + _keyUpHandler (new KeyEvent (map, _keyModifiers)); + } else { + _keyDownHandler (new KeyEvent (map, _keyModifiers)); + _keyHandler (new KeyEvent (map, _keyModifiers)); + _keyUpHandler (new KeyEvent (map, _keyModifiers)); + } + break; + case NetEvents.EventType.Mouse: + _mouseHandler (ToDriverMouse (inputEvent.MouseEvent)); + break; + case NetEvents.EventType.WindowSize: + _winSizeChanging = true; + Top = 0; + Left = 0; + Cols = inputEvent.WindowSizeEvent.Size.Width; + Rows = Math.Max (inputEvent.WindowSizeEvent.Size.Height, 0); ; + ResizeScreen (); + ClearContents (); + _winSizeChanging = false; + TerminalResized?.Invoke (); + break; + case NetEvents.EventType.RequestResponse: + // BUGBUG: What is this for? It does not seem to be used anywhere. + // It is also not clear what it does. View.Data is documented as "This property is not used internally" + Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; + break; + case NetEvents.EventType.WindowPosition: + break; + default: + throw new ArgumentOutOfRangeException (); + } + } + + MouseEvent ToDriverMouse (NetEvents.MouseEvent me) + { + //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}"); + + MouseFlags mouseFlag = 0; + + if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) { + mouseFlag |= MouseFlags.Button1Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) { + mouseFlag |= MouseFlags.Button1Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) { + mouseFlag |= MouseFlags.Button1Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) { + mouseFlag |= MouseFlags.Button1DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) { + mouseFlag |= MouseFlags.Button1TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) { + mouseFlag |= MouseFlags.Button2Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) { + mouseFlag |= MouseFlags.Button2Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) { + mouseFlag |= MouseFlags.Button2Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) { + mouseFlag |= MouseFlags.Button2DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) { + mouseFlag |= MouseFlags.Button2TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) { + mouseFlag |= MouseFlags.Button3Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) { + mouseFlag |= MouseFlags.Button3Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) { + mouseFlag |= MouseFlags.Button3Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) { + mouseFlag |= MouseFlags.Button3DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) { + mouseFlag |= MouseFlags.Button3TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) { + mouseFlag |= MouseFlags.WheeledUp; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) { + mouseFlag |= MouseFlags.WheeledDown; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) { + mouseFlag |= MouseFlags.WheeledLeft; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) { + mouseFlag |= MouseFlags.WheeledRight; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) { + mouseFlag |= MouseFlags.Button4Pressed; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) { + mouseFlag |= MouseFlags.Button4Released; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) { + mouseFlag |= MouseFlags.Button4Clicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) { + mouseFlag |= MouseFlags.Button4DoubleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) { + mouseFlag |= MouseFlags.Button4TripleClicked; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) { + mouseFlag |= MouseFlags.ReportMousePosition; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) { + mouseFlag |= MouseFlags.ButtonShift; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) { + mouseFlag |= MouseFlags.ButtonCtrl; + } + if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) { + mouseFlag |= MouseFlags.ButtonAlt; + } + + return new MouseEvent () { + X = me.Position.X, + Y = me.Position.Y, + Flags = mouseFlag + }; + } + + public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) + { + NetEvents.InputResult input = new NetEvents.InputResult { + EventType = NetEvents.EventType.Key, + ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control) + }; + + try { + ProcessInput (input); + } catch (OverflowException) { } + } + + + #region Not Implemented + public override void Suspend () + { + throw new NotImplementedException (); + } + #endregion } @@ -1423,148 +1249,125 @@ public override void Suspend() /// /// This implementation is used for NetDriver. /// -internal class NetMainLoop : IMainLoopDriver -{ - ManualResetEventSlim _keyReady = new ManualResetEventSlim(false); - ManualResetEventSlim _waitForProbe = new ManualResetEventSlim(false); - Queue _inputResult = new Queue(); - MainLoop _mainLoop; - CancellationTokenSource _tokenSource = new CancellationTokenSource(); - internal NetEvents _netEvents; - - /// - /// Invoked when a Key is pressed. - /// - internal Action ProcessInput; - - /// - /// Initializes the class with the console driver. - /// - /// - /// Passing a consoleDriver is provided to capture windows resizing. - /// - /// The console driver used by this Net main loop. - /// - public NetMainLoop(ConsoleDriver consoleDriver = null) - { - if (consoleDriver == null) - { - throw new ArgumentNullException(nameof(consoleDriver)); - } - _netEvents = new NetEvents(consoleDriver); - } - - void NetInputHandler() - { - while (true) - { - _waitForProbe.Wait(); - _waitForProbe.Reset(); - if (_inputResult.Count == 0) - { - _inputResult.Enqueue(_netEvents.ReadConsoleInput()); - } - try - { - while (_inputResult.Peek() == null) - { - _inputResult.Dequeue(); - } - if (_inputResult.Count > 0) - { - _keyReady.Set(); - } - } - catch (InvalidOperationException) { } - } - } - - void IMainLoopDriver.Setup(MainLoop mainLoop) - { - _mainLoop = mainLoop; - Task.Run(NetInputHandler); - } - - void IMainLoopDriver.Wakeup() - { - _keyReady.Set(); - } - - bool IMainLoopDriver.EventsPending(bool wait) - { - _waitForProbe.Set(); - - if (CheckTimers(wait, out var waitTimeout)) - { - return true; - } - - try - { - if (!_tokenSource.IsCancellationRequested) - { - _keyReady.Wait(waitTimeout, _tokenSource.Token); - } - } - catch (OperationCanceledException) - { - return true; - } - finally - { - _keyReady.Reset(); - } - - if (!_tokenSource.IsCancellationRequested) - { - return _inputResult.Count > 0 || CheckTimers(wait, out _); - } - - _tokenSource.Dispose(); - _tokenSource = new CancellationTokenSource(); - return true; - } - - bool CheckTimers(bool wait, out int waitTimeout) - { - var now = DateTime.UtcNow.Ticks; - - if (_mainLoop.timeouts.Count > 0) - { - waitTimeout = (int)((_mainLoop.timeouts.Keys[0] - now) / TimeSpan.TicksPerMillisecond); - if (waitTimeout < 0) - { - return true; - } - } - else - { - waitTimeout = -1; - } - - if (!wait) - { - waitTimeout = 0; - } - - int ic; - lock (_mainLoop.idleHandlers) - { - ic = _mainLoop.idleHandlers.Count; - } - - return ic > 0; - } - - void IMainLoopDriver.Iteration() - { - while (_inputResult.Count > 0) - { - ProcessInput?.Invoke(_inputResult.Dequeue().Value); - } - } - public void TearDown() - { - //throw new NotImplementedException (); - } +internal class NetMainLoop : IMainLoopDriver { + ManualResetEventSlim _keyReady = new ManualResetEventSlim (false); + ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); + Queue _inputResult = new Queue (); + MainLoop _mainLoop; + CancellationTokenSource _tokenSource = new CancellationTokenSource (); + internal NetEvents _netEvents; + + /// + /// Invoked when a Key is pressed. + /// + internal Action ProcessInput; + + /// + /// Initializes the class with the console driver. + /// + /// + /// Passing a consoleDriver is provided to capture windows resizing. + /// + /// The console driver used by this Net main loop. + /// + public NetMainLoop (ConsoleDriver consoleDriver = null) + { + if (consoleDriver == null) { + throw new ArgumentNullException (nameof (consoleDriver)); + } + _netEvents = new NetEvents (consoleDriver); + } + + void NetInputHandler () + { + while (true) { + _waitForProbe.Wait (); + _waitForProbe.Reset (); + if (_inputResult.Count == 0) { + _inputResult.Enqueue (_netEvents.ReadConsoleInput ()); + } + try { + while (_inputResult.Peek () == null) { + _inputResult.Dequeue (); + } + if (_inputResult.Count > 0) { + _keyReady.Set (); + } + } catch (InvalidOperationException) { } + } + } + + void IMainLoopDriver.Setup (MainLoop mainLoop) + { + _mainLoop = mainLoop; + Task.Run (NetInputHandler); + } + + void IMainLoopDriver.Wakeup () + { + _keyReady.Set (); + } + + bool IMainLoopDriver.EventsPending (bool wait) + { + _waitForProbe.Set (); + + if (CheckTimers (wait, out var waitTimeout)) { + return true; + } + + try { + if (!_tokenSource.IsCancellationRequested) { + _keyReady.Wait (waitTimeout, _tokenSource.Token); + } + } catch (OperationCanceledException) { + return true; + } finally { + _keyReady.Reset (); + } + + if (!_tokenSource.IsCancellationRequested) { + return _inputResult.Count > 0 || CheckTimers (wait, out _); + } + + _tokenSource.Dispose (); + _tokenSource = new CancellationTokenSource (); + return true; + } + + bool CheckTimers (bool wait, out int waitTimeout) + { + var now = DateTime.UtcNow.Ticks; + + if (_mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) { + return true; + } + } else { + waitTimeout = -1; + } + + if (!wait) { + waitTimeout = 0; + } + + int ic; + lock (_mainLoop.idleHandlers) { + ic = _mainLoop.idleHandlers.Count; + } + + return ic > 0; + } + + void IMainLoopDriver.Iteration () + { + while (_inputResult.Count > 0) { + ProcessInput?.Invoke (_inputResult.Dequeue ().Value); + } + } + public void TearDown () + { + //throw new NotImplementedException (); + } } \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 99513bf814..0cec3e9c8f 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -12,745 +12,669 @@ namespace Terminal.Gui; -internal class WindowsConsole -{ - public const int STD_OUTPUT_HANDLE = -11; - public const int STD_INPUT_HANDLE = -10; - public const int STD_ERROR_HANDLE = -12; - - IntPtr _inputHandle, _outputHandle; - IntPtr _screenBuffer; - readonly uint _originalConsoleMode; - CursorVisibility? _initialCursorVisibility = null; - CursorVisibility? _currentCursorVisibility = null; - CursorVisibility? _pendingCursorVisibility = null; - readonly StringBuilder _stringBuilder = new StringBuilder(256 * 1024); - - public WindowsConsole() - { - _inputHandle = GetStdHandle(STD_INPUT_HANDLE); - _outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); - _originalConsoleMode = ConsoleMode; - var newConsoleMode = _originalConsoleMode; - newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags); - newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode; - newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput; - ConsoleMode = newConsoleMode; - } - - CharInfo[] _originalStdOutChars; - - public bool WriteToConsole(Size size, ExtendedCharInfo[] charInfoBuffer, Coord coords, SmallRect window, bool useTrueColor) - { - if (_screenBuffer == IntPtr.Zero) - { - ReadFromConsoleOutput(size, coords, ref window); - } - - if (!useTrueColor) - { - int i = 0; - CharInfo[] ci = new CharInfo[charInfoBuffer.Length]; - foreach (ExtendedCharInfo info in charInfoBuffer) - { - ci[i++] = new CharInfo() - { - Char = new CharUnion() { UnicodeChar = info.Char }, - Attributes = (ushort)info.Attribute.Value - }; - } - return WriteConsoleOutput(_screenBuffer, ci, coords, new Coord() { X = window.Left, Y = window.Top }, ref window); - } - - - return WriteConsoleTrueColorOutput(charInfoBuffer); - } - - private bool WriteConsoleTrueColorOutput(ExtendedCharInfo[] charInfoBuffer) - { - _stringBuilder.Clear(); - - _stringBuilder.Append(EscSeqUtils.CSI_SaveCursorPosition); - _stringBuilder.Append(EscSeqUtils.CSI_SetCursorPosition(0, 0)); - - Attribute? prev = null; - foreach (var info in charInfoBuffer) - { - var attr = info.Attribute; - - if (attr != prev) - { - prev = attr; - - _stringBuilder.Append(EscSeqUtils.CSI_SetForegroundColorRGB(attr.TrueColorForeground.Value.Red, attr.TrueColorForeground.Value.Green, attr.TrueColorForeground.Value.Blue)); - _stringBuilder.Append(EscSeqUtils.CSI_SetBackgroundColorRGB(attr.TrueColorBackground.Value.Red, attr.TrueColorBackground.Value.Green, attr.TrueColorBackground.Value.Blue)); - } - - if (info.Char != '\x1b') - { - if (!info.Empty) - { - _stringBuilder.Append(info.Char); - } - - } - else - { - _stringBuilder.Append(' '); - } - } - - _stringBuilder.Append(EscSeqUtils.CSI_RestoreCursorPosition); - - string s = _stringBuilder.ToString(); - - return WriteConsole(_screenBuffer, s, (uint)(s.Length), out uint _, null); - } - - public void ReadFromConsoleOutput(Size size, Coord coords, ref SmallRect window) - { - _screenBuffer = CreateConsoleScreenBuffer( - DesiredAccess.GenericRead | DesiredAccess.GenericWrite, - ShareMode.FileShareRead | ShareMode.FileShareWrite, - IntPtr.Zero, - 1, - IntPtr.Zero - ); - if (_screenBuffer == INVALID_HANDLE_VALUE) - { - var err = Marshal.GetLastWin32Error(); - - if (err != 0) - { - throw new System.ComponentModel.Win32Exception(err); - } - } - - if (!_initialCursorVisibility.HasValue && GetCursorVisibility(out CursorVisibility visibility)) - { - _initialCursorVisibility = visibility; - } - - if (!SetConsoleActiveScreenBuffer(_screenBuffer)) - { - throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); - } - - _originalStdOutChars = new CharInfo[size.Height * size.Width]; - - if (!ReadConsoleOutput(_screenBuffer, _originalStdOutChars, coords, new Coord() { X = 0, Y = 0 }, ref window)) - { - throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); - } - } - - public bool SetCursorPosition(Coord position) - { - return SetConsoleCursorPosition(_screenBuffer, position); - } - - public void SetInitialCursorVisibility() - { - if (_initialCursorVisibility.HasValue == false && GetCursorVisibility(out CursorVisibility visibility)) - { - _initialCursorVisibility = visibility; - } - } - - public bool GetCursorVisibility(out CursorVisibility visibility) - { - if (_screenBuffer == IntPtr.Zero) - { - visibility = CursorVisibility.Invisible; - return false; - } - if (!GetConsoleCursorInfo(_screenBuffer, out ConsoleCursorInfo info)) - { - var err = Marshal.GetLastWin32Error(); - if (err != 0) - { - throw new System.ComponentModel.Win32Exception(err); - } - visibility = Gui.CursorVisibility.Default; - - return false; - } - - if (!info.bVisible) - { - visibility = CursorVisibility.Invisible; - } - else if (info.dwSize > 50) - { - visibility = CursorVisibility.Box; - } - else - { - visibility = CursorVisibility.Underline; - } - - return true; - } - - public bool EnsureCursorVisibility() - { - if (_initialCursorVisibility.HasValue && _pendingCursorVisibility.HasValue && SetCursorVisibility(_pendingCursorVisibility.Value)) - { - _pendingCursorVisibility = null; - - return true; - } - - return false; - } - - public void ForceRefreshCursorVisibility() - { - if (_currentCursorVisibility.HasValue) - { - _pendingCursorVisibility = _currentCursorVisibility; - _currentCursorVisibility = null; - } - } - - public bool SetCursorVisibility(CursorVisibility visibility) - { - if (_initialCursorVisibility.HasValue == false) - { - _pendingCursorVisibility = visibility; - - return false; - } - - if (_currentCursorVisibility.HasValue == false || _currentCursorVisibility.Value != visibility) - { - ConsoleCursorInfo info = new ConsoleCursorInfo - { - dwSize = (uint)visibility & 0x00FF, - bVisible = ((uint)visibility & 0xFF00) != 0 - }; - - if (!SetConsoleCursorInfo(_screenBuffer, ref info)) - { - return false; - } - - _currentCursorVisibility = visibility; - } - - return true; - } - - public void Cleanup() - { - if (_initialCursorVisibility.HasValue) - { - SetCursorVisibility(_initialCursorVisibility.Value); - } - - SetConsoleOutputWindow(out _); - - ConsoleMode = _originalConsoleMode; - if (!SetConsoleActiveScreenBuffer(_outputHandle)) - { - var err = Marshal.GetLastWin32Error(); - Console.WriteLine("Error: {0}", err); - } - - if (_screenBuffer != IntPtr.Zero) - { - CloseHandle(_screenBuffer); - } - - _screenBuffer = IntPtr.Zero; - } - - internal Size GetConsoleBufferWindow(out Point position) - { - if (_screenBuffer == IntPtr.Zero) - { - position = Point.Empty; - return Size.Empty; - } - - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX(); - csbi.cbSize = (uint)Marshal.SizeOf(csbi); - if (!GetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) - { - //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - position = Point.Empty; - return Size.Empty; - } - var sz = new Size(csbi.srWindow.Right - csbi.srWindow.Left + 1, - csbi.srWindow.Bottom - csbi.srWindow.Top + 1); - position = new Point(csbi.srWindow.Left, csbi.srWindow.Top); - - return sz; - } - - internal Size GetConsoleOutputWindow(out Point position) - { - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX(); - csbi.cbSize = (uint)Marshal.SizeOf(csbi); - if (!GetConsoleScreenBufferInfoEx(_outputHandle, ref csbi)) - { - throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); - } - var sz = new Size(csbi.srWindow.Right - csbi.srWindow.Left + 1, - csbi.srWindow.Bottom - csbi.srWindow.Top + 1); - position = new Point(csbi.srWindow.Left, csbi.srWindow.Top); - - return sz; - } - - internal Size SetConsoleWindow(short cols, short rows) - { - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX(); - csbi.cbSize = (uint)Marshal.SizeOf(csbi); - - if (!GetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) - { - throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); - } - var maxWinSize = GetLargestConsoleWindowSize(_screenBuffer); - var newCols = Math.Min(cols, maxWinSize.X); - var newRows = Math.Min(rows, maxWinSize.Y); - csbi.dwSize = new Coord(newCols, Math.Max(newRows, (short)1)); - csbi.srWindow = new SmallRect(0, 0, newCols, newRows); - csbi.dwMaximumWindowSize = new Coord(newCols, newRows); - if (!SetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) - { - throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); - } - var winRect = new SmallRect(0, 0, (short)(newCols - 1), (short)Math.Max(newRows - 1, 0)); - if (!SetConsoleWindowInfo(_outputHandle, true, ref winRect)) - { - //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); - return new Size(cols, rows); - } - SetConsoleOutputWindow(csbi); - return new Size(winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1); - } - - void SetConsoleOutputWindow(CONSOLE_SCREEN_BUFFER_INFOEX csbi) - { - if (_screenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) - { - throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); - } - } - - internal Size SetConsoleOutputWindow(out Point position) - { - if (_screenBuffer == IntPtr.Zero) - { - position = Point.Empty; - return Size.Empty; - } - - var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX(); - csbi.cbSize = (uint)Marshal.SizeOf(csbi); - if (!GetConsoleScreenBufferInfoEx(_screenBuffer, ref csbi)) - { - throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); - } - var sz = new Size(csbi.srWindow.Right - csbi.srWindow.Left + 1, - Math.Max(csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0)); - position = new Point(csbi.srWindow.Left, csbi.srWindow.Top); - SetConsoleOutputWindow(csbi); - var winRect = new SmallRect(0, 0, (short)(sz.Width - 1), (short)Math.Max(sz.Height - 1, 0)); - if (!SetConsoleScreenBufferInfoEx(_outputHandle, ref csbi)) - { - throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); - } - if (!SetConsoleWindowInfo(_outputHandle, true, ref winRect)) - { - throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); - } - - return sz; - } - - uint ConsoleMode - { - get - { - GetConsoleMode(_inputHandle, out uint v); - return v; - } - set - { - SetConsoleMode(_inputHandle, value); - } - } - - [Flags] - public enum ConsoleModes : uint - { - EnableProcessedInput = 1, - EnableMouseInput = 16, - EnableQuickEditMode = 64, - EnableExtendedFlags = 128, - } - - [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] - public struct KeyEventRecord - { - [FieldOffset(0), MarshalAs(UnmanagedType.Bool)] - public bool bKeyDown; - [FieldOffset(4), MarshalAs(UnmanagedType.U2)] - public ushort wRepeatCount; - [FieldOffset(6), MarshalAs(UnmanagedType.U2)] - public ushort wVirtualKeyCode; - [FieldOffset(8), MarshalAs(UnmanagedType.U2)] - public ushort wVirtualScanCode; - [FieldOffset(10)] - public char UnicodeChar; - [FieldOffset(12), MarshalAs(UnmanagedType.U4)] - public ControlKeyState dwControlKeyState; - } - - [Flags] - public enum ButtonState - { - Button1Pressed = 1, - Button2Pressed = 4, - Button3Pressed = 8, - Button4Pressed = 16, - RightmostButtonPressed = 2 - } - - [Flags] - public enum ControlKeyState - { - RightAltPressed = 1, - LeftAltPressed = 2, - RightControlPressed = 4, - LeftControlPressed = 8, - ShiftPressed = 16, - NumlockOn = 32, - ScrolllockOn = 64, - CapslockOn = 128, - EnhancedKey = 256 - } - - [Flags] - public enum EventFlags - { - MouseMoved = 1, - DoubleClick = 2, - MouseWheeled = 4, - MouseHorizontalWheeled = 8 - } - - [StructLayout(LayoutKind.Explicit)] - public struct MouseEventRecord - { - [FieldOffset(0)] - public Coord MousePosition; - [FieldOffset(4)] - public ButtonState ButtonState; - [FieldOffset(8)] - public ControlKeyState ControlKeyState; - [FieldOffset(12)] - public EventFlags EventFlags; - - public override readonly string ToString() => $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}"; - - } - - public struct WindowBufferSizeRecord - { - public Coord _size; - - public WindowBufferSizeRecord(short x, short y) - { - _size = new Coord(x, y); - } - - public override readonly string ToString() => $"[WindowBufferSize{_size}"; - } - - [StructLayout(LayoutKind.Sequential)] - public struct MenuEventRecord - { - public uint dwCommandId; - } - - [StructLayout(LayoutKind.Sequential)] - public struct FocusEventRecord - { - public uint bSetFocus; - } - - public enum EventType : ushort - { - Focus = 0x10, - Key = 0x1, - Menu = 0x8, - Mouse = 2, - WindowBufferSize = 4 - } - - [StructLayout(LayoutKind.Explicit)] - public struct InputRecord - { - [FieldOffset(0)] - public EventType EventType; - [FieldOffset(4)] - public KeyEventRecord KeyEvent; - [FieldOffset(4)] - public MouseEventRecord MouseEvent; - [FieldOffset(4)] - public WindowBufferSizeRecord WindowBufferSizeEvent; - [FieldOffset(4)] - public MenuEventRecord MenuEvent; - [FieldOffset(4)] - public FocusEventRecord FocusEvent; - - public override readonly string ToString() - { - return EventType switch - { - EventType.Focus => FocusEvent.ToString(), - EventType.Key => KeyEvent.ToString(), - EventType.Menu => MenuEvent.ToString(), - EventType.Mouse => MouseEvent.ToString(), - EventType.WindowBufferSize => WindowBufferSizeEvent.ToString(), - _ => "Unknown event type: " + EventType - }; - } - }; - - [Flags] - enum ShareMode : uint - { - FileShareRead = 1, - FileShareWrite = 2, - } - - [Flags] - enum DesiredAccess : uint - { - GenericRead = 2147483648, - GenericWrite = 1073741824, - } - - [StructLayout(LayoutKind.Sequential)] - public struct ConsoleScreenBufferInfo - { - public Coord dwSize; - public Coord dwCursorPosition; - public ushort wAttributes; - public SmallRect srWindow; - public Coord dwMaximumWindowSize; - } - - [StructLayout(LayoutKind.Sequential)] - public struct Coord - { - public short X; - public short Y; - - public Coord(short x, short y) - { - X = x; - Y = y; - } - public override readonly string ToString() => $"({X},{Y})"; - }; - - [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] - public struct CharUnion - { - [FieldOffset(0)] public char UnicodeChar; - [FieldOffset(0)] public byte AsciiChar; - } - - [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] - public struct CharInfo - { - [FieldOffset(0)] public CharUnion Char; - [FieldOffset(2)] public ushort Attributes; - } - - public struct ExtendedCharInfo - { - public char Char { get; set; } - public Attribute Attribute { get; set; } - public bool Empty { get; set; } // TODO: Temp hack until virutal terminal sequences - - public ExtendedCharInfo(char character, Attribute attribute) - { - Char = character; - Attribute = attribute; - Empty = false; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct SmallRect - { - public short Left; - public short Top; - public short Right; - public short Bottom; - - public SmallRect(short left, short top, short right, short bottom) - { - Left = left; - Top = top; - Right = right; - Bottom = bottom; - } - - public static void MakeEmpty(ref SmallRect rect) - { - rect.Left = -1; - } - - public static void Update(ref SmallRect rect, short col, short row) - { - if (rect.Left == -1) - { - rect.Left = rect.Right = col; - rect.Bottom = rect.Top = row; - return; - } - if (col >= rect.Left && col <= rect.Right && row >= rect.Top && row <= rect.Bottom) - return; - if (col < rect.Left) - rect.Left = col; - if (col > rect.Right) - rect.Right = col; - if (row < rect.Top) - rect.Top = row; - if (row > rect.Bottom) - rect.Bottom = row; - } - - public override readonly string ToString() => $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}"; - } - - [StructLayout(LayoutKind.Sequential)] - public struct ConsoleKeyInfoEx - { - public ConsoleKeyInfo ConsoleKeyInfo; - public bool CapsLock; - public bool NumLock; - public bool ScrollLock; - - public ConsoleKeyInfoEx(ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock) - { - ConsoleKeyInfo = consoleKeyInfo; - CapsLock = capslock; - NumLock = numlock; - ScrollLock = scrolllock; - } - } - - [DllImport("kernel32.dll", SetLastError = true)] - static extern IntPtr GetStdHandle(int nStdHandle); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool CloseHandle(IntPtr handle); - - [DllImport("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)] - public static extern bool ReadConsoleInput( - IntPtr hConsoleInput, - IntPtr lpBuffer, - uint nLength, - out uint lpNumberOfEventsRead); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool ReadConsoleOutput( - IntPtr hConsoleOutput, - [Out] CharInfo[] lpBuffer, - Coord dwBufferSize, - Coord dwBufferCoord, - ref SmallRect lpReadRegion - ); - - // TODO: This API is obsolete. See https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput - [DllImport("kernel32.dll", EntryPoint = "WriteConsoleOutputW", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool WriteConsoleOutput( - IntPtr hConsoleOutput, - CharInfo[] lpBuffer, - Coord dwBufferSize, - Coord dwBufferCoord, - ref SmallRect lpWriteRegion - ); - - [DllImport("kernel32.dll", EntryPoint = "WriteConsole", SetLastError = true, CharSet = CharSet.Unicode)] - static extern bool WriteConsole( - IntPtr hConsoleOutput, - String lpbufer, - UInt32 NumberOfCharsToWriten, - out UInt32 lpNumberOfCharsWritten, - object lpReserved - ); - - [DllImport("kernel32.dll")] - static extern bool SetConsoleCursorPosition(IntPtr hConsoleOutput, Coord dwCursorPosition); - - [StructLayout(LayoutKind.Sequential)] - public struct ConsoleCursorInfo - { - public uint dwSize; - public bool bVisible; - } - - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleCursorInfo(IntPtr hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool GetConsoleCursorInfo(IntPtr hConsoleOutput, out ConsoleCursorInfo lpConsoleCursorInfo); - - [DllImport("kernel32.dll")] - static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); - - [DllImport("kernel32.dll")] - static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern IntPtr CreateConsoleScreenBuffer( - DesiredAccess dwDesiredAccess, - ShareMode dwShareMode, - IntPtr secutiryAttributes, - uint flags, - IntPtr screenBufferData - ); - - internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleActiveScreenBuffer(IntPtr Handle); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool GetNumberOfConsoleInputEvents(IntPtr handle, out uint lpcNumberOfEvents); - - public InputRecord[] ReadConsoleInput() - { - const int bufferSize = 1; - var pRecord = Marshal.AllocHGlobal(Marshal.SizeOf() * bufferSize); - try - { - ReadConsoleInput(_inputHandle, pRecord, bufferSize, - out var numberEventsRead); - - return numberEventsRead == 0 - ? null - : new[] { Marshal.PtrToStructure(pRecord) }; - } - catch (Exception) - { - return null; - } - finally - { - Marshal.FreeHGlobal(pRecord); - } - } +internal class WindowsConsole { + public const int STD_OUTPUT_HANDLE = -11; + public const int STD_INPUT_HANDLE = -10; + public const int STD_ERROR_HANDLE = -12; + + IntPtr _inputHandle, _outputHandle; + IntPtr _screenBuffer; + readonly uint _originalConsoleMode; + CursorVisibility? _initialCursorVisibility = null; + CursorVisibility? _currentCursorVisibility = null; + CursorVisibility? _pendingCursorVisibility = null; + readonly StringBuilder _stringBuilder = new StringBuilder (256 * 1024); + + public WindowsConsole () + { + _inputHandle = GetStdHandle (STD_INPUT_HANDLE); + _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE); + _originalConsoleMode = ConsoleMode; + var newConsoleMode = _originalConsoleMode; + newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags); + newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode; + newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput; + ConsoleMode = newConsoleMode; + } + + CharInfo [] _originalStdOutChars; + + public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord coords, SmallRect window, bool useTrueColor) + { + if (_screenBuffer == IntPtr.Zero) { + ReadFromConsoleOutput (size, coords, ref window); + } + + if (!useTrueColor) { + int i = 0; + CharInfo [] ci = new CharInfo [charInfoBuffer.Length]; + foreach (ExtendedCharInfo info in charInfoBuffer) { + ci [i++] = new CharInfo () { + Char = new CharUnion () { UnicodeChar = info.Char }, + Attributes = (ushort)info.Attribute.Value + }; + } + return WriteConsoleOutput (_screenBuffer, ci, coords, new Coord () { X = window.Left, Y = window.Top }, ref window); + } + + + return WriteConsoleTrueColorOutput (charInfoBuffer); + } + + private bool WriteConsoleTrueColorOutput (ExtendedCharInfo [] charInfoBuffer) + { + _stringBuilder.Clear (); + + _stringBuilder.Append (EscSeqUtils.CSI_SaveCursorPosition); + _stringBuilder.Append (EscSeqUtils.CSI_SetCursorPosition (0, 0)); + + Attribute? prev = null; + foreach (var info in charInfoBuffer) { + var attr = info.Attribute; + + if (attr != prev) { + prev = attr; + + _stringBuilder.Append (EscSeqUtils.CSI_SetForegroundColorRGB (attr.TrueColorForeground.Value.Red, attr.TrueColorForeground.Value.Green, attr.TrueColorForeground.Value.Blue)); + _stringBuilder.Append (EscSeqUtils.CSI_SetBackgroundColorRGB (attr.TrueColorBackground.Value.Red, attr.TrueColorBackground.Value.Green, attr.TrueColorBackground.Value.Blue)); + } + + if (info.Char != '\x1b') { + if (!info.Empty) { + _stringBuilder.Append (info.Char); + } + + } else { + _stringBuilder.Append (' '); + } + } + + _stringBuilder.Append (EscSeqUtils.CSI_RestoreCursorPosition); + + string s = _stringBuilder.ToString (); + + return WriteConsole (_screenBuffer, s, (uint)(s.Length), out uint _, null); + } + + public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window) + { + _screenBuffer = CreateConsoleScreenBuffer ( + DesiredAccess.GenericRead | DesiredAccess.GenericWrite, + ShareMode.FileShareRead | ShareMode.FileShareWrite, + IntPtr.Zero, + 1, + IntPtr.Zero + ); + if (_screenBuffer == INVALID_HANDLE_VALUE) { + var err = Marshal.GetLastWin32Error (); + + if (err != 0) { + throw new System.ComponentModel.Win32Exception (err); + } + } + + if (!_initialCursorVisibility.HasValue && GetCursorVisibility (out CursorVisibility visibility)) { + _initialCursorVisibility = visibility; + } + + if (!SetConsoleActiveScreenBuffer (_screenBuffer)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + + _originalStdOutChars = new CharInfo [size.Height * size.Width]; + + if (!ReadConsoleOutput (_screenBuffer, _originalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + } + + public bool SetCursorPosition (Coord position) + { + return SetConsoleCursorPosition (_screenBuffer, position); + } + + public void SetInitialCursorVisibility () + { + if (_initialCursorVisibility.HasValue == false && GetCursorVisibility (out CursorVisibility visibility)) { + _initialCursorVisibility = visibility; + } + } + + public bool GetCursorVisibility (out CursorVisibility visibility) + { + if (_screenBuffer == IntPtr.Zero) { + visibility = CursorVisibility.Invisible; + return false; + } + if (!GetConsoleCursorInfo (_screenBuffer, out ConsoleCursorInfo info)) { + var err = Marshal.GetLastWin32Error (); + if (err != 0) { + throw new System.ComponentModel.Win32Exception (err); + } + visibility = Gui.CursorVisibility.Default; + + return false; + } + + if (!info.bVisible) { + visibility = CursorVisibility.Invisible; + } else if (info.dwSize > 50) { + visibility = CursorVisibility.Box; + } else { + visibility = CursorVisibility.Underline; + } + + return true; + } + + public bool EnsureCursorVisibility () + { + if (_initialCursorVisibility.HasValue && _pendingCursorVisibility.HasValue && SetCursorVisibility (_pendingCursorVisibility.Value)) { + _pendingCursorVisibility = null; + + return true; + } + + return false; + } + + public void ForceRefreshCursorVisibility () + { + if (_currentCursorVisibility.HasValue) { + _pendingCursorVisibility = _currentCursorVisibility; + _currentCursorVisibility = null; + } + } + + public bool SetCursorVisibility (CursorVisibility visibility) + { + if (_initialCursorVisibility.HasValue == false) { + _pendingCursorVisibility = visibility; + + return false; + } + + if (_currentCursorVisibility.HasValue == false || _currentCursorVisibility.Value != visibility) { + ConsoleCursorInfo info = new ConsoleCursorInfo { + dwSize = (uint)visibility & 0x00FF, + bVisible = ((uint)visibility & 0xFF00) != 0 + }; + + if (!SetConsoleCursorInfo (_screenBuffer, ref info)) { + return false; + } + + _currentCursorVisibility = visibility; + } + + return true; + } + + public void Cleanup () + { + if (_initialCursorVisibility.HasValue) { + SetCursorVisibility (_initialCursorVisibility.Value); + } + + SetConsoleOutputWindow (out _); + + ConsoleMode = _originalConsoleMode; + if (!SetConsoleActiveScreenBuffer (_outputHandle)) { + var err = Marshal.GetLastWin32Error (); + Console.WriteLine ("Error: {0}", err); + } + + if (_screenBuffer != IntPtr.Zero) { + CloseHandle (_screenBuffer); + } + + _screenBuffer = IntPtr.Zero; + } + + internal Size GetConsoleBufferWindow (out Point position) + { + if (_screenBuffer == IntPtr.Zero) { + position = Point.Empty; + return Size.Empty; + } + + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + position = Point.Empty; + return Size.Empty; + } + var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, + csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); + + return sz; + } + + internal Size GetConsoleOutputWindow (out Point position) + { + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, + csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); + + return sz; + } + + internal Size SetConsoleWindow (short cols, short rows) + { + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + + if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + var maxWinSize = GetLargestConsoleWindowSize (_screenBuffer); + var newCols = Math.Min (cols, maxWinSize.X); + var newRows = Math.Min (rows, maxWinSize.Y); + csbi.dwSize = new Coord (newCols, Math.Max (newRows, (short)1)); + csbi.srWindow = new SmallRect (0, 0, newCols, newRows); + csbi.dwMaximumWindowSize = new Coord (newCols, newRows); + if (!SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0)); + if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { + //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + return new Size (cols, rows); + } + SetConsoleOutputWindow (csbi); + return new Size (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1); + } + + void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi) + { + if (_screenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + } + + internal Size SetConsoleOutputWindow (out Point position) + { + if (_screenBuffer == IntPtr.Zero) { + position = Point.Empty; + return Size.Empty; + } + + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1, + Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0)); + position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); + SetConsoleOutputWindow (csbi); + var winRect = new SmallRect (0, 0, (short)(sz.Width - 1), (short)Math.Max (sz.Height - 1, 0)); + if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } + + return sz; + } + + uint ConsoleMode { + get { + GetConsoleMode (_inputHandle, out uint v); + return v; + } + set { + SetConsoleMode (_inputHandle, value); + } + } + + [Flags] + public enum ConsoleModes : uint { + EnableProcessedInput = 1, + EnableMouseInput = 16, + EnableQuickEditMode = 64, + EnableExtendedFlags = 128, + } + + [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] + public struct KeyEventRecord { + [FieldOffset (0), MarshalAs (UnmanagedType.Bool)] + public bool bKeyDown; + [FieldOffset (4), MarshalAs (UnmanagedType.U2)] + public ushort wRepeatCount; + [FieldOffset (6), MarshalAs (UnmanagedType.U2)] + public ushort wVirtualKeyCode; + [FieldOffset (8), MarshalAs (UnmanagedType.U2)] + public ushort wVirtualScanCode; + [FieldOffset (10)] + public char UnicodeChar; + [FieldOffset (12), MarshalAs (UnmanagedType.U4)] + public ControlKeyState dwControlKeyState; + } + + [Flags] + public enum ButtonState { + Button1Pressed = 1, + Button2Pressed = 4, + Button3Pressed = 8, + Button4Pressed = 16, + RightmostButtonPressed = 2 + } + + [Flags] + public enum ControlKeyState { + RightAltPressed = 1, + LeftAltPressed = 2, + RightControlPressed = 4, + LeftControlPressed = 8, + ShiftPressed = 16, + NumlockOn = 32, + ScrolllockOn = 64, + CapslockOn = 128, + EnhancedKey = 256 + } + + [Flags] + public enum EventFlags { + MouseMoved = 1, + DoubleClick = 2, + MouseWheeled = 4, + MouseHorizontalWheeled = 8 + } + + [StructLayout (LayoutKind.Explicit)] + public struct MouseEventRecord { + [FieldOffset (0)] + public Coord MousePosition; + [FieldOffset (4)] + public ButtonState ButtonState; + [FieldOffset (8)] + public ControlKeyState ControlKeyState; + [FieldOffset (12)] + public EventFlags EventFlags; + + public override readonly string ToString () => $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}"; + + } + + public struct WindowBufferSizeRecord { + public Coord _size; + + public WindowBufferSizeRecord (short x, short y) + { + _size = new Coord (x, y); + } + + public override readonly string ToString () => $"[WindowBufferSize{_size}"; + } + + [StructLayout (LayoutKind.Sequential)] + public struct MenuEventRecord { + public uint dwCommandId; + } + + [StructLayout (LayoutKind.Sequential)] + public struct FocusEventRecord { + public uint bSetFocus; + } + + public enum EventType : ushort { + Focus = 0x10, + Key = 0x1, + Menu = 0x8, + Mouse = 2, + WindowBufferSize = 4 + } + + [StructLayout (LayoutKind.Explicit)] + public struct InputRecord { + [FieldOffset (0)] + public EventType EventType; + [FieldOffset (4)] + public KeyEventRecord KeyEvent; + [FieldOffset (4)] + public MouseEventRecord MouseEvent; + [FieldOffset (4)] + public WindowBufferSizeRecord WindowBufferSizeEvent; + [FieldOffset (4)] + public MenuEventRecord MenuEvent; + [FieldOffset (4)] + public FocusEventRecord FocusEvent; + + public override readonly string ToString () + { + return EventType switch { + EventType.Focus => FocusEvent.ToString (), + EventType.Key => KeyEvent.ToString (), + EventType.Menu => MenuEvent.ToString (), + EventType.Mouse => MouseEvent.ToString (), + EventType.WindowBufferSize => WindowBufferSizeEvent.ToString (), + _ => "Unknown event type: " + EventType + }; + } + }; + + [Flags] + enum ShareMode : uint { + FileShareRead = 1, + FileShareWrite = 2, + } + + [Flags] + enum DesiredAccess : uint { + GenericRead = 2147483648, + GenericWrite = 1073741824, + } + + [StructLayout (LayoutKind.Sequential)] + public struct ConsoleScreenBufferInfo { + public Coord dwSize; + public Coord dwCursorPosition; + public ushort wAttributes; + public SmallRect srWindow; + public Coord dwMaximumWindowSize; + } + + [StructLayout (LayoutKind.Sequential)] + public struct Coord { + public short X; + public short Y; + + public Coord (short x, short y) + { + X = x; + Y = y; + } + public override readonly string ToString () => $"({X},{Y})"; + }; + + [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] + public struct CharUnion { + [FieldOffset (0)] public char UnicodeChar; + [FieldOffset (0)] public byte AsciiChar; + } + + [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)] + public struct CharInfo { + [FieldOffset (0)] public CharUnion Char; + [FieldOffset (2)] public ushort Attributes; + } + + public struct ExtendedCharInfo { + public char Char { get; set; } + public Attribute Attribute { get; set; } + public bool Empty { get; set; } // TODO: Temp hack until virutal terminal sequences + + public ExtendedCharInfo (char character, Attribute attribute) + { + Char = character; + Attribute = attribute; + Empty = false; + } + } + + [StructLayout (LayoutKind.Sequential)] + public struct SmallRect { + public short Left; + public short Top; + public short Right; + public short Bottom; + + public SmallRect (short left, short top, short right, short bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public static void MakeEmpty (ref SmallRect rect) + { + rect.Left = -1; + } + + public static void Update (ref SmallRect rect, short col, short row) + { + if (rect.Left == -1) { + rect.Left = rect.Right = col; + rect.Bottom = rect.Top = row; + return; + } + if (col >= rect.Left && col <= rect.Right && row >= rect.Top && row <= rect.Bottom) + return; + if (col < rect.Left) + rect.Left = col; + if (col > rect.Right) + rect.Right = col; + if (row < rect.Top) + rect.Top = row; + if (row > rect.Bottom) + rect.Bottom = row; + } + + public override readonly string ToString () => $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}"; + } + + [StructLayout (LayoutKind.Sequential)] + public struct ConsoleKeyInfoEx { + public ConsoleKeyInfo ConsoleKeyInfo; + public bool CapsLock; + public bool NumLock; + public bool ScrollLock; + + public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock) + { + ConsoleKeyInfo = consoleKeyInfo; + CapsLock = capslock; + NumLock = numlock; + ScrollLock = scrolllock; + } + } + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern IntPtr GetStdHandle (int nStdHandle); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool CloseHandle (IntPtr handle); + + [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)] + public static extern bool ReadConsoleInput ( + IntPtr hConsoleInput, + IntPtr lpBuffer, + uint nLength, + out uint lpNumberOfEventsRead); + + [DllImport ("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool ReadConsoleOutput ( + IntPtr hConsoleOutput, + [Out] CharInfo [] lpBuffer, + Coord dwBufferSize, + Coord dwBufferCoord, + ref SmallRect lpReadRegion + ); + + // TODO: This API is obsolete. See https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput + [DllImport ("kernel32.dll", EntryPoint = "WriteConsoleOutputW", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool WriteConsoleOutput ( + IntPtr hConsoleOutput, + CharInfo [] lpBuffer, + Coord dwBufferSize, + Coord dwBufferCoord, + ref SmallRect lpWriteRegion + ); + + [DllImport ("kernel32.dll", EntryPoint = "WriteConsole", SetLastError = true, CharSet = CharSet.Unicode)] + static extern bool WriteConsole ( + IntPtr hConsoleOutput, + String lpbufer, + UInt32 NumberOfCharsToWriten, + out UInt32 lpNumberOfCharsWritten, + object lpReserved + ); + + [DllImport ("kernel32.dll")] + static extern bool SetConsoleCursorPosition (IntPtr hConsoleOutput, Coord dwCursorPosition); + + [StructLayout (LayoutKind.Sequential)] + public struct ConsoleCursorInfo { + public uint dwSize; + public bool bVisible; + } + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleCursorInfo (IntPtr hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool GetConsoleCursorInfo (IntPtr hConsoleOutput, out ConsoleCursorInfo lpConsoleCursorInfo); + + [DllImport ("kernel32.dll")] + static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode); + + [DllImport ("kernel32.dll")] + static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern IntPtr CreateConsoleScreenBuffer ( + DesiredAccess dwDesiredAccess, + ShareMode dwShareMode, + IntPtr secutiryAttributes, + uint flags, + IntPtr screenBufferData + ); + + internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr (-1); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleActiveScreenBuffer (IntPtr Handle); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents); + + public InputRecord [] ReadConsoleInput () + { + const int bufferSize = 1; + var pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * bufferSize); + try { + ReadConsoleInput (_inputHandle, pRecord, bufferSize, + out var numberEventsRead); + + return numberEventsRead == 0 + ? null + : new [] { Marshal.PtrToStructure (pRecord) }; + } catch (Exception) { + return null; + } finally { + Marshal.FreeHGlobal (pRecord); + } + } #if false // Not needed on the constructor. Perhaps could be used on resizing. To study. [DllImport ("kernel32.dll", ExactSpelling = true)] @@ -770,1097 +694,963 @@ internal void ShowWindow (int state) ShowWindow (thisConsole, state); } #endif - // See: https://github.com/gui-cs/Terminal.Gui/issues/357 - - [StructLayout(LayoutKind.Sequential)] - public struct CONSOLE_SCREEN_BUFFER_INFOEX - { - public uint cbSize; - public Coord dwSize; - public Coord dwCursorPosition; - public ushort wAttributes; - public SmallRect srWindow; - public Coord dwMaximumWindowSize; - public ushort wPopupAttributes; - public bool bFullscreenSupported; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public COLORREF[] ColorTable; - } - - [StructLayout(LayoutKind.Explicit, Size = 4)] - public struct COLORREF - { - public COLORREF(byte r, byte g, byte b) - { - Value = 0; - R = r; - G = g; - B = b; - } - - public COLORREF(uint value) - { - R = 0; - G = 0; - B = 0; - Value = value & 0x00FFFFFF; - } - - [FieldOffset(0)] - public byte R; - [FieldOffset(1)] - public byte G; - [FieldOffset(2)] - public byte B; - - [FieldOffset(0)] - public uint Value; - } - - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool GetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX ConsoleScreenBufferInfo); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleWindowInfo( - IntPtr hConsoleOutput, - bool bAbsolute, - [In] ref SmallRect lpConsoleWindow); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern Coord GetLargestConsoleWindowSize( - IntPtr hConsoleOutput); + // See: https://github.com/gui-cs/Terminal.Gui/issues/357 + + [StructLayout (LayoutKind.Sequential)] + public struct CONSOLE_SCREEN_BUFFER_INFOEX { + public uint cbSize; + public Coord dwSize; + public Coord dwCursorPosition; + public ushort wAttributes; + public SmallRect srWindow; + public Coord dwMaximumWindowSize; + public ushort wPopupAttributes; + public bool bFullscreenSupported; + + [MarshalAs (UnmanagedType.ByValArray, SizeConst = 16)] + public COLORREF [] ColorTable; + } + + [StructLayout (LayoutKind.Explicit, Size = 4)] + public struct COLORREF { + public COLORREF (byte r, byte g, byte b) + { + Value = 0; + R = r; + G = g; + B = b; + } + + public COLORREF (uint value) + { + R = 0; + G = 0; + B = 0; + Value = value & 0x00FFFFFF; + } + + [FieldOffset (0)] + public byte R; + [FieldOffset (1)] + public byte G; + [FieldOffset (2)] + public byte B; + + [FieldOffset (0)] + public uint Value; + } + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool GetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX ConsoleScreenBufferInfo); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern bool SetConsoleWindowInfo ( + IntPtr hConsoleOutput, + bool bAbsolute, + [In] ref SmallRect lpConsoleWindow); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern Coord GetLargestConsoleWindowSize ( + IntPtr hConsoleOutput); } -internal class WindowsDriver : ConsoleDriver -{ - WindowsConsole.ExtendedCharInfo[] _outputBuffer; - WindowsConsole.SmallRect _damageRegion; - Action _keyHandler; - Action _keyDownHandler; - Action _keyUpHandler; - Action _mouseHandler; - - public WindowsConsole WinConsole { get; private set; } - - public override bool SupportsTrueColor => Environment.OSVersion.Version.Build >= 14931; - - public WindowsDriver() - { - WinConsole = new WindowsConsole(); - Clipboard = new WindowsClipboard(); - } - - public override void PrepareToRun(MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) - { - _keyHandler = keyHandler; - _keyDownHandler = keyDownHandler; - _keyUpHandler = keyUpHandler; - _mouseHandler = mouseHandler; - - var mLoop = mainLoop.MainLoopDriver as WindowsMainLoop; - - mLoop.ProcessInput = (e) => ProcessInput(e); - - mLoop.WinChanged = (s, e) => - { - ChangeWin(e.Size); - }; - } - - private void ChangeWin(Size e) - { - var w = e.Width; - if (w == Cols - 3 && e.Height < Rows) - { - w += 3; - } - var newSize = WinConsole.SetConsoleWindow( - (short)Math.Max(w, 16), (short)Math.Max(e.Height, 0)); - Left = 0; - Top = 0; - Cols = newSize.Width; - Rows = newSize.Height; - ResizeScreen(); - ClearContents(); - TerminalResized.Invoke(); - } - - void ProcessInput(WindowsConsole.InputRecord inputEvent) - { - switch (inputEvent.EventType) - { - case WindowsConsole.EventType.Key: - var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet; - if (fromPacketKey) - { - inputEvent.KeyEvent = FromVKPacketToKeyEventRecord(inputEvent.KeyEvent); - } - var map = MapKey(ToConsoleKeyInfoEx(inputEvent.KeyEvent)); - //var ke = inputEvent.KeyEvent; - //System.Diagnostics.Debug.WriteLine ($"fromPacketKey: {fromPacketKey}"); - //if (ke.UnicodeChar == '\0') { - // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 0'\\0'"); - //} else if (ke.UnicodeChar == 13) { - // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 13'\\n'"); - //} else { - // System.Diagnostics.Debug.WriteLine ($"UnicodeChar: {(uint)ke.UnicodeChar}'{ke.UnicodeChar}'"); - //} - //System.Diagnostics.Debug.WriteLine ($"bKeyDown: {ke.bKeyDown}"); - //System.Diagnostics.Debug.WriteLine ($"dwControlKeyState: {ke.dwControlKeyState}"); - //System.Diagnostics.Debug.WriteLine ($"wRepeatCount: {ke.wRepeatCount}"); - //System.Diagnostics.Debug.WriteLine ($"wVirtualKeyCode: {ke.wVirtualKeyCode}"); - //System.Diagnostics.Debug.WriteLine ($"wVirtualScanCode: {ke.wVirtualScanCode}"); - - if (map == (Key)0xffffffff) - { - KeyEvent key = new KeyEvent(); - - // Shift = VK_SHIFT = 0x10 - // Ctrl = VK_CONTROL = 0x11 - // Alt = VK_MENU = 0x12 - - if (inputEvent.KeyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.CapslockOn)) - { - inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn; - } - - if (inputEvent.KeyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.ScrolllockOn)) - { - inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn; - } - - if (inputEvent.KeyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.NumlockOn)) - { - inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn; - } - - switch (inputEvent.KeyEvent.dwControlKeyState) - { - case WindowsConsole.ControlKeyState.RightAltPressed: - case WindowsConsole.ControlKeyState.RightAltPressed | - WindowsConsole.ControlKeyState.LeftControlPressed | - WindowsConsole.ControlKeyState.EnhancedKey: - case WindowsConsole.ControlKeyState.EnhancedKey: - key = new KeyEvent(Key.CtrlMask | Key.AltMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.LeftAltPressed: - key = new KeyEvent(Key.AltMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.RightControlPressed: - case WindowsConsole.ControlKeyState.LeftControlPressed: - key = new KeyEvent(Key.CtrlMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.ShiftPressed: - key = new KeyEvent(Key.ShiftMask, _keyModifiers); - break; - case WindowsConsole.ControlKeyState.NumlockOn: - break; - case WindowsConsole.ControlKeyState.ScrolllockOn: - break; - case WindowsConsole.ControlKeyState.CapslockOn: - break; - default: - key = inputEvent.KeyEvent.wVirtualKeyCode switch - { - 0x10 => new KeyEvent(Key.ShiftMask, _keyModifiers), - 0x11 => new KeyEvent(Key.CtrlMask, _keyModifiers), - 0x12 => new KeyEvent(Key.AltMask, _keyModifiers), - _ => new KeyEvent(Key.Unknown, _keyModifiers) - }; - break; - } - - if (inputEvent.KeyEvent.bKeyDown) - { - _keyDownHandler(key); - } - else - { - _keyUpHandler(key); - } - } - else - { - if (inputEvent.KeyEvent.bKeyDown) - { - // May occurs using SendKeys - _keyModifiers ??= new KeyModifiers(); - // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event - _keyDownHandler(new KeyEvent(map, _keyModifiers)); - _keyHandler(new KeyEvent(map, _keyModifiers)); - } - else - { - _keyUpHandler(new KeyEvent(map, _keyModifiers)); - } - } - if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) - { - _keyModifiers = null; - } - break; - - case WindowsConsole.EventType.Mouse: - var me = ToDriverMouse(inputEvent.MouseEvent); - _mouseHandler(me); - if (_processButtonClick) - { - _mouseHandler( - new MouseEvent() - { - X = me.X, - Y = me.Y, - Flags = ProcessButtonClick(inputEvent.MouseEvent) - }); - } - break; - - case WindowsConsole.EventType.Focus: - break; - } - } - - WindowsConsole.ButtonState? _lastMouseButtonPressed = null; - bool _isButtonPressed = false; - bool _isButtonReleased = false; - bool _isButtonDoubleClicked = false; - Point? _point; - Point _pointMove; - bool _isOneFingerDoubleClicked = false; - bool _processButtonClick; - - MouseEvent ToDriverMouse(WindowsConsole.MouseEventRecord mouseEvent) - { - MouseFlags mouseFlag = MouseFlags.AllEvents; - - //System.Diagnostics.Debug.WriteLine ( - // $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}"); - - if (_isButtonDoubleClicked || _isOneFingerDoubleClicked) - { - Application.MainLoop.AddIdle(() => - { - Task.Run(async () => await ProcessButtonDoubleClickedAsync()); - return false; - }); - } - - // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button. - // This will tell when a mouse button is pressed. When the button is released this event will - // be fired with it's bit set to 0. So when the button is up ButtonState will be 0. - // To map to the correct driver events we save the last pressed mouse button so we can - // map to the correct clicked event. - if ((_lastMouseButtonPressed != null || _isButtonReleased) && mouseEvent.ButtonState != 0) - { - _lastMouseButtonPressed = null; - //isButtonPressed = false; - _isButtonReleased = false; - } - - var p = new Point() - { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y - }; - - if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && _lastMouseButtonPressed == null && !_isButtonDoubleClicked) || - (_lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag(WindowsConsole.EventFlags.MouseMoved) && - mouseEvent.ButtonState != 0 && !_isButtonReleased && !_isButtonDoubleClicked)) - { - switch (mouseEvent.ButtonState) - { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Pressed; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Pressed; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Pressed; - break; - } - - if (_point == null) - { - _point = p; - } - - if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) - { - mouseFlag |= MouseFlags.ReportMousePosition; - _isButtonReleased = false; - _processButtonClick = false; - } - _lastMouseButtonPressed = mouseEvent.ButtonState; - _isButtonPressed = true; - - if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) - { - Application.MainLoop.AddIdle(() => - { - Task.Run(async () => await ProcessContinuousButtonPressedAsync(mouseFlag)); - return false; - }); - } - - } - else if (_lastMouseButtonPressed != null && mouseEvent.EventFlags == 0 - && !_isButtonReleased && !_isButtonDoubleClicked && !_isOneFingerDoubleClicked) - { - switch (_lastMouseButtonPressed) - { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Released; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Released; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Released; - break; - } - _isButtonPressed = false; - _isButtonReleased = true; - if (_point != null && (((Point)_point).X == mouseEvent.MousePosition.X && ((Point)_point).Y == mouseEvent.MousePosition.Y)) - { - _processButtonClick = true; - } - else - { - _point = null; - } - } - else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved - && !_isOneFingerDoubleClicked && _isButtonReleased && p == _point) - { - - mouseFlag = ProcessButtonClick(mouseEvent); - - } - else if (mouseEvent.EventFlags.HasFlag(WindowsConsole.EventFlags.DoubleClick)) - { - switch (mouseEvent.ButtonState) - { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1DoubleClicked; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2DoubleClicked; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3DoubleClicked; - break; - } - _isButtonDoubleClicked = true; - } - else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && _isButtonDoubleClicked) - { - switch (mouseEvent.ButtonState) - { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1TripleClicked; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2TripleClicked; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3TripleClicked; - break; - } - _isButtonDoubleClicked = false; - } - else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) - { - switch ((int)mouseEvent.ButtonState) - { - case int v when v > 0: - mouseFlag = MouseFlags.WheeledUp; - break; - - case int v when v < 0: - mouseFlag = MouseFlags.WheeledDown; - break; - } - - } - else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled && - mouseEvent.ControlKeyState == WindowsConsole.ControlKeyState.ShiftPressed) - { - switch ((int)mouseEvent.ButtonState) - { - case int v when v > 0: - mouseFlag = MouseFlags.WheeledLeft; - break; - - case int v when v < 0: - mouseFlag = MouseFlags.WheeledRight; - break; - } - - } - else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseHorizontalWheeled) - { - switch ((int)mouseEvent.ButtonState) - { - case int v when v < 0: - mouseFlag = MouseFlags.WheeledLeft; - break; - - case int v when v > 0: - mouseFlag = MouseFlags.WheeledRight; - break; - } - - } - else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) - { - mouseFlag = MouseFlags.ReportMousePosition; - if (mouseEvent.MousePosition.X != _pointMove.X || mouseEvent.MousePosition.Y != _pointMove.Y) - { - _pointMove = new Point(mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y); - } - } - else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) - { - mouseFlag = 0; - } - - mouseFlag = SetControlKeyStates(mouseEvent, mouseFlag); - - //System.Diagnostics.Debug.WriteLine ( - // $"point.X:{(point != null ? ((Point)point).X : -1)};point.Y:{(point != null ? ((Point)point).Y : -1)}"); - - return new MouseEvent() - { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y, - Flags = mouseFlag - }; - } - - MouseFlags ProcessButtonClick(WindowsConsole.MouseEventRecord mouseEvent) - { - MouseFlags mouseFlag = 0; - switch (_lastMouseButtonPressed) - { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Clicked; - break; - - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Clicked; - break; - - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Clicked; - break; - } - _point = new Point() - { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y - }; - _lastMouseButtonPressed = null; - _isButtonReleased = false; - _processButtonClick = false; - _point = null; - return mouseFlag; - } - - async Task ProcessButtonDoubleClickedAsync() - { - await Task.Delay(300); - _isButtonDoubleClicked = false; - _isOneFingerDoubleClicked = false; - //buttonPressedCount = 0; - } - - async Task ProcessContinuousButtonPressedAsync(MouseFlags mouseFlag) - { - while (_isButtonPressed) - { - await Task.Delay(100); - var me = new MouseEvent() - { - X = _pointMove.X, - Y = _pointMove.Y, - Flags = mouseFlag - }; - - var view = Application.WantContinuousButtonPressedView; - if (view == null) - { - break; - } - if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) - { - Application.MainLoop.Invoke(() => _mouseHandler(me)); - } - } - } - - static MouseFlags SetControlKeyStates(WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag) - { - if (mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.RightControlPressed) || - mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.LeftControlPressed)) - { - mouseFlag |= MouseFlags.ButtonCtrl; - } - - if (mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.ShiftPressed)) - { - mouseFlag |= MouseFlags.ButtonShift; - } - - if (mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.RightAltPressed) || - mouseEvent.ControlKeyState.HasFlag(WindowsConsole.ControlKeyState.LeftAltPressed)) - { - mouseFlag |= MouseFlags.ButtonAlt; - } - return mouseFlag; - } - - KeyModifiers _keyModifiers; - - public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx(WindowsConsole.KeyEventRecord keyEvent) - { - var state = keyEvent.dwControlKeyState; - - var shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0; - var alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0; - var control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0; - var capsLock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0; - var numLock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0; - var scrollLock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0; - - _keyModifiers ??= new KeyModifiers(); - if (shift) - { - _keyModifiers.Shift = true; - } - if (alt) - { - _keyModifiers.Alt = true; - } - if (control) - { - _keyModifiers.Ctrl = true; - } - if (capsLock) - { - _keyModifiers.Capslock = true; - } - if (numLock) - { - _keyModifiers.Numlock = true; - } - if (scrollLock) - { - _keyModifiers.Scrolllock = true; - } - - var consoleKeyInfo = new ConsoleKeyInfo(keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); - - return new WindowsConsole.ConsoleKeyInfoEx(consoleKeyInfo, capsLock, numLock, scrollLock); - } - - public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord(WindowsConsole.KeyEventRecord keyEvent) - { - if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) - { - return keyEvent; - } - - var mod = new ConsoleModifiers(); - if (keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.ShiftPressed)) - { - mod |= ConsoleModifiers.Shift; - } - if (keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.RightAltPressed) || - keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.LeftAltPressed)) - { - mod |= ConsoleModifiers.Alt; - } - if (keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.LeftControlPressed) || - keyEvent.dwControlKeyState.HasFlag(WindowsConsole.ControlKeyState.RightControlPressed)) - { - mod |= ConsoleModifiers.Control; - } - var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey(keyEvent.UnicodeChar, mod, out uint virtualKey, out uint scanCode); - - return new WindowsConsole.KeyEventRecord - { - UnicodeChar = (char)keyChar, - bKeyDown = keyEvent.bKeyDown, - dwControlKeyState = keyEvent.dwControlKeyState, - wRepeatCount = keyEvent.wRepeatCount, - wVirtualKeyCode = (ushort)virtualKey, - wVirtualScanCode = (ushort)scanCode - }; - } - - public Key MapKey(WindowsConsole.ConsoleKeyInfoEx keyInfoEx) - { - var keyInfo = keyInfoEx.ConsoleKeyInfo; - switch (keyInfo.Key) - { - case ConsoleKey.Escape: - return MapKeyModifiers(keyInfo, Key.Esc); - case ConsoleKey.Tab: - return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; - case ConsoleKey.Clear: - return MapKeyModifiers(keyInfo, Key.Clear); - case ConsoleKey.Home: - return MapKeyModifiers(keyInfo, Key.Home); - case ConsoleKey.End: - return MapKeyModifiers(keyInfo, Key.End); - case ConsoleKey.LeftArrow: - return MapKeyModifiers(keyInfo, Key.CursorLeft); - case ConsoleKey.RightArrow: - return MapKeyModifiers(keyInfo, Key.CursorRight); - case ConsoleKey.UpArrow: - return MapKeyModifiers(keyInfo, Key.CursorUp); - case ConsoleKey.DownArrow: - return MapKeyModifiers(keyInfo, Key.CursorDown); - case ConsoleKey.PageUp: - return MapKeyModifiers(keyInfo, Key.PageUp); - case ConsoleKey.PageDown: - return MapKeyModifiers(keyInfo, Key.PageDown); - case ConsoleKey.Enter: - return MapKeyModifiers(keyInfo, Key.Enter); - case ConsoleKey.Spacebar: - return MapKeyModifiers(keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); - case ConsoleKey.Backspace: - return MapKeyModifiers(keyInfo, Key.Backspace); - case ConsoleKey.Delete: - return MapKeyModifiers(keyInfo, Key.DeleteChar); - case ConsoleKey.Insert: - return MapKeyModifiers(keyInfo, Key.InsertChar); - case ConsoleKey.PrintScreen: - return MapKeyModifiers(keyInfo, Key.PrintScreen); - - case ConsoleKey.NumPad0: - return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar; - case ConsoleKey.NumPad1: - return keyInfoEx.NumLock ? Key.D1 : Key.End; - case ConsoleKey.NumPad2: - return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown; - case ConsoleKey.NumPad3: - return keyInfoEx.NumLock ? Key.D3 : Key.PageDown; - case ConsoleKey.NumPad4: - return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft; - case ConsoleKey.NumPad5: - return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar); - case ConsoleKey.NumPad6: - return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight; - case ConsoleKey.NumPad7: - return keyInfoEx.NumLock ? Key.D7 : Key.Home; - case ConsoleKey.NumPad8: - return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp; - case ConsoleKey.NumPad9: - return keyInfoEx.NumLock ? Key.D9 : Key.PageUp; - - case ConsoleKey.Oem1: - case ConsoleKey.Oem2: - case ConsoleKey.Oem3: - case ConsoleKey.Oem4: - case ConsoleKey.Oem5: - case ConsoleKey.Oem6: - case ConsoleKey.Oem7: - case ConsoleKey.Oem8: - case ConsoleKey.Oem102: - case ConsoleKey.OemPeriod: - case ConsoleKey.OemComma: - case ConsoleKey.OemPlus: - case ConsoleKey.OemMinus: - if (keyInfo.KeyChar == 0) - { - return Key.Unknown; - } - - return (Key)((uint)keyInfo.KeyChar); - } - - var key = keyInfo.Key; - //var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a'; - - if (key >= ConsoleKey.A && key <= ConsoleKey.Z) - { - var delta = key - ConsoleKey.A; - if (keyInfo.Modifiers == ConsoleModifiers.Control) - { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Alt) - { - return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); - } - if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) - { - return MapKeyModifiers(keyInfo, (Key)((uint)Key.A + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) - { - if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) - { - return MapKeyModifiers(keyInfo, (Key)((uint)Key.A + delta)); - } - } - //return (Key)((uint)alphaBase + delta); - return (Key)((uint)keyInfo.KeyChar); - } - if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) - { - var delta = key - ConsoleKey.D0; - if (keyInfo.Modifiers == ConsoleModifiers.Alt) - { - return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == ConsoleModifiers.Control) - { - return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); - } - if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) - { - return MapKeyModifiers(keyInfo, (Key)((uint)Key.D0 + delta)); - } - if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) - { - if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) - { - return MapKeyModifiers(keyInfo, (Key)((uint)Key.D0 + delta)); - } - } - return (Key)((uint)keyInfo.KeyChar); - } - if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) - { - var delta = key - ConsoleKey.F1; - if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) - { - return MapKeyModifiers(keyInfo, (Key)((uint)Key.F1 + delta)); - } - - return (Key)((uint)Key.F1 + delta); - } - if (keyInfo.KeyChar != 0) - { - return MapKeyModifiers(keyInfo, (Key)((uint)keyInfo.KeyChar)); - } - - return (Key)(0xffffffff); - } - - private Key MapKeyModifiers(ConsoleKeyInfo keyInfo, Key key) - { - Key keyMod = new Key(); - if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) - { - keyMod = Key.ShiftMask; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) - { - keyMod |= Key.CtrlMask; - } - if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) - { - keyMod |= Key.AltMask; - } - - return keyMod != Key.Null ? keyMod | key : key; - } - - public override bool IsRuneSupported(Rune rune) - { - return base.IsRuneSupported(rune) && rune.IsBmp; - } - - public override void Init(Action terminalResized) - { - TerminalResized = terminalResized; - - try - { - // Needed for Windows Terminal - Console.Out.Write(EscSeqUtils.CSI_ActivateAltBufferNoBackscroll); - - var winSize = WinConsole.GetConsoleOutputWindow(out Point pos); - Cols = winSize.Width; - Rows = winSize.Height; - WindowsConsole.SmallRect.MakeEmpty(ref _damageRegion); - - } - catch (Win32Exception) - { - // Likely running unit tests. Set WinConsole to null so we can test it elsewhere. - WinConsole = null; - } - - CurrentAttribute = MakeColor(Color.White, Color.Black); - InitializeColorSchemes(); - - _outputBuffer = new WindowsConsole.ExtendedCharInfo[Rows * Cols]; - Clip = new Rect(0, 0, Cols, Rows); - _damageRegion = new WindowsConsole.SmallRect() - { - Top = 0, - Left = 0, - Bottom = (short)Rows, - Right = (short)Cols - }; - - ClearContents(); - } - - public virtual void ResizeScreen() - { - if (WinConsole == null) - { - return; - } - - _outputBuffer = new WindowsConsole.ExtendedCharInfo[Rows * Cols]; - Clip = new Rect(0, 0, Cols, Rows); - _damageRegion = new WindowsConsole.SmallRect() - { - Top = 0, - Left = 0, - Bottom = (short)Rows, - Right = (short)Cols - }; - _dirtyLines = new bool[Rows]; - - WinConsole.ForceRefreshCursorVisibility(); - } - - - public override void UpdateScreen() - { - var windowSize = WinConsole.GetConsoleBufferWindow(out _); - if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) - { - return; - } - - var bufferCoords = new WindowsConsole.Coord() - { - X = (short)Clip.Width, - Y = (short)Clip.Height - }; - - for (int row = 0; row < Rows; row++) - { - if (!_dirtyLines[row]) - { - continue; - } - _dirtyLines[row] = false; - - for (int col = 0; col < Cols; col++) - { - int position = row * Cols + col; - _outputBuffer[position].Attribute = Contents[row, col].Attribute.GetValueOrDefault(); - if (Contents[row, col].IsDirty == false) - { - _outputBuffer[position].Empty = true; - _outputBuffer[position].Char = (char)Rune.ReplacementChar.Value; - continue; - } - _outputBuffer[position].Empty = false; - if (Contents[row, col].Runes[0].IsBmp) - { - _outputBuffer[position].Char = (char)Contents[row, col].Runes[0].Value; - } - else - { - //_outputBuffer [position].Empty = true; - _outputBuffer[position].Char = (char)Rune.ReplacementChar.Value; - if (Contents[row, col].Runes[0].GetColumns() > 1 && col + 1 < Cols) - { - // TODO: This is a hack to deal with non-BMP and wide characters. - col++; - position = row * Cols + col; - _outputBuffer[position].Empty = false; - _outputBuffer[position].Char = ' '; - } - } - } - } - - WinConsole.WriteToConsole(new Size(Cols, Rows), _outputBuffer, bufferCoords, _damageRegion, UseTrueColor); - WindowsConsole.SmallRect.MakeEmpty(ref _damageRegion); - } - - public override void Refresh() - { - UpdateScreen(); - WinConsole.SetInitialCursorVisibility(); - UpdateCursor(); - } - - #region Color Handling - - /// - /// In the WindowsDriver, colors are encoded as an int. - /// The background color is stored in the least significant 4 bits, - /// and the foreground color is stored in the next 4 bits. - /// - public override Attribute MakeColor(Color foreground, Color background) - { - // Encode the colors into the int value. - return new Attribute( - value: (((int)foreground) | ((int)background << 4)), - foreground: foreground, - background: background - ); - } - - /// - /// Extracts the foreground and background colors from the encoded value. - /// Assumes a 4-bit encoded value for both foreground and background colors. - /// - internal override void GetColors(int value, out Color foreground, out Color background) - { - // Assume a 4-bit encoded value for both foreground and background colors. - foreground = (Color)((value >> 16) & 0xF); - background = (Color)(value & 0xF); - } - - #endregion - - CursorVisibility savedCursorVisibility; - - public override void UpdateCursor() - { - if (Col < 0 || Row < 0 || Col > Cols || Row > Rows) - { - GetCursorVisibility(out CursorVisibility cursorVisibility); - savedCursorVisibility = cursorVisibility; - SetCursorVisibility(CursorVisibility.Invisible); - return; - } - - SetCursorVisibility(savedCursorVisibility); - var position = new WindowsConsole.Coord() - { - X = (short)Col, - Y = (short)Row - }; - WinConsole.SetCursorPosition(position); - } - - /// - public override bool GetCursorVisibility(out CursorVisibility visibility) - { - return WinConsole.GetCursorVisibility(out visibility); - } - - /// - public override bool SetCursorVisibility(CursorVisibility visibility) - { - savedCursorVisibility = visibility; - return WinConsole.SetCursorVisibility(visibility); - } - - /// - public override bool EnsureCursorVisibility() - { - return WinConsole.EnsureCursorVisibility(); - } - - public override void SendKeys(char keyChar, ConsoleKey key, bool shift, bool alt, bool control) - { - WindowsConsole.InputRecord input = new WindowsConsole.InputRecord - { - EventType = WindowsConsole.EventType.Key - }; - - WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord - { - bKeyDown = true - }; - WindowsConsole.ControlKeyState controlKey = new WindowsConsole.ControlKeyState(); - if (shift) - { - controlKey |= WindowsConsole.ControlKeyState.ShiftPressed; - keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = 16; - } - if (alt) - { - controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed; - controlKey |= WindowsConsole.ControlKeyState.RightAltPressed; - keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = 18; - } - if (control) - { - controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed; - controlKey |= WindowsConsole.ControlKeyState.RightControlPressed; - keyEvent.UnicodeChar = '\0'; - keyEvent.wVirtualKeyCode = 17; - } - keyEvent.dwControlKeyState = controlKey; - - input.KeyEvent = keyEvent; - - if (shift || alt || control) - { - ProcessInput(input); - } - - keyEvent.UnicodeChar = keyChar; - if ((uint)key < 255) - { - keyEvent.wVirtualKeyCode = (ushort)key; - } - else - { - keyEvent.wVirtualKeyCode = '\0'; - } - - input.KeyEvent = keyEvent; - - try - { - ProcessInput(input); - } - catch (OverflowException) { } - finally - { - keyEvent.bKeyDown = false; - input.KeyEvent = keyEvent; - ProcessInput(input); - } - } - - public override void End() - { - WinConsole?.Cleanup(); - WinConsole = null; - - // Disable alternative screen buffer. - Console.Out.Write(EscSeqUtils.CSI_RestoreAltBufferWithBackscroll); - } - - #region Not Implemented - public override void Suspend() - { - throw new NotImplementedException(); - } - #endregion +internal class WindowsDriver : ConsoleDriver { + WindowsConsole.ExtendedCharInfo [] _outputBuffer; + WindowsConsole.SmallRect _damageRegion; + Action _keyHandler; + Action _keyDownHandler; + Action _keyUpHandler; + Action _mouseHandler; + + public WindowsConsole WinConsole { get; private set; } + + public override bool SupportsTrueColor => Environment.OSVersion.Version.Build >= 14931; + + public WindowsDriver () + { + WinConsole = new WindowsConsole (); + Clipboard = new WindowsClipboard (); + } + + public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) + { + _keyHandler = keyHandler; + _keyDownHandler = keyDownHandler; + _keyUpHandler = keyUpHandler; + _mouseHandler = mouseHandler; + + var mLoop = mainLoop.MainLoopDriver as WindowsMainLoop; + + mLoop.ProcessInput = (e) => ProcessInput (e); + + mLoop.WinChanged = (s, e) => { + ChangeWin (e.Size); + }; + } + + private void ChangeWin (Size e) + { + var w = e.Width; + if (w == Cols - 3 && e.Height < Rows) { + w += 3; + } + var newSize = WinConsole.SetConsoleWindow ( + (short)Math.Max (w, 16), (short)Math.Max (e.Height, 0)); + Left = 0; + Top = 0; + Cols = newSize.Width; + Rows = newSize.Height; + ResizeScreen (); + ClearContents (); + TerminalResized.Invoke (); + } + + void ProcessInput (WindowsConsole.InputRecord inputEvent) + { + switch (inputEvent.EventType) { + case WindowsConsole.EventType.Key: + var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet; + if (fromPacketKey) { + inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent); + } + var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent)); + //var ke = inputEvent.KeyEvent; + //System.Diagnostics.Debug.WriteLine ($"fromPacketKey: {fromPacketKey}"); + //if (ke.UnicodeChar == '\0') { + // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 0'\\0'"); + //} else if (ke.UnicodeChar == 13) { + // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 13'\\n'"); + //} else { + // System.Diagnostics.Debug.WriteLine ($"UnicodeChar: {(uint)ke.UnicodeChar}'{ke.UnicodeChar}'"); + //} + //System.Diagnostics.Debug.WriteLine ($"bKeyDown: {ke.bKeyDown}"); + //System.Diagnostics.Debug.WriteLine ($"dwControlKeyState: {ke.dwControlKeyState}"); + //System.Diagnostics.Debug.WriteLine ($"wRepeatCount: {ke.wRepeatCount}"); + //System.Diagnostics.Debug.WriteLine ($"wVirtualKeyCode: {ke.wVirtualKeyCode}"); + //System.Diagnostics.Debug.WriteLine ($"wVirtualScanCode: {ke.wVirtualScanCode}"); + + if (map == (Key)0xffffffff) { + KeyEvent key = new KeyEvent (); + + // Shift = VK_SHIFT = 0x10 + // Ctrl = VK_CONTROL = 0x11 + // Alt = VK_MENU = 0x12 + + if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.CapslockOn)) { + inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn; + } + + if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ScrolllockOn)) { + inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn; + } + + if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.NumlockOn)) { + inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn; + } + + switch (inputEvent.KeyEvent.dwControlKeyState) { + case WindowsConsole.ControlKeyState.RightAltPressed: + case WindowsConsole.ControlKeyState.RightAltPressed | + WindowsConsole.ControlKeyState.LeftControlPressed | + WindowsConsole.ControlKeyState.EnhancedKey: + case WindowsConsole.ControlKeyState.EnhancedKey: + key = new KeyEvent (Key.CtrlMask | Key.AltMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.LeftAltPressed: + key = new KeyEvent (Key.AltMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.RightControlPressed: + case WindowsConsole.ControlKeyState.LeftControlPressed: + key = new KeyEvent (Key.CtrlMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.ShiftPressed: + key = new KeyEvent (Key.ShiftMask, _keyModifiers); + break; + case WindowsConsole.ControlKeyState.NumlockOn: + break; + case WindowsConsole.ControlKeyState.ScrolllockOn: + break; + case WindowsConsole.ControlKeyState.CapslockOn: + break; + default: + key = inputEvent.KeyEvent.wVirtualKeyCode switch { + 0x10 => new KeyEvent (Key.ShiftMask, _keyModifiers), + 0x11 => new KeyEvent (Key.CtrlMask, _keyModifiers), + 0x12 => new KeyEvent (Key.AltMask, _keyModifiers), + _ => new KeyEvent (Key.Unknown, _keyModifiers) + }; + break; + } + + if (inputEvent.KeyEvent.bKeyDown) { + _keyDownHandler (key); + } else { + _keyUpHandler (key); + } + } else { + if (inputEvent.KeyEvent.bKeyDown) { + // May occurs using SendKeys + _keyModifiers ??= new KeyModifiers (); + // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event + _keyDownHandler (new KeyEvent (map, _keyModifiers)); + _keyHandler (new KeyEvent (map, _keyModifiers)); + } else { + _keyUpHandler (new KeyEvent (map, _keyModifiers)); + } + } + if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) { + _keyModifiers = null; + } + break; + + case WindowsConsole.EventType.Mouse: + var me = ToDriverMouse (inputEvent.MouseEvent); + _mouseHandler (me); + if (_processButtonClick) { + _mouseHandler ( + new MouseEvent () { + X = me.X, + Y = me.Y, + Flags = ProcessButtonClick (inputEvent.MouseEvent) + }); + } + break; + + case WindowsConsole.EventType.Focus: + break; + } + } + + WindowsConsole.ButtonState? _lastMouseButtonPressed = null; + bool _isButtonPressed = false; + bool _isButtonReleased = false; + bool _isButtonDoubleClicked = false; + Point? _point; + Point _pointMove; + bool _isOneFingerDoubleClicked = false; + bool _processButtonClick; + + MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent) + { + MouseFlags mouseFlag = MouseFlags.AllEvents; + + //System.Diagnostics.Debug.WriteLine ( + // $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}"); + + if (_isButtonDoubleClicked || _isOneFingerDoubleClicked) { + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); + return false; + }); + } + + // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button. + // This will tell when a mouse button is pressed. When the button is released this event will + // be fired with it's bit set to 0. So when the button is up ButtonState will be 0. + // To map to the correct driver events we save the last pressed mouse button so we can + // map to the correct clicked event. + if ((_lastMouseButtonPressed != null || _isButtonReleased) && mouseEvent.ButtonState != 0) { + _lastMouseButtonPressed = null; + //isButtonPressed = false; + _isButtonReleased = false; + } + + var p = new Point () { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y + }; + + if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && _lastMouseButtonPressed == null && !_isButtonDoubleClicked) || + (_lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.MouseMoved) && + mouseEvent.ButtonState != 0 && !_isButtonReleased && !_isButtonDoubleClicked)) { + switch (mouseEvent.ButtonState) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Pressed; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Pressed; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Pressed; + break; + } + + if (_point == null) { + _point = p; + } + + if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { + mouseFlag |= MouseFlags.ReportMousePosition; + _isButtonReleased = false; + _processButtonClick = false; + } + _lastMouseButtonPressed = mouseEvent.ButtonState; + _isButtonPressed = true; + + if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) { + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag)); + return false; + }); + } + + } else if (_lastMouseButtonPressed != null && mouseEvent.EventFlags == 0 + && !_isButtonReleased && !_isButtonDoubleClicked && !_isOneFingerDoubleClicked) { + switch (_lastMouseButtonPressed) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Released; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Released; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Released; + break; + } + _isButtonPressed = false; + _isButtonReleased = true; + if (_point != null && (((Point)_point).X == mouseEvent.MousePosition.X && ((Point)_point).Y == mouseEvent.MousePosition.Y)) { + _processButtonClick = true; + } else { + _point = null; + } + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved + && !_isOneFingerDoubleClicked && _isButtonReleased && p == _point) { + + mouseFlag = ProcessButtonClick (mouseEvent); + + } else if (mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.DoubleClick)) { + switch (mouseEvent.ButtonState) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1DoubleClicked; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2DoubleClicked; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3DoubleClicked; + break; + } + _isButtonDoubleClicked = true; + } else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && _isButtonDoubleClicked) { + switch (mouseEvent.ButtonState) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1TripleClicked; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2TripleClicked; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3TripleClicked; + break; + } + _isButtonDoubleClicked = false; + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) { + switch ((int)mouseEvent.ButtonState) { + case int v when v > 0: + mouseFlag = MouseFlags.WheeledUp; + break; + + case int v when v < 0: + mouseFlag = MouseFlags.WheeledDown; + break; + } + + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled && + mouseEvent.ControlKeyState == WindowsConsole.ControlKeyState.ShiftPressed) { + switch ((int)mouseEvent.ButtonState) { + case int v when v > 0: + mouseFlag = MouseFlags.WheeledLeft; + break; + + case int v when v < 0: + mouseFlag = MouseFlags.WheeledRight; + break; + } + + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseHorizontalWheeled) { + switch ((int)mouseEvent.ButtonState) { + case int v when v < 0: + mouseFlag = MouseFlags.WheeledLeft; + break; + + case int v when v > 0: + mouseFlag = MouseFlags.WheeledRight; + break; + } + + } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) { + mouseFlag = MouseFlags.ReportMousePosition; + if (mouseEvent.MousePosition.X != _pointMove.X || mouseEvent.MousePosition.Y != _pointMove.Y) { + _pointMove = new Point (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y); + } + } else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) { + mouseFlag = 0; + } + + mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag); + + //System.Diagnostics.Debug.WriteLine ( + // $"point.X:{(point != null ? ((Point)point).X : -1)};point.Y:{(point != null ? ((Point)point).Y : -1)}"); + + return new MouseEvent () { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y, + Flags = mouseFlag + }; + } + + MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent) + { + MouseFlags mouseFlag = 0; + switch (_lastMouseButtonPressed) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Clicked; + break; + + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Clicked; + break; + + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Clicked; + break; + } + _point = new Point () { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y + }; + _lastMouseButtonPressed = null; + _isButtonReleased = false; + _processButtonClick = false; + _point = null; + return mouseFlag; + } + + async Task ProcessButtonDoubleClickedAsync () + { + await Task.Delay (300); + _isButtonDoubleClicked = false; + _isOneFingerDoubleClicked = false; + //buttonPressedCount = 0; + } + + async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag) + { + while (_isButtonPressed) { + await Task.Delay (100); + var me = new MouseEvent () { + X = _pointMove.X, + Y = _pointMove.Y, + Flags = mouseFlag + }; + + var view = Application.WantContinuousButtonPressedView; + if (view == null) { + break; + } + if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { + Application.MainLoop.Invoke (() => _mouseHandler (me)); + } + } + } + + static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag) + { + if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed) || + mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed)) { + mouseFlag |= MouseFlags.ButtonCtrl; + } + + if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) { + mouseFlag |= MouseFlags.ButtonShift; + } + + if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) || + mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) { + mouseFlag |= MouseFlags.ButtonAlt; + } + return mouseFlag; + } + + KeyModifiers _keyModifiers; + + public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent) + { + var state = keyEvent.dwControlKeyState; + + var shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0; + var alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0; + var control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0; + var capsLock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0; + var numLock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0; + var scrollLock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0; + + _keyModifiers ??= new KeyModifiers (); + if (shift) { + _keyModifiers.Shift = true; + } + if (alt) { + _keyModifiers.Alt = true; + } + if (control) { + _keyModifiers.Ctrl = true; + } + if (capsLock) { + _keyModifiers.Capslock = true; + } + if (numLock) { + _keyModifiers.Numlock = true; + } + if (scrollLock) { + _keyModifiers.Scrolllock = true; + } + + var consoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); + + return new WindowsConsole.ConsoleKeyInfoEx (consoleKeyInfo, capsLock, numLock, scrollLock); + } + + public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent) + { + if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) { + return keyEvent; + } + + var mod = new ConsoleModifiers (); + if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) { + mod |= ConsoleModifiers.Shift; + } + if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) || + keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) { + mod |= ConsoleModifiers.Alt; + } + if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed) || + keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed)) { + mod |= ConsoleModifiers.Control; + } + var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyEvent.UnicodeChar, mod, out uint virtualKey, out uint scanCode); + + return new WindowsConsole.KeyEventRecord { + UnicodeChar = (char)keyChar, + bKeyDown = keyEvent.bKeyDown, + dwControlKeyState = keyEvent.dwControlKeyState, + wRepeatCount = keyEvent.wRepeatCount, + wVirtualKeyCode = (ushort)virtualKey, + wVirtualScanCode = (ushort)scanCode + }; + } + + public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx) + { + var keyInfo = keyInfoEx.ConsoleKeyInfo; + switch (keyInfo.Key) { + case ConsoleKey.Escape: + return MapKeyModifiers (keyInfo, Key.Esc); + case ConsoleKey.Tab: + return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; + case ConsoleKey.Clear: + return MapKeyModifiers (keyInfo, Key.Clear); + case ConsoleKey.Home: + return MapKeyModifiers (keyInfo, Key.Home); + case ConsoleKey.End: + return MapKeyModifiers (keyInfo, Key.End); + case ConsoleKey.LeftArrow: + return MapKeyModifiers (keyInfo, Key.CursorLeft); + case ConsoleKey.RightArrow: + return MapKeyModifiers (keyInfo, Key.CursorRight); + case ConsoleKey.UpArrow: + return MapKeyModifiers (keyInfo, Key.CursorUp); + case ConsoleKey.DownArrow: + return MapKeyModifiers (keyInfo, Key.CursorDown); + case ConsoleKey.PageUp: + return MapKeyModifiers (keyInfo, Key.PageUp); + case ConsoleKey.PageDown: + return MapKeyModifiers (keyInfo, Key.PageDown); + case ConsoleKey.Enter: + return MapKeyModifiers (keyInfo, Key.Enter); + case ConsoleKey.Spacebar: + return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar); + case ConsoleKey.Backspace: + return MapKeyModifiers (keyInfo, Key.Backspace); + case ConsoleKey.Delete: + return MapKeyModifiers (keyInfo, Key.DeleteChar); + case ConsoleKey.Insert: + return MapKeyModifiers (keyInfo, Key.InsertChar); + case ConsoleKey.PrintScreen: + return MapKeyModifiers (keyInfo, Key.PrintScreen); + + case ConsoleKey.NumPad0: + return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar; + case ConsoleKey.NumPad1: + return keyInfoEx.NumLock ? Key.D1 : Key.End; + case ConsoleKey.NumPad2: + return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown; + case ConsoleKey.NumPad3: + return keyInfoEx.NumLock ? Key.D3 : Key.PageDown; + case ConsoleKey.NumPad4: + return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft; + case ConsoleKey.NumPad5: + return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar); + case ConsoleKey.NumPad6: + return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight; + case ConsoleKey.NumPad7: + return keyInfoEx.NumLock ? Key.D7 : Key.Home; + case ConsoleKey.NumPad8: + return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp; + case ConsoleKey.NumPad9: + return keyInfoEx.NumLock ? Key.D9 : Key.PageUp; + + case ConsoleKey.Oem1: + case ConsoleKey.Oem2: + case ConsoleKey.Oem3: + case ConsoleKey.Oem4: + case ConsoleKey.Oem5: + case ConsoleKey.Oem6: + case ConsoleKey.Oem7: + case ConsoleKey.Oem8: + case ConsoleKey.Oem102: + case ConsoleKey.OemPeriod: + case ConsoleKey.OemComma: + case ConsoleKey.OemPlus: + case ConsoleKey.OemMinus: + if (keyInfo.KeyChar == 0) { + return Key.Unknown; + } + + return (Key)((uint)keyInfo.KeyChar); + } + + var key = keyInfo.Key; + //var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a'; + + if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { + var delta = key - ConsoleKey.A; + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); + } + } + //return (Key)((uint)alphaBase + delta); + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { + var delta = key - ConsoleKey.D0; + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); + } + } + return (Key)((uint)keyInfo.KeyChar); + } + if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { + var delta = key - ConsoleKey.F1; + if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); + } + + return (Key)((uint)Key.F1 + delta); + } + if (keyInfo.KeyChar != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar)); + } + + return (Key)(0xffffffff); + } + + private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) + { + Key keyMod = new Key (); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) { + keyMod = Key.ShiftMask; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) { + keyMod |= Key.CtrlMask; + } + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) { + keyMod |= Key.AltMask; + } + + return keyMod != Key.Null ? keyMod | key : key; + } + + public override bool IsRuneSupported (Rune rune) + { + return base.IsRuneSupported (rune) && rune.IsBmp; + } + + public override void Init (Action terminalResized) + { + TerminalResized = terminalResized; + + try { + // Needed for Windows Terminal + Console.Out.Write (EscSeqUtils.CSI_ActivateAltBufferNoBackscroll); + + var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); + Cols = winSize.Width; + Rows = winSize.Height; + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); + + } catch (Win32Exception) { + // Likely running unit tests. Set WinConsole to null so we can test it elsewhere. + WinConsole = null; + } + + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitializeColorSchemes (); + + _outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols]; + Clip = new Rect (0, 0, Cols, Rows); + _damageRegion = new WindowsConsole.SmallRect () { + Top = 0, + Left = 0, + Bottom = (short)Rows, + Right = (short)Cols + }; + + ClearContents (); + } + + public virtual void ResizeScreen () + { + if (WinConsole == null) { + return; + } + + _outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols]; + Clip = new Rect (0, 0, Cols, Rows); + _damageRegion = new WindowsConsole.SmallRect () { + Top = 0, + Left = 0, + Bottom = (short)Rows, + Right = (short)Cols + }; + _dirtyLines = new bool [Rows]; + + WinConsole.ForceRefreshCursorVisibility (); + } + + + public override void UpdateScreen () + { + var windowSize = WinConsole.GetConsoleBufferWindow (out _); + if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) { + return; + } + + var bufferCoords = new WindowsConsole.Coord () { + X = (short)Clip.Width, + Y = (short)Clip.Height + }; + + for (int row = 0; row < Rows; row++) { + if (!_dirtyLines [row]) { + continue; + } + _dirtyLines [row] = false; + + for (int col = 0; col < Cols; col++) { + int position = row * Cols + col; + _outputBuffer [position].Attribute = Contents [row, col].Attribute.GetValueOrDefault (); + if (Contents [row, col].IsDirty == false) { + _outputBuffer [position].Empty = true; + _outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; + continue; + } + _outputBuffer [position].Empty = false; + if (Contents [row, col].Runes [0].IsBmp) { + _outputBuffer [position].Char = (char)Contents [row, col].Runes [0].Value; + } else { + //_outputBuffer [position].Empty = true; + _outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; + if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { + // TODO: This is a hack to deal with non-BMP and wide characters. + col++; + position = row * Cols + col; + _outputBuffer [position].Empty = false; + _outputBuffer [position].Char = ' '; + } + } + } + } + + WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion, UseTrueColor); + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); + } + + public override void Refresh () + { + UpdateScreen (); + WinConsole.SetInitialCursorVisibility (); + UpdateCursor (); + } + + #region Color Handling + + /// + /// In the WindowsDriver, colors are encoded as an int. + /// The background color is stored in the least significant 4 bits, + /// and the foreground color is stored in the next 4 bits. + /// + public override Attribute MakeColor (Color foreground, Color background) + { + // Encode the colors into the int value. + return new Attribute ( + value: (((int)foreground) | ((int)background << 4)), + foreground: foreground, + background: background + ); + } + + /// + /// Extracts the foreground and background colors from the encoded value. + /// Assumes a 4-bit encoded value for both foreground and background colors. + /// + internal override void GetColors (int value, out Color foreground, out Color background) + { + // Assume a 4-bit encoded value for both foreground and background colors. + foreground = (Color)((value >> 16) & 0xF); + background = (Color)(value & 0xF); + } + + #endregion + + CursorVisibility savedCursorVisibility; + + public override void UpdateCursor () + { + if (Col < 0 || Row < 0 || Col > Cols || Row > Rows) { + GetCursorVisibility (out CursorVisibility cursorVisibility); + savedCursorVisibility = cursorVisibility; + SetCursorVisibility (CursorVisibility.Invisible); + return; + } + + SetCursorVisibility (savedCursorVisibility); + var position = new WindowsConsole.Coord () { + X = (short)Col, + Y = (short)Row + }; + WinConsole.SetCursorPosition (position); + } + + /// + public override bool GetCursorVisibility (out CursorVisibility visibility) + { + return WinConsole.GetCursorVisibility (out visibility); + } + + /// + public override bool SetCursorVisibility (CursorVisibility visibility) + { + savedCursorVisibility = visibility; + return WinConsole.SetCursorVisibility (visibility); + } + + /// + public override bool EnsureCursorVisibility () + { + return WinConsole.EnsureCursorVisibility (); + } + + public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) + { + WindowsConsole.InputRecord input = new WindowsConsole.InputRecord { + EventType = WindowsConsole.EventType.Key + }; + + WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord { + bKeyDown = true + }; + WindowsConsole.ControlKeyState controlKey = new WindowsConsole.ControlKeyState (); + if (shift) { + controlKey |= WindowsConsole.ControlKeyState.ShiftPressed; + keyEvent.UnicodeChar = '\0'; + keyEvent.wVirtualKeyCode = 16; + } + if (alt) { + controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed; + controlKey |= WindowsConsole.ControlKeyState.RightAltPressed; + keyEvent.UnicodeChar = '\0'; + keyEvent.wVirtualKeyCode = 18; + } + if (control) { + controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed; + controlKey |= WindowsConsole.ControlKeyState.RightControlPressed; + keyEvent.UnicodeChar = '\0'; + keyEvent.wVirtualKeyCode = 17; + } + keyEvent.dwControlKeyState = controlKey; + + input.KeyEvent = keyEvent; + + if (shift || alt || control) { + ProcessInput (input); + } + + keyEvent.UnicodeChar = keyChar; + if ((uint)key < 255) { + keyEvent.wVirtualKeyCode = (ushort)key; + } else { + keyEvent.wVirtualKeyCode = '\0'; + } + + input.KeyEvent = keyEvent; + + try { + ProcessInput (input); + } catch (OverflowException) { } finally { + keyEvent.bKeyDown = false; + input.KeyEvent = keyEvent; + ProcessInput (input); + } + } + + public override void End () + { + WinConsole?.Cleanup (); + WinConsole = null; + + // Disable alternative screen buffer. + Console.Out.Write (EscSeqUtils.CSI_RestoreAltBufferWithBackscroll); + } + + #region Not Implemented + public override void Suspend () + { + throw new NotImplementedException (); + } + #endregion } /// @@ -1870,337 +1660,292 @@ public override void Suspend() /// /// This implementation is used for WindowsDriver. /// -internal class WindowsMainLoop : IMainLoopDriver -{ - ManualResetEventSlim _eventReady = new ManualResetEventSlim(false); - ManualResetEventSlim _waitForProbe = new ManualResetEventSlim(false); - ManualResetEventSlim _winChange = new ManualResetEventSlim(false); - MainLoop _mainLoop; - ConsoleDriver _consoleDriver; - WindowsConsole _winConsole; - bool _winChanged; - Size _windowSize; - CancellationTokenSource _tokenSource = new CancellationTokenSource(); - - // The records that we keep fetching - Queue _resultQueue = new Queue(); - - /// - /// Invoked when a Key is pressed or released. - /// - public Action ProcessInput; - - /// - /// Invoked when the window is changed. - /// - public EventHandler WinChanged; - - public WindowsMainLoop(ConsoleDriver consoleDriver = null) - { - _consoleDriver = consoleDriver ?? throw new ArgumentNullException("Console driver instance must be provided."); - _winConsole = ((WindowsDriver)consoleDriver).WinConsole; - } - - void IMainLoopDriver.Setup(MainLoop mainLoop) - { - _mainLoop = mainLoop; - Task.Run(WindowsInputHandler); - Task.Run(CheckWinChange); - } - - void WindowsInputHandler() - { - while (true) - { - _waitForProbe.Wait(); - _waitForProbe.Reset(); - - if (_resultQueue?.Count == 0) - { - _resultQueue.Enqueue(_winConsole.ReadConsoleInput()); - } - - _eventReady.Set(); - } - } - - void CheckWinChange() - { - while (true) - { - _winChange.Wait(); - _winChange.Reset(); - WaitWinChange(); - _winChanged = true; - _eventReady.Set(); - } - } - - void WaitWinChange() - { - while (true) - { - Task.Delay(500).Wait(); - _windowSize = _winConsole.GetConsoleBufferWindow(out _); - if (_windowSize != Size.Empty && _windowSize.Width != _consoleDriver.Cols - || _windowSize.Height != _consoleDriver.Rows) - { - return; - } - } - } - - void IMainLoopDriver.Wakeup() - { - //tokenSource.Cancel (); - _eventReady.Set(); - } - - bool IMainLoopDriver.EventsPending(bool wait) - { - _waitForProbe.Set(); - _winChange.Set(); - - if (CheckTimers(wait, out var waitTimeout)) - { - return true; - } - - try - { - if (!_tokenSource.IsCancellationRequested) - { - _eventReady.Wait(waitTimeout, _tokenSource.Token); - } - } - catch (OperationCanceledException) - { - return true; - } - finally - { - _eventReady.Reset(); - } - - if (!_tokenSource.IsCancellationRequested) - { - return _resultQueue.Count > 0 || CheckTimers(wait, out _) || _winChanged; - } - - _tokenSource.Dispose(); - _tokenSource = new CancellationTokenSource(); - return true; - } - - bool CheckTimers(bool wait, out int waitTimeout) - { - long now = DateTime.UtcNow.Ticks; - - if (_mainLoop.timeouts.Count > 0) - { - waitTimeout = (int)((_mainLoop.timeouts.Keys[0] - now) / TimeSpan.TicksPerMillisecond); - if (waitTimeout < 0) - return true; - } - else - { - waitTimeout = -1; - } - - if (!wait) - waitTimeout = 0; - - int ic; - lock (_mainLoop.idleHandlers) - { - ic = _mainLoop.idleHandlers.Count; - } - - return ic > 0; - } - - void IMainLoopDriver.Iteration() - { - while (_resultQueue.Count > 0) - { - var inputRecords = _resultQueue.Dequeue(); - if (inputRecords != null && inputRecords.Length > 0) - { - var inputEvent = inputRecords[0]; - ProcessInput?.Invoke(inputEvent); - } - } - if (_winChanged) - { - _winChanged = false; - WinChanged?.Invoke(this, new SizeChangedEventArgs(_windowSize)); - } - } - public void TearDown() - { - //throw new NotImplementedException (); - } +internal class WindowsMainLoop : IMainLoopDriver { + ManualResetEventSlim _eventReady = new ManualResetEventSlim (false); + ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false); + ManualResetEventSlim _winChange = new ManualResetEventSlim (false); + MainLoop _mainLoop; + ConsoleDriver _consoleDriver; + WindowsConsole _winConsole; + bool _winChanged; + Size _windowSize; + CancellationTokenSource _tokenSource = new CancellationTokenSource (); + + // The records that we keep fetching + Queue _resultQueue = new Queue (); + + /// + /// Invoked when a Key is pressed or released. + /// + public Action ProcessInput; + + /// + /// Invoked when the window is changed. + /// + public EventHandler WinChanged; + + public WindowsMainLoop (ConsoleDriver consoleDriver = null) + { + _consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided."); + _winConsole = ((WindowsDriver)consoleDriver).WinConsole; + } + + void IMainLoopDriver.Setup (MainLoop mainLoop) + { + _mainLoop = mainLoop; + Task.Run (WindowsInputHandler); + Task.Run (CheckWinChange); + } + + void WindowsInputHandler () + { + while (true) { + _waitForProbe.Wait (); + _waitForProbe.Reset (); + + if (_resultQueue?.Count == 0) { + _resultQueue.Enqueue (_winConsole.ReadConsoleInput ()); + } + + _eventReady.Set (); + } + } + + void CheckWinChange () + { + while (true) { + _winChange.Wait (); + _winChange.Reset (); + WaitWinChange (); + _winChanged = true; + _eventReady.Set (); + } + } + + void WaitWinChange () + { + while (true) { + Task.Delay (500).Wait (); + _windowSize = _winConsole.GetConsoleBufferWindow (out _); + if (_windowSize != Size.Empty && _windowSize.Width != _consoleDriver.Cols + || _windowSize.Height != _consoleDriver.Rows) { + return; + } + } + } + + void IMainLoopDriver.Wakeup () + { + //tokenSource.Cancel (); + _eventReady.Set (); + } + + bool IMainLoopDriver.EventsPending (bool wait) + { + _waitForProbe.Set (); + _winChange.Set (); + + if (CheckTimers (wait, out var waitTimeout)) { + return true; + } + + try { + if (!_tokenSource.IsCancellationRequested) { + _eventReady.Wait (waitTimeout, _tokenSource.Token); + } + } catch (OperationCanceledException) { + return true; + } finally { + _eventReady.Reset (); + } + + if (!_tokenSource.IsCancellationRequested) { + return _resultQueue.Count > 0 || CheckTimers (wait, out _) || _winChanged; + } + + _tokenSource.Dispose (); + _tokenSource = new CancellationTokenSource (); + return true; + } + + bool CheckTimers (bool wait, out int waitTimeout) + { + long now = DateTime.UtcNow.Ticks; + + if (_mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) + return true; + } else { + waitTimeout = -1; + } + + if (!wait) + waitTimeout = 0; + + int ic; + lock (_mainLoop.idleHandlers) { + ic = _mainLoop.idleHandlers.Count; + } + + return ic > 0; + } + + void IMainLoopDriver.Iteration () + { + while (_resultQueue.Count > 0) { + var inputRecords = _resultQueue.Dequeue (); + if (inputRecords != null && inputRecords.Length > 0) { + var inputEvent = inputRecords [0]; + ProcessInput?.Invoke (inputEvent); + } + } + if (_winChanged) { + _winChanged = false; + WinChanged?.Invoke (this, new SizeChangedEventArgs (_windowSize)); + } + } + public void TearDown () + { + //throw new NotImplementedException (); + } } -class WindowsClipboard : ClipboardBase -{ - public WindowsClipboard() - { - IsSupported = IsClipboardFormatAvailable(_cfUnicodeText); - } - - public override bool IsSupported { get; } - - protected override string GetClipboardDataImpl() - { - try - { - if (!OpenClipboard(IntPtr.Zero)) - { - return string.Empty; - } - - IntPtr handle = GetClipboardData(_cfUnicodeText); - if (handle == IntPtr.Zero) - { - return string.Empty; - } - - IntPtr pointer = IntPtr.Zero; - - try - { - pointer = GlobalLock(handle); - if (pointer == IntPtr.Zero) - { - return string.Empty; - } - - int size = GlobalSize(handle); - byte[] buff = new byte[size]; - - Marshal.Copy(pointer, buff, 0, size); - - return System.Text.Encoding.Unicode.GetString(buff).TrimEnd('\0'); - } - finally - { - if (pointer != IntPtr.Zero) - { - GlobalUnlock(handle); - } - } - } - finally - { - CloseClipboard(); - } - } - - protected override void SetClipboardDataImpl(string text) - { - OpenClipboard(); - - EmptyClipboard(); - IntPtr hGlobal = default; - try - { - var bytes = (text.Length + 1) * 2; - hGlobal = Marshal.AllocHGlobal(bytes); - - if (hGlobal == default) - { - ThrowWin32(); - } - - var target = GlobalLock(hGlobal); - - if (target == default) - { - ThrowWin32(); - } - - try - { - Marshal.Copy(text.ToCharArray(), 0, target, text.Length); - } - finally - { - GlobalUnlock(target); - } - - if (SetClipboardData(_cfUnicodeText, hGlobal) == default) - { - ThrowWin32(); - } - - hGlobal = default; - } - finally - { - if (hGlobal != default) - { - Marshal.FreeHGlobal(hGlobal); - } - - CloseClipboard(); - } - } - - void OpenClipboard() - { - var num = 10; - while (true) - { - if (OpenClipboard(default)) - { - break; - } - - if (--num == 0) - { - ThrowWin32(); - } - - Thread.Sleep(100); - } - } - - const uint _cfUnicodeText = 13; - - void ThrowWin32() - { - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - - [DllImport("User32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool IsClipboardFormatAvailable(uint format); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern int GlobalSize(IntPtr handle); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern IntPtr GlobalLock(IntPtr hMem); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool GlobalUnlock(IntPtr hMem); - - [DllImport("user32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool OpenClipboard(IntPtr hWndNewOwner); - - [DllImport("user32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool CloseClipboard(); - - [DllImport("user32.dll", SetLastError = true)] - static extern IntPtr SetClipboardData(uint uFormat, IntPtr data); - - [DllImport("user32.dll")] - static extern bool EmptyClipboard(); - - [DllImport("user32.dll", SetLastError = true)] - static extern IntPtr GetClipboardData(uint uFormat); +class WindowsClipboard : ClipboardBase { + public WindowsClipboard () + { + IsSupported = IsClipboardFormatAvailable (_cfUnicodeText); + } + + public override bool IsSupported { get; } + + protected override string GetClipboardDataImpl () + { + try { + if (!OpenClipboard (IntPtr.Zero)) { + return string.Empty; + } + + IntPtr handle = GetClipboardData (_cfUnicodeText); + if (handle == IntPtr.Zero) { + return string.Empty; + } + + IntPtr pointer = IntPtr.Zero; + + try { + pointer = GlobalLock (handle); + if (pointer == IntPtr.Zero) { + return string.Empty; + } + + int size = GlobalSize (handle); + byte [] buff = new byte [size]; + + Marshal.Copy (pointer, buff, 0, size); + + return System.Text.Encoding.Unicode.GetString (buff).TrimEnd ('\0'); + } finally { + if (pointer != IntPtr.Zero) { + GlobalUnlock (handle); + } + } + } finally { + CloseClipboard (); + } + } + + protected override void SetClipboardDataImpl (string text) + { + OpenClipboard (); + + EmptyClipboard (); + IntPtr hGlobal = default; + try { + var bytes = (text.Length + 1) * 2; + hGlobal = Marshal.AllocHGlobal (bytes); + + if (hGlobal == default) { + ThrowWin32 (); + } + + var target = GlobalLock (hGlobal); + + if (target == default) { + ThrowWin32 (); + } + + try { + Marshal.Copy (text.ToCharArray (), 0, target, text.Length); + } finally { + GlobalUnlock (target); + } + + if (SetClipboardData (_cfUnicodeText, hGlobal) == default) { + ThrowWin32 (); + } + + hGlobal = default; + } finally { + if (hGlobal != default) { + Marshal.FreeHGlobal (hGlobal); + } + + CloseClipboard (); + } + } + + void OpenClipboard () + { + var num = 10; + while (true) { + if (OpenClipboard (default)) { + break; + } + + if (--num == 0) { + ThrowWin32 (); + } + + Thread.Sleep (100); + } + } + + const uint _cfUnicodeText = 13; + + void ThrowWin32 () + { + throw new Win32Exception (Marshal.GetLastWin32Error ()); + } + + [DllImport ("User32.dll", SetLastError = true)] + [return: MarshalAs (UnmanagedType.Bool)] + static extern bool IsClipboardFormatAvailable (uint format); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern int GlobalSize (IntPtr handle); + + [DllImport ("kernel32.dll", SetLastError = true)] + static extern IntPtr GlobalLock (IntPtr hMem); + + [DllImport ("kernel32.dll", SetLastError = true)] + [return: MarshalAs (UnmanagedType.Bool)] + static extern bool GlobalUnlock (IntPtr hMem); + + [DllImport ("user32.dll", SetLastError = true)] + [return: MarshalAs (UnmanagedType.Bool)] + static extern bool OpenClipboard (IntPtr hWndNewOwner); + + [DllImport ("user32.dll", SetLastError = true)] + [return: MarshalAs (UnmanagedType.Bool)] + static extern bool CloseClipboard (); + + [DllImport ("user32.dll", SetLastError = true)] + static extern IntPtr SetClipboardData (uint uFormat, IntPtr data); + + [DllImport ("user32.dll")] + static extern bool EmptyClipboard (); + + [DllImport ("user32.dll", SetLastError = true)] + static extern IntPtr GetClipboardData (uint uFormat); } \ No newline at end of file diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs index 371119c32d..40c26115f2 100644 --- a/Terminal.Gui/View/ViewSubViews.cs +++ b/Terminal.Gui/View/ViewSubViews.cs @@ -5,7 +5,7 @@ using System.Text; namespace Terminal.Gui { - public partial class View { + public partial class View { static readonly IList _empty = new List (0).AsReadOnly (); View _superView = null; @@ -420,10 +420,12 @@ public override bool OnEnter (View view) { var args = new FocusEventArgs (view); Enter?.Invoke (this, args); - if (args.Handled) + if (args.Handled) { return true; - if (base.OnEnter (view)) + } + if (base.OnEnter (view)) { return true; + } return false; } @@ -433,11 +435,14 @@ public override bool OnLeave (View view) { var args = new FocusEventArgs (view); Leave?.Invoke (this, args); - if (args.Handled) + if (args.Handled) { return true; - if (base.OnLeave (view)) + } + if (base.OnLeave (view)) { return true; + } + Driver.SetCursorVisibility (CursorVisibility.Invisible); return false; } diff --git a/UICatalog/Properties/launchSettings.json b/UICatalog/Properties/launchSettings.json index 4fe02b923b..00e9d9e71f 100644 --- a/UICatalog/Properties/launchSettings.json +++ b/UICatalog/Properties/launchSettings.json @@ -50,6 +50,9 @@ "Windows & FrameViews": { "commandName": "Project", "commandLineArgs": "\"Windows & FrameViews\"" + }, + "Profile 1": { + "commandName": "Executable" } } } \ No newline at end of file diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 20e46db531..30d8e5bf21 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Data; using System.Globalization; using System.Linq; using System.Net.Http; @@ -407,12 +408,19 @@ public override void OnDrawContentComplete (Rect contentArea) Driver.SetAttribute (GetFocusColor ()); } var scalar = val + col; + Rune rune = (Rune)'?'; if (Rune.IsValid (scalar)) { - Driver.AddRune (new Rune (scalar)); - } else { - Driver.AddRune ((Rune)'?'); + rune = new Rune (scalar); } + var width = rune.GetColumns (); + //if (width == 0) { + // if (rune.IsCombiningMark ()) { + // Driver.AddRune (Rune.ReplacementChar); + // } + //} + Driver.AddRune (rune); + if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { Driver.SetAttribute (GetNormalColor ()); } @@ -572,7 +580,6 @@ void ShowDetails () Application.GrabMouse (this); } - public override bool OnEnter (View view) { if (IsInitialized) { @@ -580,6 +587,12 @@ public override bool OnEnter (View view) } return base.OnEnter (view); } + + public override bool OnLeave (View view) + { + Driver.SetCursorVisibility (CursorVisibility.Invisible); + return base.OnLeave (view); + } } public class UcdApiClient { From 5864cf1a64e824c1f2c6aa8fd9fa60a11041dab2 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Tue, 8 Aug 2023 07:08:55 -0600 Subject: [PATCH 90/99] Merged in changes from PR #2786, Fixes #2784 --- .../CursesDriver/CursesDriver.cs | 54 ++++++++++++-- .../CursesDriver/UnixMainLoop.cs | 4 +- .../ConsoleDrivers/CursesDriver/binding.cs | 5 +- UnitTests/ConsoleDrivers/KeyTests.cs | 70 ++++++++++--------- 4 files changed, 91 insertions(+), 42 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 6a77aff560..03203418a8 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -189,6 +189,10 @@ public override void End () StopReportingMouseMoves (); SetCursorVisibility (CursorVisibility.Default); + // throws away any typeahead that has been typed by + // the user and has not yet been read by the program. + Curses.flushinp (); + Curses.endwin (); } @@ -208,7 +212,7 @@ public override void UpdateScreen () if (Contents [row, col].Runes [0].IsBmp) { //_outputBuffer [position].Char = (char)Contents [row, col].Runes [0].Value; - Curses.mvaddwstr (row, col, Contents [row, col].Runes [0].ToString()); + Curses.mvaddwstr (row, col, Contents [row, col].Runes [0].ToString ()); if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { col++; //Curses.mvaddwstr (row, col, ' '); @@ -376,8 +380,12 @@ void ProcessInput () Key k = Key.Null; if (code == Curses.KEY_CODE_YES) { - if (wch == Curses.KeyResize) { + while (code == Curses.KEY_CODE_YES && wch == Curses.KeyResize) { ProcessWinChange (); + code = Curses.get_wch (out wch); + } + if (wch == 0) { + return; } if (wch == Curses.KeyMouse) { int wch2 = wch; @@ -536,8 +544,45 @@ void HandleEscSeqResponse (ref int code, ref Key k, ref int wch2, ref KeyEvent k } } + MouseFlags _lastMouseFlags; + void ProcessMouseEvent (MouseFlags mouseFlag, Point pos) { + bool WasButtonReleased (MouseFlags flag) + { + return flag.HasFlag (MouseFlags.Button1Released) || + flag.HasFlag (MouseFlags.Button2Released) || + flag.HasFlag (MouseFlags.Button3Released) || + flag.HasFlag (MouseFlags.Button4Released); + } + + bool IsButtonNotPressed (MouseFlags flag) + { + return !flag.HasFlag (MouseFlags.Button1Pressed) && + !flag.HasFlag (MouseFlags.Button2Pressed) && + !flag.HasFlag (MouseFlags.Button3Pressed) && + !flag.HasFlag (MouseFlags.Button4Pressed); + } + + bool IsButtonClickedOrDoubleClicked (MouseFlags flag) + { + return flag.HasFlag (MouseFlags.Button1Clicked) || + flag.HasFlag (MouseFlags.Button2Clicked) || + flag.HasFlag (MouseFlags.Button3Clicked) || + flag.HasFlag (MouseFlags.Button4Clicked) || + flag.HasFlag (MouseFlags.Button1DoubleClicked) || + flag.HasFlag (MouseFlags.Button2DoubleClicked) || + flag.HasFlag (MouseFlags.Button3DoubleClicked) || + flag.HasFlag (MouseFlags.Button4DoubleClicked); + } + + if ((WasButtonReleased (mouseFlag) && IsButtonNotPressed (_lastMouseFlags)) || + (IsButtonClickedOrDoubleClicked (mouseFlag) && _lastMouseFlags == 0)) { + return; + } + + _lastMouseFlags = mouseFlag; + var me = new MouseEvent () { Flags = mouseFlag, X = pos.X, @@ -546,6 +591,7 @@ void ProcessMouseEvent (MouseFlags mouseFlag, Point pos) _mouseHandler (me); } + void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) { ProcessMouseEvent (mouseFlag, pos); @@ -572,9 +618,7 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle return true; }); - mLoop.WinChanged += () => { - ProcessWinChange (); - }; + mLoop.WinChanged += ProcessInput; } public override void Init (Action terminalResized) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs index 031f5edb1a..48d8fbcb22 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs @@ -97,8 +97,8 @@ void IMainLoopDriver.Setup (MainLoop mainLoop) { this.mainLoop = mainLoop; pipe (wakeupPipes); - AddWatch (wakeupPipes [0], Condition.PollIn, ml => { - read (wakeupPipes [0], ignore, readHandle); + AddWatch (wakeupPipes [1], Condition.PollIn, ml => { + read (wakeupPipes [1], ignore, readHandle); return true; }); } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs index 7f87c9ef5c..391bfaa716 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs @@ -141,7 +141,10 @@ public static bool CheckWinChange () console_sharp_get_dims (out l, out c); - if (l == 1 || l != lines || c != cols) { + if (l < 1) { + l = 1; + } + if (l != lines || c != cols) { lines = l; cols = c; return true; diff --git a/UnitTests/ConsoleDrivers/KeyTests.cs b/UnitTests/ConsoleDrivers/KeyTests.cs index 4f40707ae3..56e6d3c1f3 100644 --- a/UnitTests/ConsoleDrivers/KeyTests.cs +++ b/UnitTests/ConsoleDrivers/KeyTests.cs @@ -184,41 +184,43 @@ public void Key_ToString () [ClassData (typeof (PacketTest))] public void TestVKPacket (uint unicodeCharacter, bool shift, bool alt, bool control, uint initialVirtualKey, uint initialScanCode, Key expectedRemapping, uint expectedVirtualKey, uint expectedScanCode) { - var modifiers = new ConsoleModifiers (); - if (shift) modifiers |= ConsoleModifiers.Shift; - if (alt) modifiers |= ConsoleModifiers.Alt; - if (control) modifiers |= ConsoleModifiers.Control; - var mappedConsoleKey = ConsoleKeyMapping.GetConsoleKeyFromKey (unicodeCharacter, modifiers, out uint scanCode, out uint outputChar); - - if ((scanCode > 0 || mappedConsoleKey == 0) && mappedConsoleKey == initialVirtualKey) Assert.Equal (mappedConsoleKey, initialVirtualKey); - else Assert.Equal (mappedConsoleKey, outputChar < 0xff ? outputChar & 0xff | 0xff << 8 : outputChar); - Assert.Equal (scanCode, initialScanCode); - - var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (mappedConsoleKey, modifiers, out uint consoleKey, out scanCode); - - //if (scanCode > 0 && consoleKey == keyChar && consoleKey > 48 && consoleKey > 57 && consoleKey < 65 && consoleKey > 91) { - if (scanCode > 0 && keyChar == 0 && consoleKey == mappedConsoleKey) Assert.Equal (0, (double)keyChar); - else Assert.Equal (keyChar, unicodeCharacter); - Assert.Equal (consoleKey, expectedVirtualKey); - Assert.Equal (scanCode, expectedScanCode); - - var top = Application.Top; - - top.KeyPress += (s, e) => { - var after = ShortcutHelper.GetModifiersKey (e.KeyEvent); - Assert.Equal (expectedRemapping, after); - e.Handled = true; - Application.RequestStop (); - }; - - var iterations = -1; - - Application.Iteration += () => { - iterations++; - if (iterations == 0) Application.Driver.SendKeys ((char)mappedConsoleKey, ConsoleKey.Packet, shift, alt, control); - }; - lock (packetLock) { + Application._forceFakeConsole = true; + Application.Init (); + + var modifiers = new ConsoleModifiers (); + if (shift) modifiers |= ConsoleModifiers.Shift; + if (alt) modifiers |= ConsoleModifiers.Alt; + if (control) modifiers |= ConsoleModifiers.Control; + var mappedConsoleKey = ConsoleKeyMapping.GetConsoleKeyFromKey (unicodeCharacter, modifiers, out uint scanCode, out uint outputChar); + + if ((scanCode > 0 || mappedConsoleKey == 0) && mappedConsoleKey == initialVirtualKey) Assert.Equal (mappedConsoleKey, initialVirtualKey); + else Assert.Equal (mappedConsoleKey, outputChar < 0xff ? outputChar & 0xff | 0xff << 8 : outputChar); + Assert.Equal (scanCode, initialScanCode); + + var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (mappedConsoleKey, modifiers, out uint consoleKey, out scanCode); + + //if (scanCode > 0 && consoleKey == keyChar && consoleKey > 48 && consoleKey > 57 && consoleKey < 65 && consoleKey > 91) { + if (scanCode > 0 && keyChar == 0 && consoleKey == mappedConsoleKey) Assert.Equal (0, (double)keyChar); + else Assert.Equal (keyChar, unicodeCharacter); + Assert.Equal (consoleKey, expectedVirtualKey); + Assert.Equal (scanCode, expectedScanCode); + + var top = Application.Top; + + top.KeyPress += (s, e) => { + var after = ShortcutHelper.GetModifiersKey (e.KeyEvent); + Assert.Equal (expectedRemapping, after); + e.Handled = true; + Application.RequestStop (); + }; + + var iterations = -1; + + Application.Iteration += () => { + iterations++; + if (iterations == 0) Application.Driver.SendKeys ((char)mappedConsoleKey, ConsoleKey.Packet, shift, alt, control); + }; Application.Run (); Application.Shutdown (); } From f7bced9db2dac87415c49c6ec9c28068f1d7cabf Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 9 Aug 2023 05:50:16 -0600 Subject: [PATCH 91/99] revamp charmap rendering --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 6 +- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 136 ++++++++----- Terminal.Gui/Terminal.Gui.csproj | 7 +- Terminal.Gui/Text/RuneExtensions.cs | 34 ++-- Terminal.Gui/View/ViewSubViews.cs | 2 +- UICatalog/Scenarios/CharacterMap.cs | 190 ++++++++++-------- UnitTests/ConsoleDrivers/AddRuneTests.cs | 140 +++++++++++++ UnitTests/Text/RuneTests.cs | 89 ++++++-- UnitTests/Text/StringTests.cs | 2 + 9 files changed, 435 insertions(+), 171 deletions(-) create mode 100644 UnitTests/ConsoleDrivers/AddRuneTests.cs diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 8a1178225d..3f2d1da83c 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -135,7 +135,7 @@ public virtual bool IsRuneSupported (Rune rune) /// /// /// When the method returns, will be incremented by the number of columns required, - /// unless the new column value is outside of the or screen dimensions defined by . + /// even if the new column value is outside of the or screen dimensions defined by . /// /// /// If requires more than one column, and plus the number of columns needed @@ -150,7 +150,7 @@ public void AddRune (Rune rune) if (validLocation) { rune = rune.MakePrintable (); runeWidth = rune.GetColumns (); - if (runeWidth == 0 && rune.IsCombiningMark() && Col > 0) { + if (runeWidth == 0 && rune.IsCombiningMark () && Col > 0) { // This is a combining character, and we are not at the beginning of the line. // TODO: Remove hard-coded [0] once combining pairs is supported @@ -223,7 +223,7 @@ public void AddRune (Rune rune) } if (runeWidth > 1) { - Debug.Assert(runeWidth <= 2); + Debug.Assert (runeWidth <= 2); if (validLocation && Col < Clip.Right) { // This is a double-width character, and we are not at the end of the line. // Col now points to the second column of the character. Ensure it doesn't diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index d6a965b9cb..6e06903bc0 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -14,6 +14,7 @@ using Console = Terminal.Gui.FakeConsole; using Unix.Terminal; using static Terminal.Gui.WindowsConsole; +using System.Drawing; namespace Terminal.Gui; /// @@ -87,53 +88,94 @@ public override void Init (Action terminalResized) public override void UpdateScreen () { - //int top = Top; - //int left = Left; - //int rows = Math.Min (FakeConsole.WindowHeight + top, Rows); - //int cols = Cols; - - //var savedRow = FakeConsole.CursorTop; - //var savedCol = FakeConsole.CursorLeft; - //var savedCursorVisible = FakeConsole.CursorVisible; - //for (int row = top; row < rows; row++) { - // if (!_dirtyLine [row]) { - // continue; - // } - // _dirtyLine [row] = false; - // for (int col = left; col < cols; col++) { - // FakeConsole.CursorTop = row; - // FakeConsole.CursorLeft = col; - // for (; col < cols; col++) { - // if (Contents [row, col, 2] == 0) { - // FakeConsole.CursorLeft++; - // continue; - // } - - // var color = Contents [row, col, 1]; - // // NOTE: In real drivers setting the color can be a performance hit, so we only do it when needed. - // // in fakedriver we don't care about perf. - // SetColor (color); - - // var rune = (Rune)Contents [row, col, 0]; - // if (rune.Utf16SequenceLength == 1) { - // FakeConsole.Write (rune); - // } else { - // // TODO: Not sure we need to do this. I think we can just write the rune. - - // FakeConsole.Write (rune.ToString ()); - // } - // //if (Rune.DecodeSurrogatePair (rune, out char [] spair)) { - // // FakeConsole.Write (spair); - // //} else { - // // FakeConsole.Write ((char)rune); - // //} - // Contents [row, col, 2] = 0; - // } - // } - //} - //FakeConsole.CursorTop = savedRow; - //FakeConsole.CursorLeft = savedCol; - //FakeConsole.CursorVisible = savedCursorVisible; + var savedRow = FakeConsole.CursorTop; + var savedCol = FakeConsole.CursorLeft; + var savedCursorVisible = FakeConsole.CursorVisible; + + var top = 0; + var left = 0; + var rows = Rows; + var cols = Cols; + System.Text.StringBuilder output = new System.Text.StringBuilder (); + Attribute redrawAttr = new Attribute (); + var lastCol = -1; + + for (var row = top; row < rows; row++) { + if (!_dirtyLines [row]) { + continue; + } + + FakeConsole.CursorTop = row; + FakeConsole.CursorLeft = 0; + + _dirtyLines [row] = false; + output.Clear (); + for (var col = left; col < cols; col++) { + lastCol = -1; + var outputWidth = 0; + for (; col < cols; col++) { + if (!Contents [row, col].IsDirty) { + if (output.Length > 0) { + WriteToConsole (output, ref lastCol, row, ref outputWidth); + } else if (lastCol == -1) { + lastCol = col; + } + if (lastCol + 1 < cols) + lastCol++; + continue; + } + + if (lastCol == -1) { + lastCol = col; + } + + Attribute attr = Contents [row, col].Attribute.Value; + // Performance: Only send the escape sequence if the attribute has changed. + if (attr != redrawAttr) { + redrawAttr = attr; + FakeConsole.ForegroundColor = (ConsoleColor)attr.Foreground; + FakeConsole.BackgroundColor = (ConsoleColor)attr.Background; + } + outputWidth++; + var rune = (Rune)Contents [row, col].Runes [0]; + output.Append (rune.ToString ()); + if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { + WriteToConsole (output, ref lastCol, row, ref outputWidth); + FakeConsole.CursorLeft--; + } + Contents [row, col].IsDirty = false; + } + } + if (output.Length > 0) { + FakeConsole.CursorTop = row; + FakeConsole.CursorLeft = lastCol; + + foreach (var c in output.ToString ()) { + FakeConsole.Write (c); + } + } + } + FakeConsole.CursorTop = 0; + FakeConsole.CursorLeft = 0; + + //SetCursorVisibility (savedVisibitity); + + void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth) + { + FakeConsole.CursorTop = row; + FakeConsole.CursorLeft = lastCol; + foreach (var c in output.ToString ()) { + FakeConsole.Write (c); + } + + output.Clear (); + lastCol += outputWidth; + outputWidth = 0; + } + + FakeConsole.CursorTop = savedRow; + FakeConsole.CursorLeft = savedCol; + FakeConsole.CursorVisible = savedCursorVisible; } public override void Refresh () diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 8e6a488821..30592f23d0 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -27,9 +27,10 @@ - - - + + + + diff --git a/Terminal.Gui/Text/RuneExtensions.cs b/Terminal.Gui/Text/RuneExtensions.cs index f273678749..0db68cb18d 100644 --- a/Terminal.Gui/Text/RuneExtensions.cs +++ b/Terminal.Gui/Text/RuneExtensions.cs @@ -1,5 +1,6 @@ using System.Globalization; using System.Text; +using Wcwidth; namespace Terminal.Gui; @@ -26,21 +27,24 @@ public static class RuneExtensions { /// public static int GetColumns (this Rune rune) { - // TODO: I believe there is a way to do this without using our own tables, using Rune. - var codePoint = rune.Value; - switch (codePoint) { - case < 0x20: - case >= 0x7f and < 0xa0: - return -1; - case < 0x7f: - return 1; - } - /* binary search in table of non-spacing characters */ - if (BiSearch (codePoint, _combining, _combining.GetLength (0) - 1) != 0) { - return 0; - } - /* if we arrive here, ucs is not a combining or C0/C1 control character */ - return 1 + (BiSearch (codePoint, _combiningWideChars, _combiningWideChars.GetLength (0) - 1) != 0 ? 1 : 0); + return UnicodeCalculator.GetWidth (rune); + + //// TODO: I believe there is a way to do this without using our own tables, using Rune. + //// See https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + //var codePoint = rune.Value; + //switch (codePoint) { + //case < 0x20: + //case >= 0x7f and < 0xa0: + // return -1; + //case < 0x7f: + // return 1; + //} + ///* binary search in table of non-spacing characters */ + //if (BiSearch (codePoint, _combining, _combining.GetLength (0) - 1) != 0) { + // return 0; + //} + ///* if we arrive here, ucs is not a combining or C0/C1 control character */ + //return 1 + (BiSearch (codePoint, _combiningWideChars, _combiningWideChars.GetLength (0) - 1) != 0 ? 1 : 0); } /// diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs index 40c26115f2..1657e09ed0 100644 --- a/Terminal.Gui/View/ViewSubViews.cs +++ b/Terminal.Gui/View/ViewSubViews.cs @@ -442,7 +442,7 @@ public override bool OnLeave (View view) return true; } - Driver.SetCursorVisibility (CursorVisibility.Invisible); + Driver?.SetCursorVisibility (CursorVisibility.Invisible); return false; } diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 30d8e5bf21..11066ac770 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -194,6 +194,7 @@ private void JumpEdit_TextChanged (object sender, TextChangedEventArgs e) class CharMap : ScrollView { + public bool ShowColumnWidths { get; set;} /// /// Specifies the starting offset for the character map. The default is 0x2500 /// which is the Box Drawing characters. @@ -218,15 +219,16 @@ public int SelectedCodePoint { get => _selected; set { _selected = value; - var col = Cursor.X; - var row = Cursor.Y; - var height = (Bounds.Height / ROW_HEIGHT) - (ShowHorizontalScrollIndicator ? 2 : 1); + var row = (SelectedCodePoint / 16 * _rowHeight); + var col = (SelectedCodePoint % 16 * COLUMN_WIDTH); + + var height = (Bounds.Height) - (ShowHorizontalScrollIndicator ? 2 : 1); if (row + ContentOffset.Y < 0) { // Moving up. ContentOffset = new Point (ContentOffset.X, row); } else if (row + ContentOffset.Y >= height) { // Moving down. - ContentOffset = new Point (ContentOffset.X, Math.Min (row, row - height + ROW_HEIGHT)); + ContentOffset = new Point (ContentOffset.X, Math.Min (row, row - height + _rowHeight)); } var width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth); if (col + ContentOffset.X < 0) { @@ -241,10 +243,13 @@ public int SelectedCodePoint { } } + /// + /// Gets the coordinates of the Cursor based on the SelectedCodePoint in screen coordinates + /// public Point Cursor { get { - var row = SelectedCodePoint / 16; - var col = (SelectedCodePoint - row * 16) * COLUMN_WIDTH; + var row = (SelectedCodePoint / 16 * _rowHeight) + ContentOffset.Y + 1; + var col = (SelectedCodePoint % 16 * COLUMN_WIDTH) + ContentOffset.X + RowLabelWidth + 1; // + 1 for padding return new Point (col, row); } set => throw new NotImplementedException (); @@ -252,13 +257,13 @@ public Point Cursor { public override void PositionCursor () { - if (HasFocus && Cursor.X + ContentOffset.X + RowLabelWidth + 1 >= RowLabelWidth && - Cursor.X + ContentOffset.X + RowLabelWidth + 1 < Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0) && - Cursor.Y + ContentOffset.Y + 1 > 0 && - Cursor.Y + ContentOffset.Y + 1 < Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0)) { - + if (HasFocus && + Cursor.X >= RowLabelWidth && + Cursor.X < Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0) && + Cursor.Y > 0 && + Cursor.Y < Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0)) { Driver.SetCursorVisibility (CursorVisibility.Default); - Move (Cursor.X + ContentOffset.X + RowLabelWidth + 1, Cursor.Y + ContentOffset.Y + 1); + Move (Cursor.X, Cursor.Y); } else { Driver.SetCursorVisibility (CursorVisibility.Invisible); } @@ -268,19 +273,19 @@ public override void PositionCursor () int _start = 0; int _selected = 0; - public const int COLUMN_WIDTH = 3; - public const int ROW_HEIGHT = 1; + const int COLUMN_WIDTH = 3; + int _rowHeight = 2; public static int MaxCodePoint => 0x10FFFF; - public static int RowLabelWidth => $"U+{MaxCodePoint:x5}".Length + 1; - public static int RowWidth => RowLabelWidth + (COLUMN_WIDTH * 16); + static int RowLabelWidth => $"U+{MaxCodePoint:x5}".Length + 1; + static int RowWidth => RowLabelWidth + (COLUMN_WIDTH * 16); public CharMap () { ColorScheme = Colors.Dialog; CanFocus = true; - ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + (ShowHorizontalScrollIndicator ? 2 : 1))); + ContentSize = new Size (CharMap.RowWidth, (int)((MaxCodePoint / 16 + (ShowHorizontalScrollIndicator ? 2 : 1)) * _rowHeight)); AddCommand (Command.ScrollUp, () => { if (SelectedCodePoint >= 16) { @@ -307,12 +312,12 @@ public CharMap () return true; }); AddCommand (Command.PageUp, () => { - var page = (Bounds.Height / ROW_HEIGHT - 1) * 16; + var page = (Bounds.Height / _rowHeight - 1) * 16; SelectedCodePoint -= Math.Min (page, SelectedCodePoint); return true; }); AddCommand (Command.PageDown, () => { - var page = (Bounds.Height / ROW_HEIGHT - 1) * 16; + var page = (Bounds.Height / _rowHeight - 1) * 16; SelectedCodePoint += Math.Min (page, MaxCodePoint - SelectedCodePoint); return true; }); @@ -338,20 +343,25 @@ public CharMap () public override void OnDrawContent (Rect contentArea) { - if (ShowHorizontalScrollIndicator && ContentSize.Height < (int)(MaxCodePoint / 16 + 2)) { - ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 2)); - var width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth); - if (Cursor.X + ContentOffset.X >= width) { - // Snap to the selected glyph. - ContentOffset = new Point (Math.Min (Cursor.X, Cursor.X - width + COLUMN_WIDTH), ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); - } else { - ContentOffset = new Point (ContentOffset.X - Cursor.X, ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); - } - } else if (!ShowHorizontalScrollIndicator && ContentSize.Height > (int)(MaxCodePoint / 16 + 1)) { - ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 1)); - // Snap 1st column into view if it's been scrolled horizontally - ContentOffset = new Point (0, ContentOffset.Y < -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); - } + //if (ShowHorizontalScrollIndicator && ContentSize.Height < (int)(MaxCodePoint / 16 + 2)) { + // //ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 2)); + // //ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16) * _rowHeight + 2); + // var width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth); + // if (Cursor.X + ContentOffset.X >= width) { + // // Snap to the selected glyph. + // ContentOffset = new Point ( + // Math.Min (Cursor.X, Cursor.X - width + COLUMN_WIDTH), + // ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); + // } else { + // ContentOffset = new Point ( + // ContentOffset.X - Cursor.X, + // ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); + // } + //} else if (!ShowHorizontalScrollIndicator && ContentSize.Height > (int)(MaxCodePoint / 16 + 1)) { + // //ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 1)); + // // Snap 1st column into view if it's been scrolled horizontally + // ContentOffset = new Point (0, ContentOffset.Y < -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y); + //} base.OnDrawContent (contentArea); } @@ -372,8 +382,8 @@ public override void OnDrawContentComplete (Rect contentArea) Driver.Clip = new Rect (Driver.Clip.Location, new Size (Driver.Clip.Width - 1, Driver.Clip.Height)); } - var cursorCol = Cursor.X; - var cursorRow = Cursor.Y; + var cursorCol = Cursor.X - ContentOffset.X - RowLabelWidth - 1; + var cursorRow = Cursor.Y - ContentOffset.Y - 1; Driver.SetAttribute (GetHotNormalColor ()); Move (0, 0); @@ -384,7 +394,7 @@ public override void OnDrawContentComplete (Rect contentArea) Move (x, 0); Driver.SetAttribute (GetHotNormalColor ()); Driver.AddStr (" "); - Driver.SetAttribute (HasFocus && (cursorCol + RowLabelWidth == x) ? ColorScheme.HotFocus : GetHotNormalColor ()); + Driver.SetAttribute (HasFocus && (cursorCol + ContentOffset.X + RowLabelWidth == x) ? ColorScheme.HotFocus : GetHotNormalColor ()); Driver.AddStr ($"{hexDigit:x}"); Driver.SetAttribute (GetHotNormalColor ()); Driver.AddStr (" "); @@ -392,43 +402,55 @@ public override void OnDrawContentComplete (Rect contentArea) } var firstColumnX = viewport.X + RowLabelWidth; - for (int row = -ContentOffset.Y, y = 1; row <= (-ContentOffset.Y) + (Bounds.Height / ROW_HEIGHT); row++, y += ROW_HEIGHT) { - var val = (row) * 16; - if (val > MaxCodePoint) { - continue; - } - Move (firstColumnX + COLUMN_WIDTH, y); - Driver.SetAttribute (GetNormalColor ()); - for (int col = 0; col < 16; col++) { - - var x = firstColumnX + COLUMN_WIDTH * col + 1; - - Move (x, y); - if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { - Driver.SetAttribute (GetFocusColor ()); + for (int y = 1; y < Bounds.Height; y++) { + // What row is this? + var row = (y - ContentOffset.Y) / _rowHeight; + //Move (0, y); + //Driver.SetAttribute (HasFocus && (cursorRow + ContentOffset.Y + 1 == y) ? ColorScheme.HotFocus : ColorScheme.HotNormal); + //Driver.AddStr ($"y={y},r={row}"); + + // are we at first row of the row? + if ((y - ContentOffset.Y) % _rowHeight > 0) { + var val = (row) * 16; + if (val > MaxCodePoint) { + continue; } - var scalar = val + col; - Rune rune = (Rune)'?'; - if (Rune.IsValid (scalar)) { - rune = new Rune (scalar); - } - var width = rune.GetColumns (); - //if (width == 0) { - // if (rune.IsCombiningMark ()) { - // Driver.AddRune (Rune.ReplacementChar); - // } - //} - - Driver.AddRune (rune); - - if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { - Driver.SetAttribute (GetNormalColor ()); + Move (firstColumnX + COLUMN_WIDTH, y); + Driver.SetAttribute (GetNormalColor ()); + for (int col = 0; col < 16; col++) { + + var x = firstColumnX + COLUMN_WIDTH * col + 1; + + Move (x, y); + if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { + Driver.SetAttribute (GetFocusColor ()); + } + var scalar = val + col; + Rune rune = (Rune)'?'; + if (Rune.IsValid (scalar)) { + rune = new Rune (scalar); + } + var width = rune.GetColumns (); + //if (width == 0) { + // if (rune.IsCombiningMark ()) { + // Driver.AddRune (Rune.ReplacementChar); + // } + //} + + Driver.AddRune (rune); + + if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { + Driver.SetAttribute (GetNormalColor ()); + } } + Move (0, y); + Driver.SetAttribute (HasFocus && (cursorRow + ContentOffset.Y + 1 == y) ? ColorScheme.HotFocus : ColorScheme.HotNormal); + var rowLabel = $"U+{val / 16:x5}_ "; + Driver.AddStr (rowLabel); + } else { + //Move (firstColumnX + 1, y); + //Driver.AddStr ("+++++++++++++++++++"); } - Move (0, y); - Driver.SetAttribute (HasFocus && (cursorRow + ContentOffset.Y + 1 == y) ? ColorScheme.HotFocus : ColorScheme.HotNormal); - var rowLabel = $"U+{val / 16:x5}_ "; - Driver.AddStr (rowLabel); } Driver.Clip = oldClip; } @@ -442,21 +464,29 @@ void Handle_MouseClick (object sender, MouseEventEventArgs args) return; } - if (me.X < RowLabelWidth) { - return; - } + //if (me.X < RowLabelWidth) { + // return; + //} - if (me.Y < 1) { - return; - } + //if (me.Y < 1) { + // return; + //} - var row = me.Y - 1; + var row = (me.Y - 1 - ContentOffset.Y) / _rowHeight; // -1 for header var col = (me.X - RowLabelWidth - ContentOffset.X) / COLUMN_WIDTH; - if (row < 0 || row > Bounds.Height || col < 0 || col > 15) { - return; + //if (row < 0 || row > Bounds.Height || col > 15) { + // return; + //} + + if (row < 0) { + row = (Cursor.Y) / _rowHeight; + } + + if (col < 0) { + col = (Cursor.X) / COLUMN_WIDTH; } - var val = (row - ContentOffset.Y) * 16 + col; + var val = (row ) * 16 + col; if (val > MaxCodePoint) { return; } diff --git a/UnitTests/ConsoleDrivers/AddRuneTests.cs b/UnitTests/ConsoleDrivers/AddRuneTests.cs new file mode 100644 index 0000000000..b4cc5e5ad3 --- /dev/null +++ b/UnitTests/ConsoleDrivers/AddRuneTests.cs @@ -0,0 +1,140 @@ +using System.Buffers; +using System.Text; +using Xunit; +using Xunit.Abstractions; +using static Terminal.Gui.SpinnerStyle; + +// Alias Console to MockConsole so we don't accidentally use Console + +namespace Terminal.Gui.DriverTests; +public class AddRuneTests { + readonly ITestOutputHelper _output; + + public AddRuneTests (ITestOutputHelper output) + { + this._output = output; + } + + [Fact] + public void AddRune () + { + + var driver = new FakeDriver (); + Application.Init (driver); + driver.Init (() => { }); + + driver.AddRune (new Rune ('a')); + Assert.Equal ((Rune)'a', driver.Contents[0,0].Runes[0]); + + driver.End (); + Application.Shutdown (); + } + + [Fact] + public void AddRune_InvalidLocation_DoesNothing () + { + var driver = new FakeDriver (); + Application.Init (driver); + driver.Init (() => { }); + + driver.Move (driver.Cols, driver.Rows); + driver.AddRune ('a'); + + for (var col = 0; col < driver.Cols; col++) { + for (var row = 0; row < driver.Rows; row++) { + Assert.Equal ((Rune)' ', driver.Contents [row, col].Runes [0]); + } + } + + driver.End (); + Application.Shutdown (); + } + + [Fact] + public void AddRune_MovesToNextColumn () + { + var driver = new FakeDriver (); + Application.Init (driver); + driver.Init (() => { }); + + driver.AddRune ('a'); + Assert.Equal ((Rune)'a', driver.Contents [0, 0].Runes [0]); + Assert.Equal (0, driver.Row); + Assert.Equal (1, driver.Col); + + driver.AddRune ('b'); + Assert.Equal ((Rune)'b', driver.Contents [0, 1].Runes [0]); + Assert.Equal (0, driver.Row); + Assert.Equal (2, driver.Col); + + // Move to the last column of the first row + var lastCol = driver.Cols - 1; + driver.Move (lastCol, 0); + Assert.Equal (0, driver.Row); + Assert.Equal (lastCol, driver.Col); + + // Add a rune to the last column of the first row; should increment the row or col even though it's now invalid + driver.AddRune ('c'); + Assert.Equal ((Rune)'c', driver.Contents [0, lastCol].Runes [0]); + Assert.Equal (lastCol + 1, driver.Col); + + // Add a rune; should succeed but do nothing as it's outside of Contents + driver.AddRune ('d'); + Assert.Equal (lastCol + 2, driver.Col); + for (var col = 0; col < driver.Cols; col++) { + for (var row = 0; row < driver.Rows; row++) { + Assert.NotEqual ((Rune)'d', driver.Contents [row, col].Runes [0]); + } + } + + driver.End (); + Application.Shutdown (); + } + + [Fact] + public void AddRune_MovesToNextColumn_Wide () + { + var driver = new FakeDriver (); + Application.Init (driver); + driver.Init (() => { }); + + // 🍕 Slice of Pizza "\U0001F355" + var operationStatus = Rune.DecodeFromUtf16 ("\U0001F355", out Rune rune, out int charsConsumed); + Assert.Equal (OperationStatus.Done, operationStatus); + Assert.Equal (charsConsumed, rune.Utf16SequenceLength); + Assert.Equal (2, rune.GetColumns ()); + + driver.AddRune (rune); + Assert.Equal (rune, driver.Contents [0, 0].Runes [0]); + Assert.Equal (0, driver.Row); + Assert.Equal (2, driver.Col); + + //driver.AddRune ('b'); + //Assert.Equal ((Rune)'b', driver.Contents [0, 1].Runes [0]); + //Assert.Equal (0, driver.Row); + //Assert.Equal (2, driver.Col); + + //// Move to the last column of the first row + //var lastCol = driver.Cols - 1; + //driver.Move (lastCol, 0); + //Assert.Equal (0, driver.Row); + //Assert.Equal (lastCol, driver.Col); + + //// Add a rune to the last column of the first row; should increment the row or col even though it's now invalid + //driver.AddRune ('c'); + //Assert.Equal ((Rune)'c', driver.Contents [0, lastCol].Runes [0]); + //Assert.Equal (lastCol + 1, driver.Col); + + //// Add a rune; should succeed but do nothing as it's outside of Contents + //driver.AddRune ('d'); + //Assert.Equal (lastCol + 2, driver.Col); + //for (var col = 0; col < driver.Cols; col++) { + // for (var row = 0; row < driver.Rows; row++) { + // Assert.NotEqual ((Rune)'d', driver.Contents [row, col].Runes [0]); + // } + //} + + driver.End (); + Application.Shutdown (); + } +} diff --git a/UnitTests/Text/RuneTests.cs b/UnitTests/Text/RuneTests.cs index 8d783fd8ef..71ae2c9e5f 100644 --- a/UnitTests/Text/RuneTests.cs +++ b/UnitTests/Text/RuneTests.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using Xunit; +using static Terminal.Gui.SpinnerStyle; namespace Terminal.Gui.TextTests; @@ -19,7 +20,10 @@ public class RuneTests { [InlineData (0x0328, true)] // Combining ogonek (a small hook or comma shape) U+0328 [InlineData (0x00E9, false)] // Latin Small Letter E with Acute, Unicode U+00E9 é [InlineData (0x0061, false)] // Latin Small Letter A is U+0061. - public void TestIsCombiningMark (int codepoint, bool expected) + [InlineData ('\uFE20', true)] // Combining Ligature Left Half - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml + [InlineData ('\uFE21', true)] // Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml + + public void IsCombiningMark (int codepoint, bool expected) { var rune = new Rune (codepoint); Assert.Equal (expected, rune.IsCombiningMark ()); @@ -53,6 +57,8 @@ public void MakePrintable_Does_Not_Convert_Ansi_Chars_To_Unicode (int code) [InlineData (0x0301)] // Combining acute accent (é) [InlineData (0x0302)] // Combining Circumflex Accent [InlineData (0x0061)] // Combining ogonek (a small hook or comma shape) + [InlineData ('\uFE20')] // Combining Ligature Left Half - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml + [InlineData ('\uFE21')] // Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml public void MakePrintable_Combining_Character_Is_Not_Printable (int code) { var rune = new Rune (code); @@ -61,30 +67,65 @@ public void MakePrintable_Combining_Character_Is_Not_Printable (int code) } [Theory] + [InlineData (0, "\0", 0, 1, 1)] + [InlineData ('\u1dc0', "᷀", 0, 1, 3)] // ◌᷀ Combining Dotted Grave Accent + [InlineData ('\u20D0', "⃐", 0, 1, 3)] // ◌⃐ Combining Left Harpoon Above + + [InlineData (1, "\u0001", -1, 1, 1)] + [InlineData (2, "\u0002", -1, 1, 1)] + [InlineData (31, "\u001f", -1, 1, 1)] // non printable character - Information Separator One + [InlineData (127, "\u007f", -1, 1, 1)] // non printable character - Delete + + [InlineData (32, " ", 1, 1, 1)] // space [InlineData ('a', "a", 1, 1, 1)] [InlineData ('b', "b", 1, 1, 1)] [InlineData (123, "{", 1, 1, 1)] // { Left Curly Bracket - [InlineData ('\u1150', "ᅐ", 2, 1, 3)] // ᅐ Hangul Choseong Ceongchieumcieuc - [InlineData ('\u1161', "ᅡ", 0, 1, 3)] // ᅡ Hangul Jungseong A - Unicode Hangul Jamo for join with column width equal to 0 alone. - [InlineData (31, "\u001f", -1, 1, 1)] // non printable character - Information Separator One - [InlineData (127, "\u007f", -1, 1, 1)] // non printable character - Delete - [InlineData ('\u20D0', "⃐", 0, 1, 3)] // ◌⃐ Combining Left Harpoon Above + [InlineData ('\u231c', "⌜", 1, 1, 3)] // ⌜ Top Left Corner + + // BUGBUG: These are CLEARLY wide glyphs, but GetColumns() returns 1 + // However, most terminals treat these as narrow and they overlap the next cell when drawn (including Windows Terminal) + [InlineData ('\u1161', "ᅡ", 1, 1, 3)] // ᅡ Hangul Jungseong A - Unicode Hangul Jamo for join with column width equal to 0 alone. + [InlineData ('\u2103', "℃", 1, 1, 3)] // ℃ Degree Celsius + [InlineData ('\u2501', "━", 1, 1, 3)] // ━ Box Drawings Heavy Horizontal [InlineData ('\u25a0', "■", 1, 1, 3)] // ■ Black Square [InlineData ('\u25a1', "□", 1, 1, 3)] // □ White Square + [InlineData ('\u277f', "❿", 1, 1, 3)] //Dingbat Negative Circled Number Ten - ❿ U+277f + [InlineData ('\u4dc0', "䷀", 1, 1, 3)] // ䷀Hexagram For The Creative Heaven - U+4dc0 - https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml + [InlineData ('\ud7b0', "ힰ", 1, 1, 3)] // ힰ ┤Hangul Jungseong O-Yeo - ힰ U+d7b0')] [InlineData ('\uf61e', "", 1, 1, 3)] // Private Use Area - [InlineData ('\u2103', "℃", 1, 1, 3)] // ℃ Degree Celsius + + [InlineData ('\u23f0', "⏰", 2, 1, 3)] // Alarm Clock - ⏰ U+23f0 [InlineData ('\u1100', "ᄀ", 2, 1, 3)] // ᄀ Hangul Choseong Kiyeok - [InlineData ('\u2501', "━", 1, 1, 3)] // ━ Box Drawings Heavy Horizontal + [InlineData ('\u1150', "ᅐ", 2, 1, 3)] // ᅐ Hangul Choseong Ceongchieumcieuc [InlineData ('\u2615', "☕", 2, 1, 3)] // ☕ Hot Beverage [InlineData ('\u231a', "⌚", 2, 1, 3)] // ⌚ Watch [InlineData ('\u231b', "⌛", 2, 1, 3)] // ⌛ Hourglass - [InlineData ('\u231c', "⌜", 1, 1, 3)] // ⌜ Top Left Corner - [InlineData ('\u1dc0', "᷀", 0, 1, 3)] // ◌᷀ Combining Dotted Grave Accent - public void GetColumns_With_Single_Code (int code, string str, int runeLength, int stringLength, int utf8Length) + + // From WindowsTerminal's CodepointWidthDetector tests (https://github.com/microsoft/terminal/blob/main/src/types/CodepointWidthDetector.cpp) + //static constexpr std::wstring_view emoji = L"\xD83E\xDD22"; // U+1F922 nauseated face + //static constexpr std::wstring_view ambiguous = L"\x414"; // U+0414 cyrillic capital de + + //{ 0x414, L"\x414", CodepointWidth::Narrow }, // U+0414 cyrillic capital de + [InlineData ('\u0414', "Д", 1, 1, 2)] // U+0414 cyrillic capital de + + //{ 0x1104, L"\x1104", CodepointWidth::Wide }, // U+1104 hangul choseong ssangtikeut + [InlineData ('\u1104', "ᄄ", 2, 1, 3)] + + //{ 0x306A, L"\x306A", CodepointWidth::Wide }, // U+306A hiragana na な + [InlineData (0x306A, "な", 2, 1, 3)] + + //{ 0x30CA, L"\x30CA", CodepointWidth::Wide }, // U+30CA katakana na ナ + [InlineData (0x30CA, "ナ", 2, 1, 3)] + + //{ 0x72D7, L"\x72D7", CodepointWidth::Wide }, // U+72D7 + [InlineData (0x72D7, "狗", 2, 1, 3)] + + + public void GetColumns_With_Single_Code (int code, string str, int columns, int stringLength, int utf8Length) { var rune = new Rune (code); Assert.Equal (str, rune.ToString ()); - Assert.Equal (runeLength, rune.GetColumns ()); + Assert.Equal (columns, rune.GetColumns ()); Assert.Equal (stringLength, rune.ToString ().Length); Assert.Equal (utf8Length, rune.Utf8SequenceLength); Assert.True (Rune.IsValid (rune.Value)); @@ -97,12 +138,12 @@ public void GetColumns_With_Single_Code (int code, string str, int runeLength, i [InlineData (new byte [] { 0xf0, 0x9f, 0xa4, 0x96 }, "🤖", 2, 2)] // 🤖 Robot Face [InlineData (new byte [] { 0xf0, 0x90, 0x90, 0xa1 }, "𐐡", 1, 2)] // 𐐡 Deseret Capital Letter Er [InlineData (new byte [] { 0xf0, 0x9f, 0x8c, 0xb9 }, "🌹", 2, 2)] // 🌹 Rose - public void GetColumns_Utf8_Encode (byte [] code, string str, int runeLength, int stringLength) + public void GetColumns_Utf8_Encode (byte [] code, string str, int columns, int stringLength) { var operationStatus = Rune.DecodeFromUtf8 (code, out Rune rune, out int bytesConsumed); Assert.Equal (OperationStatus.Done, operationStatus); Assert.Equal (str, rune.ToString ()); - Assert.Equal (runeLength, rune.GetColumns ()); + Assert.Equal (columns, rune.GetColumns ()); Assert.Equal (stringLength, rune.ToString ().Length); Assert.Equal (bytesConsumed, rune.Utf8SequenceLength); Assert.True (Rune.IsValid (rune.Value)); @@ -116,11 +157,13 @@ public void GetColumns_Utf8_Encode (byte [] code, string str, int runeLength, in [InlineData (new char [] { '\ud83e', '\udde0' }, "🧠", 2, 2, 4)] // 🧠 Brain [InlineData (new char [] { '\ud801', '\udc21' }, "𐐡", 1, 2, 4)] // 𐐡 Deseret Capital Letter Er [InlineData (new char [] { '\ud83c', '\udf39' }, "🌹", 2, 2, 4)] // 🌹 Rose - public void GetColumns_Utf16_Encode (char [] code, string str, int runeLength, int stringLength, int utf8Length) + [InlineData (new char [] { '\uD83D', '\uDC7E' }, "👾", 2, 2, 4)] // U+1F47E alien monster (CodepointWidth::Wide) + [InlineData (new char [] { '\uD83D', '\uDD1C' }, "🔜", 2, 2, 4)] // 🔜 Soon With Rightwards Arrow Above (CodepointWidth::Wide) + public void GetColumns_Utf16_Encode (char [] code, string str, int columns, int stringLength, int utf8Length) { var rune = new Rune (code [0], code [1]); Assert.Equal (str, rune.ToString ()); - Assert.Equal (runeLength, rune.GetColumns ()); + Assert.Equal (columns, rune.GetColumns ()); Assert.Equal (stringLength, rune.ToString ().Length); Assert.Equal (utf8Length, rune.Utf8SequenceLength); Assert.True (Rune.IsValid (rune.Value)); @@ -134,12 +177,14 @@ public void GetColumns_Utf16_Encode (char [] code, string str, int runeLength, i [InlineData ("\U0001f9e0", "🧠", 2, 2)] // 🧠 Brain [InlineData ("\U00010421", "𐐡", 1, 2)] // 𐐡 Deseret Capital Letter Er [InlineData ("\U0001f339", "🌹", 2, 2)] // 🌹 Rose - public void GetColumns_Utf32_Encode (string code, string str, int runeLength, int stringLength) + //[InlineData ("\uFE20FE21", "", 1, 1)] // Combining Ligature Left Half - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml + // Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml + public void GetColumns_Utf32_Encode (string code, string str, int columns, int stringLength) { var operationStatus = Rune.DecodeFromUtf16 (code, out Rune rune, out int charsConsumed); Assert.Equal (OperationStatus.Done, operationStatus); Assert.Equal (str, rune.ToString ()); - Assert.Equal (runeLength, rune.GetColumns ()); + Assert.Equal (columns, rune.GetColumns ()); Assert.Equal (stringLength, rune.ToString ().Length); Assert.Equal (charsConsumed, rune.Utf16SequenceLength); Assert.True (Rune.IsValid (rune.Value)); @@ -147,7 +192,7 @@ public void GetColumns_Utf32_Encode (string code, string str, int runeLength, in // with DecodeRune (var nrune, var size) = code.DecodeRune (); Assert.Equal (str, nrune.ToString ()); - Assert.Equal (runeLength, nrune.GetColumns ()); + Assert.Equal (columns, nrune.GetColumns ()); Assert.Equal (stringLength, nrune.ToString ().Length); Assert.Equal (size, nrune.Utf8SequenceLength); for (int x = 0; x < code.Length - 1; x++) { @@ -165,12 +210,12 @@ public void GetColumns_Utf32_Encode (string code, string str, int runeLength, in [InlineData ("\u0915\u093f", "कि", 2, 2, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I [InlineData ("\u0e4d\u0e32", "ํา", 2, 1, 2)] // Decomposition: ํ (U+0E4D) - า (U+0E32) = U+0E33 ำ Thai Character Sara Am [InlineData ("\u0e33", "ำ", 1, 1, 1)] // Decomposition: ํ (U+0E4D) - า (U+0E32) = U+0E33 ำ Thai Character Sara Am - public void GetColumns_String_Without_SurrogatePair (string code, string str, int codeLength, int runesLength, int stringLength) + public void GetColumns_String_Without_SurrogatePair (string code, string str, int codeLength, int columns, int stringLength) { Assert.Equal (str, code.Normalize ()); Assert.Equal (codeLength, code.Length); - Assert.Equal (runesLength, code.EnumerateRunes ().Sum (x => x.GetColumns ())); - Assert.Equal (runesLength, str.GetColumns ()); + //Assert.Equal (columns, code.EnumerateRunes ().Sum (x => x.GetColumns ())); + Assert.Equal (columns, str.GetColumns ()); Assert.Equal (stringLength, str.Length); } diff --git a/UnitTests/Text/StringTests.cs b/UnitTests/Text/StringTests.cs index 8c9ee36bbd..9ed93dc2f4 100644 --- a/UnitTests/Text/StringTests.cs +++ b/UnitTests/Text/StringTests.cs @@ -51,6 +51,8 @@ public void TestGetColumns_MultiRune (string str, int expected) [InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 16)] [InlineData ("山", 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 [InlineData ("山🙂", 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 + //[InlineData ("\ufe20\ufe21", 2)] // Combining Ligature Left Half ︠ - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml + // // Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml public void TestGetColumns_MultiRune_WideBMP (string str, int expected) { Assert.Equal (expected, str.GetColumns ()); From 235058ea88d37a0bb4e65de44a047ded89886ea7 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 9 Aug 2023 07:11:35 -0600 Subject: [PATCH 92/99] Charmap option to show glyph widths --- UICatalog/Scenarios/CharacterMap.cs | 159 ++++++++++++++++------------ 1 file changed, 93 insertions(+), 66 deletions(-) diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 11066ac770..7c3e9da6a4 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -32,21 +32,28 @@ public class CharacterMap : Scenario { Label _errorLabel; TableView _categoryList; + // Don't create a Window, just return the top-level view + public override void Init () + { + Application.Init (); + Application.Top.ColorScheme = Colors.Base; + } + public override void Setup () { _charMap = new CharMap () { X = 0, - Y = 0, + Y = 1, Height = Dim.Fill () }; - Win.Add (_charMap); + Application.Top.Add (_charMap); var jumpLabel = new Label ("Jump To Code Point:") { X = Pos.Right (_charMap) + 1, Y = Pos.Y (_charMap) }; - Win.Add (jumpLabel); + Application.Top.Add (jumpLabel); var jumpEdit = new TextField () { X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, Caption = "e.g. 01BE3" }; - Win.Add (jumpEdit); + Application.Top.Add (jumpEdit); _errorLabel = new Label ("") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] }; - Win.Add (_errorLabel); + Application.Top.Add (_errorLabel); jumpEdit.TextChanged += JumpEdit_TextChanged; @@ -98,15 +105,38 @@ public override void Setup () _charMap.StartCodePoint = table.Data.ToArray () [args.NewRow].Start; }; - Win.Add (_categoryList); + Application.Top.Add (_categoryList); _charMap.SelectedCodePoint = 0; //jumpList.Refresh (); _charMap.SetFocus (); _charMap.Width = Dim.Fill () - _categoryList.Width; + + var menu = new MenuBar (new MenuBarItem [] { + new MenuBarItem ("_File", new MenuItem [] { + new MenuItem ("_Quit", $"{Application.QuitKey}", () => Application.RequestStop ()), + }), + new MenuBarItem ("_Options", new MenuItem [] { + CreateMenuShowWidth (), + }) + }); + Application.Top.Add (menu); } + MenuItem CreateMenuShowWidth () + { + var item = new MenuItem { + Title = "_Show Glyph Width", + }; + item.CheckType |= MenuItemCheckStyle.Checked; + item.Checked = _charMap?.ShowGlyphWidths; + item.Action += () => { + _charMap.ShowGlyphWidths = (bool)(item.Checked = !item.Checked); + }; + + return item; + } EnumerableTableSource CreateCategoryTable (int sortByColumn, bool descending) { @@ -194,7 +224,6 @@ private void JumpEdit_TextChanged (object sender, TextChangedEventArgs e) class CharMap : ScrollView { - public bool ShowColumnWidths { get; set;} /// /// Specifies the starting offset for the character map. The default is 0x2500 /// which is the Box Drawing characters. @@ -220,7 +249,7 @@ public int SelectedCodePoint { set { _selected = value; var row = (SelectedCodePoint / 16 * _rowHeight); - var col = (SelectedCodePoint % 16 * COLUMN_WIDTH); + var col = (SelectedCodePoint % 16 * COLUMN_WIDTH); var height = (Bounds.Height) - (ShowHorizontalScrollIndicator ? 2 : 1); if (row + ContentOffset.Y < 0) { @@ -257,7 +286,7 @@ public Point Cursor { public override void PositionCursor () { - if (HasFocus && + if (HasFocus && Cursor.X >= RowLabelWidth && Cursor.X < Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0) && Cursor.Y > 0 && @@ -269,12 +298,19 @@ public override void PositionCursor () } } + public bool ShowGlyphWidths { + get => _rowHeight == 2; + set { + _rowHeight = value ? 2 : 1; + SetNeedsDisplay (); + } + } int _start = 0; int _selected = 0; const int COLUMN_WIDTH = 3; - int _rowHeight = 2; + int _rowHeight = 1; public static int MaxCodePoint => 0x10FFFF; @@ -405,51 +441,46 @@ public override void OnDrawContentComplete (Rect contentArea) for (int y = 1; y < Bounds.Height; y++) { // What row is this? var row = (y - ContentOffset.Y) / _rowHeight; - //Move (0, y); - //Driver.SetAttribute (HasFocus && (cursorRow + ContentOffset.Y + 1 == y) ? ColorScheme.HotFocus : ColorScheme.HotNormal); - //Driver.AddStr ($"y={y},r={row}"); - - // are we at first row of the row? - if ((y - ContentOffset.Y) % _rowHeight > 0) { - var val = (row) * 16; - if (val > MaxCodePoint) { - continue; + + var val = (row) * 16; + if (val > MaxCodePoint) { + continue; + } + Move (firstColumnX + COLUMN_WIDTH, y); + Driver.SetAttribute (GetNormalColor ()); + for (int col = 0; col < 16; col++) { + + var x = firstColumnX + COLUMN_WIDTH * col + 1; + + Move (x, y); + if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { + Driver.SetAttribute (GetFocusColor ()); + } + var scalar = val + col; + Rune rune = (Rune)'?'; + if (Rune.IsValid (scalar)) { + rune = new Rune (scalar); } - Move (firstColumnX + COLUMN_WIDTH, y); - Driver.SetAttribute (GetNormalColor ()); - for (int col = 0; col < 16; col++) { - - var x = firstColumnX + COLUMN_WIDTH * col + 1; - - Move (x, y); - if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { - Driver.SetAttribute (GetFocusColor ()); - } - var scalar = val + col; - Rune rune = (Rune)'?'; - if (Rune.IsValid (scalar)) { - rune = new Rune (scalar); - } - var width = rune.GetColumns (); - //if (width == 0) { - // if (rune.IsCombiningMark ()) { - // Driver.AddRune (Rune.ReplacementChar); - // } - //} + var width = rune.GetColumns (); + // are we at first row of the row? + if (!ShowGlyphWidths || (y - ContentOffset.Y) % _rowHeight > 0) { Driver.AddRune (rune); + } else { + Driver.SetAttribute (ColorScheme.HotNormal); + Driver.AddStr ($"{width}"); + } - if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { - Driver.SetAttribute (GetNormalColor ()); - } + if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) { + Driver.SetAttribute (GetNormalColor ()); } - Move (0, y); - Driver.SetAttribute (HasFocus && (cursorRow + ContentOffset.Y + 1 == y) ? ColorScheme.HotFocus : ColorScheme.HotNormal); - var rowLabel = $"U+{val / 16:x5}_ "; - Driver.AddStr (rowLabel); + } + Move (0, y); + Driver.SetAttribute (HasFocus && (cursorRow + ContentOffset.Y + 1 == y) ? ColorScheme.HotFocus : ColorScheme.HotNormal); + if (!ShowGlyphWidths || (y - ContentOffset.Y) % _rowHeight > 0) { + Driver.AddStr ($"U+{val / 16:x5}_ "); } else { - //Move (firstColumnX + 1, y); - //Driver.AddStr ("+++++++++++++++++++"); + Driver.AddStr (new string (' ', RowLabelWidth)); } } Driver.Clip = oldClip; @@ -460,33 +491,29 @@ void Handle_MouseClick (object sender, MouseEventEventArgs args) { var me = args.MouseEvent; if (me.Flags == MouseFlags.ReportMousePosition || (me.Flags != MouseFlags.Button1Clicked && - me.Flags != MouseFlags.Button1DoubleClicked)) { // && me.Flags != _contextMenu.MouseFlags)) { + me.Flags != MouseFlags.Button1DoubleClicked)) { return; } - //if (me.X < RowLabelWidth) { - // return; - //} + if (me.Y == 0) { + me.Y = Cursor.Y ; + } - //if (me.Y < 1) { - // return; - //} + if (me.Y > 0) { + } + + if (me.X < RowLabelWidth || me.X > RowLabelWidth + (16 * COLUMN_WIDTH) - 1) { + me.X = Cursor.X ; + } var row = (me.Y - 1 - ContentOffset.Y) / _rowHeight; // -1 for header var col = (me.X - RowLabelWidth - ContentOffset.X) / COLUMN_WIDTH; - //if (row < 0 || row > Bounds.Height || col > 15) { - // return; - //} - - if (row < 0) { - row = (Cursor.Y) / _rowHeight; - } - if (col < 0) { - col = (Cursor.X) / COLUMN_WIDTH; + if (col > 15) { + col = 15; } - var val = (row ) * 16 + col; + var val = (row) * 16 + col; if (val > MaxCodePoint) { return; } From 8fe56cb3792aa72ca182fbc7ed15f85c2c70baa0 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 9 Aug 2023 07:41:26 -0600 Subject: [PATCH 93/99] Fixed issue with wide glpyhs being overwritten --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 33 +++++++++++++------- UICatalog/Scenarios/CharacterMap.cs | 3 ++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 3f2d1da83c..ae0321bbfb 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -160,7 +160,7 @@ public void AddRune (Rune rune) // Normalize to Form C (Canonical Composition) string normalized = combined.Normalize (NormalizationForm.FormC); - Contents [Row, Col - 1].Runes [0] = (Rune)normalized [0]; + Contents [Row, Col - 1].Runes = new List { (Rune)normalized [0] }; ; Contents [Row, Col - 1].Attribute = CurrentAttribute; Contents [Row, Col - 1].IsDirty = true; @@ -173,24 +173,33 @@ public void AddRune (Rune rune) // Check if cell to left has a wide glyph if (Contents [Row, Col - 1].Runes [0].GetColumns () > 1) { // Invalidate cell to left - Contents [Row, Col - 1].Runes [0] = Rune.ReplacementChar; + Contents [Row, Col - 1].Runes = new List { Rune.ReplacementChar }; Contents [Row, Col - 1].IsDirty = true; } } - // Check if we're at right edge of clip - if (runeWidth < 1 || (runeWidth == 2 && Col == Clip.Right - 1)) { - // Can't put a double width glyph here - Contents [Row, Col].Runes [0] = Rune.ReplacementChar; - } else { - if (runeWidth >= 1) { - // This is a single-width character, or we are not at the end of the line. - Contents [Row, Col].Runes [0] = rune; + if (runeWidth < 1) { + Contents [Row, Col].Runes = new List { Rune.ReplacementChar }; + + } else if (runeWidth == 1) { + Contents [Row, Col].Runes = new List { rune }; + if (Col < Clip.Right - 1) { + Contents [Row, Col + 1].IsDirty = true; + } + } else if (runeWidth == 2) { + if (Col == Clip.Right - 1) { + Contents [Row, Col].Runes = new List { Rune.ReplacementChar }; } else { - Contents [Row, Col].Runes [0] = (Rune)'^'; - Contents [Row, Col].IsDirty = false; + Contents [Row, Col].Runes = new List { rune }; + if (Col < Clip.Right - 1) { + Contents [Row, Col + 1].Runes = new List { (Rune)' ' }; + Contents [Row, Col + 1].IsDirty = true; + } } + } else { + Contents [Row, Col].Runes = new List { (Rune)' ' }; + Contents [Row, Col].IsDirty = false; } //if (runeWidth < 2 && Col > 0 && Contents [Row, Col - 1].Runes [0].GetColumns () > 1) { // // This is a single-width character, and we are not at the beginning of the line. diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 7c3e9da6a4..1647edcbcb 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -404,6 +404,9 @@ public override void OnDrawContent (Rect contentArea) //public void CharMap_DrawContent (object s, DrawEventArgs a) public override void OnDrawContentComplete (Rect contentArea) { + if (contentArea.Height == 0 || contentArea.Width == 0) { + return; + } Rect viewport = new Rect (ContentOffset, new Size (Math.Max (Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0), 0), Math.Max (Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0), 0))); From 7790f56847640fae3960e0766c2a1ae5189e00ff Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 9 Aug 2023 07:44:50 -0600 Subject: [PATCH 94/99] Fixed charmap startcodepoint change issue --- UICatalog/Scenarios/CharacterMap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 1647edcbcb..083071f427 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -232,8 +232,8 @@ public int StartCodePoint { get => _start; set { _start = value; - _selected = value; - ContentOffset = new Point (0, (int)(_start / 16)); + SelectedCodePoint = value; + //ContentOffset = new Point (0, (int)(_start / 16)); SetNeedsDisplay (); } } From e526fb2cdfac947aabbd172097b9ab89d0a94525 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 9 Aug 2023 10:06:17 -0600 Subject: [PATCH 95/99] Added abiltiy to see ncurses verison/lib --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 6 + .../CursesDriver/CursesDriver.cs | 158 ++++++++++++++---- .../CursesDriver/UnmanagedLibrary.cs | 10 +- .../ConsoleDrivers/CursesDriver/binding.cs | 14 ++ .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 3 +- UICatalog/Scenarios/CharacterMap.cs | 1 - UICatalog/UICatalog.cs | 2 +- 7 files changed, 150 insertions(+), 44 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index ae0321bbfb..66e9387d37 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -534,6 +534,12 @@ public void FillRect (Rect rect, Rune rune = default) /// Ends the execution of the console driver. /// public abstract void End (); + + /// + /// Returns the name of the driver and relevant library version information. + /// + /// + public virtual string GetVersionInfo () => GetType ().Name; } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 03203418a8..0faa5d4073 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -19,6 +19,8 @@ internal class CursesDriver : ConsoleDriver { CursorVisibility? _initialCursorVisibility = null; CursorVisibility? _currentCursorVisibility = null; + public override string GetVersionInfo () => $"{Curses.curses_version()}"; + public override void Move (int col, int row) { base.Move (col, row); @@ -204,6 +206,9 @@ public override void UpdateScreen () } _dirtyLines [row] = false; + // HACK: Clear line + //Curses.mvaddwstr (row, 0, new string ('_', Cols)); + for (int col = 0; col < Cols; col++) { if (Contents [row, col].IsDirty == false) { continue; @@ -212,8 +217,9 @@ public override void UpdateScreen () if (Contents [row, col].Runes [0].IsBmp) { //_outputBuffer [position].Char = (char)Contents [row, col].Runes [0].Value; - Curses.mvaddwstr (row, col, Contents [row, col].Runes [0].ToString ()); - if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { + var rune = Contents [row, col].Runes [0]; + Curses.mvaddch (row, col, rune.Value); + if (Contents [row, col].Runes [0].GetColumns () == 2 && col + 1 < Cols) { col++; //Curses.mvaddwstr (row, col, ' '); } @@ -229,43 +235,123 @@ public override void UpdateScreen () } } } - - //for (int col = 0; col < Cols; col++) { - // //Curses.mvaddch (row, col, '+'); - - // if (Contents [row, col].IsDirty == false) { - // //Curses.mvaddch (row, col, (char)Rune.ReplacementChar.Value); - // continue; - // } - - // Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().Value); - - // if (Contents [row, col].Runes [0].IsBmp) { - // Curses.mvaddch (row, col, Contents [row, col].Runes [0].Value); - // } else { - // //_outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; - // if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { - // // TODO: This is a hack to deal with non-BMP and wide characters. - // //col++; - // //_outputBuffer [position].Empty = false; - // //_outputBuffer [position].Char = ' '; - // //Curses.mvaddch (row, col, '*'); - // } - // } - - // if (Contents [row, col].Runes [0].IsSurrogatePair () && Contents [row, col].Runes [0].GetColumns () < 2) { - // col--; - // } - - // //if (col < Cols && Contents [row, col].Runes [0].GetColumns () > 1) { - // // col++; - // // Curses.mvaddch (row, col, '='); - // //} - //} } - Curses.move (Row, Col); + Curses.move (Row, Col); _window.wrefresh (); + + //for (int col = 0; col < Cols; col++) { + // //Curses.mvaddch (row, col, '+'); + + // if (Contents [row, col].IsDirty == false) { + // //Curses.mvaddch (row, col, (char)Rune.ReplacementChar.Value); + // continue; + // } + + // Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().Value); + + // if (Contents [row, col].Runes [0].IsBmp) { + // Curses.mvaddch (row, col, Contents [row, col].Runes [0].Value); + // } else { + // //_outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; + // if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { + // // TODO: This is a hack to deal with non-BMP and wide characters. + // //col++; + // //_outputBuffer [position].Empty = false; + // //_outputBuffer [position].Char = ' '; + // //Curses.mvaddch (row, col, '*'); + // } + // } + + // if (Contents [row, col].Runes [0].IsSurrogatePair () && Contents [row, col].Runes [0].GetColumns () < 2) { + // col--; + // } + + // //if (col < Cols && Contents [row, col].Runes [0].GetColumns () > 1) { + // // col++; + // // Curses.mvaddch (row, col, '='); + // //} + //} + //} + + + //var top = 0; + //var left = 0; + //var rows = Rows; + //var cols = Cols; + //System.Text.StringBuilder output = new System.Text.StringBuilder (); + //Attribute redrawAttr = new Attribute (); + //var lastCol = -1; + + //Curses.attrset (MakeColor (ColorToCursesColorNumber (Color.White), ColorToCursesColorNumber (Color.Black)).Value); + + ////GetCursorVisibility (out CursorVisibility savedVisibitity); + ////SetCursorVisibility (CursorVisibility.Invisible); + + //for (var row = top; row < rows; row++) { + // //if (Console.WindowHeight < 1) { + // // return; + // //} + // if (!_dirtyLines [row]) { + // continue; + // } + // Curses.move (Row, 0); + + // _dirtyLines [row] = false; + // output.Clear (); + // for (var col = left; col < cols; col++) { + // lastCol = -1; + // var outputWidth = 0; + // for (; col < cols; col++) { + // if (!Contents [row, col].IsDirty) { + // if (output.Length > 0) { + // WriteToConsole (output, ref lastCol, row, ref outputWidth); + // } else if (lastCol == -1) { + // lastCol = col; + // } + // if (lastCol + 1 < cols) + // lastCol++; + // continue; + // } + + // if (lastCol == -1) { + // lastCol = col; + // } + + // Attribute attr = Contents [row, col].Attribute.Value; + // // Performance: Only send the escape sequence if the attribute has changed. + // if (attr != redrawAttr) { + // redrawAttr = attr; + // //Curses.attrset (attr.Value); + // //output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Value)); + // //output.Append (EscSeqUtils.CSI_SetBackgroundColor (0)); + // } + // outputWidth++; + // var rune = (Rune)Contents [row, col].Runes [0]; + // output.Append (rune.ToString ()); + // if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { + // WriteToConsole (output, ref lastCol, row, ref outputWidth); + // Curses.move (Row, Col - 1); + // } + // Contents [row, col].IsDirty = false; + // } + // } + // if (output.Length > 0) { + // Curses.mvaddwstr (row, lastCol, output.ToString ()); + // } + //} + + //Curses.move (Row, Col); + + //_window.wrefresh (); + + //void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth) + //{ + // Curses.mvaddwstr (row, lastCol, output.ToString()); + // output.Clear (); + // lastCol += outputWidth; + // outputWidth = 0; + //} } public Curses.Window _window; diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs index 67dbe236fd..1edb532756 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs @@ -91,7 +91,7 @@ static UnmanagedLibrary () const int RTLD_LAZY = 1; const int RTLD_GLOBAL = 8; - readonly string libraryPath; + public readonly string LibraryPath; readonly IntPtr handle; public IntPtr NativeLibraryHandle => handle; @@ -104,13 +104,15 @@ static UnmanagedLibrary () public UnmanagedLibrary (string [] libraryPathAlternatives, bool isFullPath) { if (isFullPath) { - this.libraryPath = FirstValidLibraryPath (libraryPathAlternatives); - this.handle = PlatformSpecificLoadLibrary (this.libraryPath); + this.LibraryPath = FirstValidLibraryPath (libraryPathAlternatives); + this.handle = PlatformSpecificLoadLibrary (this.LibraryPath); } else { foreach (var lib in libraryPathAlternatives) { this.handle = PlatformSpecificLoadLibrary (lib); - if (this.handle != IntPtr.Zero) + if (this.handle != IntPtr.Zero) { + this.LibraryPath = lib; break; + } } } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs index 391bfaa716..804ab88934 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs @@ -71,6 +71,7 @@ public struct MouseEvent { //static bool use_naked_driver; static UnmanagedLibrary curses_library; + static NativeMethods methods; [DllImport ("libc")] @@ -97,6 +98,13 @@ static void FindNCurses () cols_ptr = get_ptr ("COLS"); } + static public string curses_version () + { + var v = methods.curses_version (); + return $"{Marshal.PtrToStringAnsi (v)}, {curses_library.LibraryPath}"; + + } + static public Window initscr () { setlocale (LC_ALL, ""); @@ -116,6 +124,9 @@ static public Window initscr () "or DYLD_LIBRARY_PATH directory or run /sbin/ldconfig"); Environment.Exit (1); } + + //Console.Error.WriteLine ($"using curses {Curses.curses_version ()}"); + return main_window; } @@ -433,6 +444,7 @@ internal class Delegates { public delegate int savetty (); public delegate int resetty (); public delegate int set_escdelay (int size); + public delegate IntPtr curses_version (); } internal class NativeMethods { @@ -507,6 +519,7 @@ internal class NativeMethods { public readonly Delegates.savetty savetty; public readonly Delegates.resetty resetty; public readonly Delegates.set_escdelay set_escdelay; + public readonly Delegates.curses_version curses_version; public UnmanagedLibrary UnmanagedLibrary; public NativeMethods (UnmanagedLibrary lib) @@ -583,6 +596,7 @@ public NativeMethods (UnmanagedLibrary lib) savetty = lib.GetNativeMethodDelegate ("savetty"); resetty = lib.GetNativeMethodDelegate ("resetty"); set_escdelay = lib.GetNativeMethodDelegate ("set_escdelay"); + curses_version = lib.GetNativeMethodDelegate ("curses_version"); } } #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 6e06903bc0..5e1ae851f5 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -67,8 +67,7 @@ public override void End () FakeConsole.ResetColor (); FakeConsole.Clear (); } - - + public override void Init (Action terminalResized) { FakeConsole.MockKeyPresses.Clear (); diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 083071f427..d4423397ee 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -233,7 +233,6 @@ public int StartCodePoint { set { _start = value; SelectedCodePoint = value; - //ContentOffset = new Point (0, (int)(_start / 16)); SetNeedsDisplay (); } } diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 06960b4c04..141015d398 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -422,7 +422,7 @@ void LoadedHandler (object? sender, EventArgs? args) ConfigChanged (); miIsMouseDisabled!.Checked = Application.IsMouseDisabled; - DriverName.Title = $"Driver: {Driver.GetType ().Name}"; + DriverName.Title = $"Driver: {Driver.GetVersionInfo()}"; OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}"; if (_selectedScenario != null) { From d4f0f69b03d6370cbb3331e5162ed0a1f9ec50cf Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 9 Aug 2023 11:14:02 -0600 Subject: [PATCH 96/99] Fought with CursesDriver; giving up for now. See notes. --- .../CursesDriver/CursesDriver.cs | 129 +----------------- .../ConsoleDrivers/CursesDriver/binding.cs | 6 + .../ConsoleDrivers/CursesDriver/handles.cs | 7 +- 3 files changed, 18 insertions(+), 124 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 0faa5d4073..24938cea09 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -206,29 +206,25 @@ public override void UpdateScreen () } _dirtyLines [row] = false; - // HACK: Clear line - //Curses.mvaddwstr (row, 0, new string ('_', Cols)); - for (int col = 0; col < Cols; col++) { if (Contents [row, col].IsDirty == false) { continue; } Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().Value); - if (Contents [row, col].Runes [0].IsBmp) { - //_outputBuffer [position].Char = (char)Contents [row, col].Runes [0].Value; - var rune = Contents [row, col].Runes [0]; + var rune = Contents [row, col].Runes [0]; + if (rune.IsBmp) { + // BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell. Curses.mvaddch (row, col, rune.Value); - if (Contents [row, col].Runes [0].GetColumns () == 2 && col + 1 < Cols) { + if (rune.GetColumns() == 2 && col + 1 < Cols) { col++; + // BUGBUG: //Curses.mvaddwstr (row, col, ' '); } } else { - //_outputBuffer [position].Empty = true; - //_outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; Curses.mvaddch (row, col, '#'); - if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { + if (rune.GetColumns () > 1 && col + 1 < Cols) { // TODO: This is a hack to deal with non-BMP and wide characters. //col++; Curses.mvaddch (row, ++col, '*'); @@ -239,119 +235,6 @@ public override void UpdateScreen () Curses.move (Row, Col); _window.wrefresh (); - - //for (int col = 0; col < Cols; col++) { - // //Curses.mvaddch (row, col, '+'); - - // if (Contents [row, col].IsDirty == false) { - // //Curses.mvaddch (row, col, (char)Rune.ReplacementChar.Value); - // continue; - // } - - // Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().Value); - - // if (Contents [row, col].Runes [0].IsBmp) { - // Curses.mvaddch (row, col, Contents [row, col].Runes [0].Value); - // } else { - // //_outputBuffer [position].Char = (char)Rune.ReplacementChar.Value; - // if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) { - // // TODO: This is a hack to deal with non-BMP and wide characters. - // //col++; - // //_outputBuffer [position].Empty = false; - // //_outputBuffer [position].Char = ' '; - // //Curses.mvaddch (row, col, '*'); - // } - // } - - // if (Contents [row, col].Runes [0].IsSurrogatePair () && Contents [row, col].Runes [0].GetColumns () < 2) { - // col--; - // } - - // //if (col < Cols && Contents [row, col].Runes [0].GetColumns () > 1) { - // // col++; - // // Curses.mvaddch (row, col, '='); - // //} - //} - //} - - - //var top = 0; - //var left = 0; - //var rows = Rows; - //var cols = Cols; - //System.Text.StringBuilder output = new System.Text.StringBuilder (); - //Attribute redrawAttr = new Attribute (); - //var lastCol = -1; - - //Curses.attrset (MakeColor (ColorToCursesColorNumber (Color.White), ColorToCursesColorNumber (Color.Black)).Value); - - ////GetCursorVisibility (out CursorVisibility savedVisibitity); - ////SetCursorVisibility (CursorVisibility.Invisible); - - //for (var row = top; row < rows; row++) { - // //if (Console.WindowHeight < 1) { - // // return; - // //} - // if (!_dirtyLines [row]) { - // continue; - // } - // Curses.move (Row, 0); - - // _dirtyLines [row] = false; - // output.Clear (); - // for (var col = left; col < cols; col++) { - // lastCol = -1; - // var outputWidth = 0; - // for (; col < cols; col++) { - // if (!Contents [row, col].IsDirty) { - // if (output.Length > 0) { - // WriteToConsole (output, ref lastCol, row, ref outputWidth); - // } else if (lastCol == -1) { - // lastCol = col; - // } - // if (lastCol + 1 < cols) - // lastCol++; - // continue; - // } - - // if (lastCol == -1) { - // lastCol = col; - // } - - // Attribute attr = Contents [row, col].Attribute.Value; - // // Performance: Only send the escape sequence if the attribute has changed. - // if (attr != redrawAttr) { - // redrawAttr = attr; - // //Curses.attrset (attr.Value); - // //output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Value)); - // //output.Append (EscSeqUtils.CSI_SetBackgroundColor (0)); - // } - // outputWidth++; - // var rune = (Rune)Contents [row, col].Runes [0]; - // output.Append (rune.ToString ()); - // if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { - // WriteToConsole (output, ref lastCol, row, ref outputWidth); - // Curses.move (Row, Col - 1); - // } - // Contents [row, col].IsDirty = false; - // } - // } - // if (output.Length > 0) { - // Curses.mvaddwstr (row, lastCol, output.ToString ()); - // } - //} - - //Curses.move (Row, Col); - - //_window.wrefresh (); - - //void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth) - //{ - // Curses.mvaddwstr (row, lastCol, output.ToString()); - // output.Clear (); - // lastCol += outputWidth; - // outputWidth = 0; - //} } public Curses.Window _window; diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs index 804ab88934..da2cf3ccf5 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs @@ -336,10 +336,12 @@ static public int IsAlt (int key) static public int move (int line, int col) => methods.move (line, col); static public int curs_set (int visibility) => methods.curs_set (visibility); //static public int addch (int ch) => methods.addch (ch); + static public int echochar (int ch) => methods.echochar (ch); static public int addwstr (string s) => methods.addwstr (s); static public int mvaddwstr (int y, int x, string s) => methods.mvaddwstr (y, x, s); static public int wmove (IntPtr win, int line, int col) => methods.wmove (win, line, col); static public int waddch (IntPtr win, int ch) => methods.waddch (win, ch); + //static public int wechochar (IntPtr win, int ch) => methods.wechochar (win, ch); static public int attron (int attrs) => methods.attron (attrs); static public int attroff (int attrs) => methods.attroff (attrs); static public int attrset (int attrs) => methods.attrset (attrs); @@ -411,6 +413,7 @@ internal class Delegates { public delegate int move (int line, int col); public delegate int curs_set (int visibility); public delegate int addch (int ch); + public delegate int echochar (int ch); public delegate int mvaddch (int y, int x, int ch); public delegate int addwstr ([MarshalAs (UnmanagedType.LPWStr)] string s); public delegate int mvaddwstr (int y, int x, [MarshalAs (UnmanagedType.LPWStr)] string s); @@ -486,11 +489,13 @@ internal class NativeMethods { public readonly Delegates.move move; public readonly Delegates.curs_set curs_set; public readonly Delegates.addch addch; + public readonly Delegates.echochar echochar; public readonly Delegates.mvaddch mvaddch; public readonly Delegates.addwstr addwstr; public readonly Delegates.mvaddwstr mvaddwstr; public readonly Delegates.wmove wmove; public readonly Delegates.waddch waddch; + //public readonly Delegates.wechochar wechochar; public readonly Delegates.attron attron; public readonly Delegates.attroff attroff; public readonly Delegates.attrset attrset; @@ -563,6 +568,7 @@ public NativeMethods (UnmanagedLibrary lib) move = lib.GetNativeMethodDelegate ("move"); curs_set = lib.GetNativeMethodDelegate ("curs_set"); addch = lib.GetNativeMethodDelegate ("addch"); + echochar = lib.GetNativeMethodDelegate ("echochar"); mvaddch = lib.GetNativeMethodDelegate ("mvaddch"); addwstr = lib.GetNativeMethodDelegate ("addwstr"); mvaddwstr = lib.GetNativeMethodDelegate ("mvaddwstr"); diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs index 8a46149d50..0158a32512 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs @@ -149,7 +149,12 @@ public int addch (char ch) { return Curses.waddch (Handle, ch); } - + + //public int echochar (char ch) + //{ + // return Curses.wechochar (Handle, ch); + //} + public int refresh () { return Curses.wrefresh (Handle); From 1b8b301a0fbbd1b0c69e46285a9aed13c57f3f14 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 9 Aug 2023 11:38:19 -0600 Subject: [PATCH 97/99] Leverage Wcwidth nuget library instaed of our own tables --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 31 +--- Terminal.Gui/Text/RuneExtensions.cs | 146 ------------------- UnitTests/Text/RuneTests.cs | 51 ++++++- 3 files changed, 57 insertions(+), 171 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 66e9387d37..7a65de1b08 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -189,40 +189,23 @@ public void AddRune (Rune rune) } } else if (runeWidth == 2) { if (Col == Clip.Right - 1) { + // We're at the right edge of the clip, so we can't display a wide character. + // TODO: Figure out if it is better to show a replacement character or ' ' Contents [Row, Col].Runes = new List { Rune.ReplacementChar }; } else { Contents [Row, Col].Runes = new List { rune }; if (Col < Clip.Right - 1) { - Contents [Row, Col + 1].Runes = new List { (Rune)' ' }; + // Invalidate cell to right so that it doesn't get drawn + // TODO: Figure out if it is better to show a replacement character or ' ' + Contents [Row, Col + 1].Runes = new List { Rune.ReplacementChar }; Contents [Row, Col + 1].IsDirty = true; } } } else { + // This is a non-spacing character, so we don't need to do anything Contents [Row, Col].Runes = new List { (Rune)' ' }; Contents [Row, Col].IsDirty = false; } - //if (runeWidth < 2 && Col > 0 && Contents [Row, Col - 1].Runes [0].GetColumns () > 1) { - // // This is a single-width character, and we are not at the beginning of the line. - // Contents [Row, Col - 1].Runes [0] = Rune.ReplacementChar; - // Contents [Row, Col - 1].IsDirty = true; - //} else if (runeWidth < 2 && Col <= Clip.Right - 1 && Contents [Row, Col].Runes [0].GetColumns () > 1) { - // // This is a single-width character, and we are not at the end of the line. - // Contents [Row, Col + 1].Runes [0] = Rune.ReplacementChar; - // Contents [Row, Col + 1].IsDirty = true; - //} - //if (runeWidth > 1 && Col == Clip.Right - 1) { - // // This is a double-width character, and we are at the end of the line. - // Contents [Row, Col].Runes [0] = Rune.ReplacementChar; - //} else { - // if (runeWidth > 0) { - // // This is a single-width character, or we are not at the end of the line. - // Contents [Row, Col].Runes [0] = rune; - // } - // else { - // Contents [Row, Col].Runes [0] = (Rune)'^'; - // Contents [Row, Col].IsDirty = false; - // } - //} _dirtyLines [Row] = true; } } @@ -239,6 +222,8 @@ public void AddRune (Rune rune) // Get rendered. Contents [Row, Col].IsDirty = false; Contents [Row, Col].Attribute = CurrentAttribute; + + // TODO: Determine if we should wipe this out (for now now) //Contents [Row, Col].Runes [0] = (Rune)' '; } Col++; diff --git a/Terminal.Gui/Text/RuneExtensions.cs b/Terminal.Gui/Text/RuneExtensions.cs index 0db68cb18d..8fc1e2abd3 100644 --- a/Terminal.Gui/Text/RuneExtensions.cs +++ b/Terminal.Gui/Text/RuneExtensions.cs @@ -28,23 +28,6 @@ public static class RuneExtensions { public static int GetColumns (this Rune rune) { return UnicodeCalculator.GetWidth (rune); - - //// TODO: I believe there is a way to do this without using our own tables, using Rune. - //// See https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c - //var codePoint = rune.Value; - //switch (codePoint) { - //case < 0x20: - //case >= 0x7f and < 0xa0: - // return -1; - //case < 0x7f: - // return 1; - //} - ///* binary search in table of non-spacing characters */ - //if (BiSearch (codePoint, _combining, _combining.GetLength (0) - 1) != 0) { - // return 0; - //} - ///* if we arrive here, ucs is not a combining or C0/C1 control character */ - //return 1 + (BiSearch (codePoint, _combiningWideChars, _combiningWideChars.GetLength (0) - 1) != 0 ? 1 : 0); } /// @@ -183,133 +166,4 @@ public static bool CanBeEncodedAsRune (byte [] buffer) } return true; } - - // ---------------- implementation details ------------------ - // TODO: Can this be handled by the new .NET 8 Rune type? - static readonly int [,] _combining = new int [,] { - { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, - { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, - { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, - { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, - { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, - { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, - { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, - { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, - { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, - { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, - { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, - { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, - { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, - { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, - { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, - { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, - { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, - { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, - { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, - { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, - { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, - { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, - { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, - { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, - { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, - { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, - { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, - { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, - { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, - { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, - { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, - { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, - { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, - { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, - { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, - { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, - { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, - { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, - { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, - { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x2E9A, 0x2E9A }, - { 0x2EF4, 0x2EFF }, { 0x2FD6, 0x2FEF }, { 0x2FFC, 0x2FFF }, - { 0x31E4, 0x31EF }, { 0x321F, 0x321F }, { 0xA48D, 0xA48F }, - { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 }, - { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE1A, 0xFE1F }, - { 0xFE20, 0xFE23 }, { 0xFE53, 0xFE53 }, { 0xFE67, 0xFE67 }, - { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, - { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, - { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, - { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, - { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, - { 0xE0100, 0xE01EF } - }; - - static readonly int [,] _combiningWideChars = new int [,] { - /* Hangul Jamo init. consonants - 0x1100, 0x11ff */ - /* Miscellaneous Technical - 0x2300, 0x23ff */ - /* Hangul Syllables - 0x11a8, 0x11c2 */ - /* CJK Compatibility Ideographs - f900, fad9 */ - /* Vertical forms - fe10, fe19 */ - /* CJK Compatibility Forms - fe30, fe4f */ - /* Fullwidth Forms - ff01, ffee */ - /* Alphabetic Presentation Forms - 0xFB00, 0xFb4f */ - /* Chess Symbols - 0x1FA00, 0x1FA0f */ - - { 0x1100, 0x115f }, { 0x231a, 0x231b }, { 0x2329, 0x232a }, - { 0x23e9, 0x23ec }, { 0x23f0, 0x23f0 }, { 0x23f3, 0x23f3 }, - { 0x25fd, 0x25fe }, { 0x2614, 0x2615 }, { 0x2648, 0x2653 }, - { 0x267f, 0x267f }, { 0x2693, 0x2693 }, { 0x26a1, 0x26a1 }, - { 0x26aa, 0x26ab }, { 0x26bd, 0x26be }, { 0x26c4, 0x26c5 }, - { 0x26ce, 0x26ce }, { 0x26d4, 0x26d4 }, { 0x26ea, 0x26ea }, - { 0x26f2, 0x26f3 }, { 0x26f5, 0x26f5 }, { 0x26fa, 0x26fa }, - { 0x26fd, 0x26fd }, { 0x2705, 0x2705 }, { 0x270a, 0x270b }, - { 0x2728, 0x2728 }, { 0x274c, 0x274c }, { 0x274e, 0x274e }, - { 0x2753, 0x2755 }, { 0x2757, 0x2757 }, { 0x2795, 0x2797 }, - { 0x27b0, 0x27b0 }, { 0x27bf, 0x27bf }, { 0x2b1b, 0x2b1c }, - { 0x2b50, 0x2b50 }, { 0x2b55, 0x2b55 }, { 0x2e80, 0x303e }, - { 0x3041, 0x3096 }, { 0x3099, 0x30ff }, { 0x3105, 0x312f }, - { 0x3131, 0x318e }, { 0x3190, 0x3247 }, { 0x3250, 0x4dbf }, - { 0x4e00, 0xa4c6 }, { 0xa960, 0xa97c }, { 0xac00, 0xd7a3 }, - { 0xf900, 0xfaff }, { 0xfe10, 0xfe1f }, { 0xfe30, 0xfe6b }, - { 0xff01, 0xff60 }, { 0xffe0, 0xffe6 }, - { 0x16fe0, 0x16fe4 }, { 0x16ff0, 0x16ff1 }, { 0x17000, 0x187f7 }, - { 0x18800, 0x18cd5 }, { 0x18d00, 0x18d08 }, { 0x1aff0, 0x1affc }, - { 0x1b000, 0x1b122 }, { 0x1b150, 0x1b152 }, { 0x1b164, 0x1b167 }, //{ 0x1b170, 0x1b2fb }, { 0x1d538, 0x1d550 }, - { 0x1f004, 0x1f004 }, { 0x1f0cf, 0x1f0cf }, /*{ 0x1f100, 0x1f10a },*/ - //{ 0x1f110, 0x1f12d }, { 0x1f130, 0x1f169 }, { 0x1f170, 0x1f1ac }, - { 0x1f18f, 0x1f199 }, - { 0x1f1e6, 0x1f1ff }, { 0x1f200, 0x1f202 }, { 0x1f210, 0x1f23b }, - { 0x1f240, 0x1f248 }, { 0x1f250, 0x1f251 }, { 0x1f260, 0x1f265 }, - { 0x1f300, 0x1f320 }, { 0x1f32d, 0x1f33e }, { 0x1f340, 0x1f37e }, - { 0x1f380, 0x1f393 }, { 0x1f3a0, 0x1f3ca }, { 0x1f3cf, 0x1f3d3 }, - { 0x1f3e0, 0x1f3f0 }, { 0x1f3f4, 0x1f3f4 }, { 0x1f3f8, 0x1f43e }, - { 0x1f440, 0x1f44e }, { 0x1f450, 0x1f4fc }, { 0x1f4ff, 0x1f53d }, - { 0x1f54b, 0x1f54e }, { 0x1f550, 0x1f567 }, { 0x1f57a, 0x1f57a }, - { 0x1f595, 0x1f596 }, { 0x1f5a4, 0x1f5a4 }, { 0x1f5fb, 0x1f606 }, - { 0x1f607, 0x1f64f }, { 0x1f680, 0x1f6c5 }, { 0x1f6cc, 0x1f6cc }, - { 0x1f6d0, 0x1f6d2 }, { 0x1f6d5, 0x1f6d7 }, { 0x1f6dd, 0x1f6df }, { 0x1f6eb, 0x1f6ec }, - { 0x1f6f4, 0x1f6fc }, { 0x1f7e0, 0x1f7eb }, { 0x1f7f0, 0x1f7f0 }, { 0x1f90c, 0x1f93a }, - { 0x1f93c, 0x1f945 }, { 0x1f947, 0x1f97f }, { 0x1f980, 0x1f9cc }, - { 0x1f9cd, 0x1f9ff }, { 0x1fa70, 0x1fa74 }, { 0x1fa78, 0x1fa7c }, { 0x1fa80, 0x1fa86 }, - { 0x1fa90, 0x1faac }, { 0x1fab0, 0x1faba }, { 0x1fac0, 0x1fac5 }, - { 0x1fad0, 0x1fad9 }, { 0x1fae0, 0x1fae7 }, { 0x1faf0, 0x1faf6 }, { 0x20000, 0x2fffd }, { 0x30000, 0x3fffd }, - //{ 0xe0100, 0xe01ef }, { 0xf0000, 0xffffd }, { 0x100000, 0x10fffd } - }; - - static int BiSearch (int rune, int [,] table, int max) - { - var min = 0; - - if (rune < table [0, 0] || rune > table [max, 1]) { - return 0; - } - while (max >= min) { - var mid = (min + max) / 2; - if (rune > table [mid, 1]) { - min = mid + 1; - } else if (rune < table [mid, 0]) { - max = mid - 1; - } else { - return 1; - } - } - - return 0; - } } \ No newline at end of file diff --git a/UnitTests/Text/RuneTests.cs b/UnitTests/Text/RuneTests.cs index 71ae2c9e5f..5eaf025f29 100644 --- a/UnitTests/Text/RuneTests.cs +++ b/UnitTests/Text/RuneTests.cs @@ -686,14 +686,61 @@ public void Equals_ToRuneList () Assert.False (d.SequenceEqual (b [1])); } + /// + /// Shows the difference between using Wcwidth.UnicodeCalculator and our + /// own port of wcwidth. Specifically, the UnicodeCalculator is more accurate to spec + /// where null has a width of 0, and our port says it's -1. + /// + /// + /// + [Theory] + [InlineData (0, 0)] + [InlineData (-1, 1)] + [InlineData (-1, 2)] + [InlineData (-1, 3)] + [InlineData (-1, 4)] + [InlineData (-1, 5)] + [InlineData (-1, 6)] + [InlineData (-1, 7)] + [InlineData (-1, 8)] + [InlineData (-1, 9)] + [InlineData (-1, 10)] + [InlineData (-1, 11)] + [InlineData (-1, 12)] + [InlineData (-1, 13)] + [InlineData (-1, 14)] + [InlineData (-1, 15)] + [InlineData (-1, 16)] + [InlineData (-1, 17)] + [InlineData (-1, 18)] + [InlineData (-1, 19)] + [InlineData (-1, 20)] + [InlineData (-1, 21)] + [InlineData (-1, 22)] + [InlineData (-1, 23)] + [InlineData (-1, 24)] + [InlineData (-1, 25)] + [InlineData (-1, 26)] + [InlineData (-1, 27)] + [InlineData (-1, 28)] + [InlineData (-1, 29)] + [InlineData (-1, 30)] + [InlineData (-1, 31)] + public void Rune_GetColumns_Non_Printable (int expectedColumns, int scalar) + { + var rune = new Rune (scalar); + Assert.Equal (expectedColumns, rune.GetColumns()); + Assert.Equal (0, rune.ToString().GetColumns()); + } + [Fact] public void Rune_GetColumns_Versus_String_GetColumns_With_Non_Printable_Characters () { int sumRuneWidth = 0; int sumConsoleWidth = 0; for (uint i = 0; i < 32; i++) { - sumRuneWidth += ((Rune)i).GetColumns (); - sumConsoleWidth += ((Rune)i).ToString ().GetColumns (); + sumRuneWidth += ((Rune)(i)).GetColumns (); + sumConsoleWidth += ((Rune)(i)).ToString ().GetColumns (); } Assert.Equal (-32, sumRuneWidth); From 6dc457fad809f7f5b5909592c9d317019b3665a4 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 9 Aug 2023 13:33:18 -0600 Subject: [PATCH 98/99] enhanced charmap Details dialog --- Terminal.Gui/Application.cs | 3 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 2 +- UICatalog/Scenarios/CharacterMap.cs | 175 +++++++++++++++++-- UnitTests/ConsoleDrivers/AddRuneTests.cs | 66 ++++++- UnitTests/Text/RuneTests.cs | 2 +- UnitTests/Text/UnicodeTests.cs | 3 +- 6 files changed, 226 insertions(+), 25 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index b124927820..b571894a83 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -517,7 +517,8 @@ public static void Run (Toplevel view, Func errorHandler = null /// public static void Refresh () { - //Driver.ClearContents(); + // TODO: Figure out how to remove this call to ClearContents. Refresh should just repaint damaged areas, not clear + Driver.ClearContents(); View last = null; foreach (var v in _toplevels.Reverse ()) { if (v.Visible) { diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 7a65de1b08..69c5a609d9 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -164,7 +164,7 @@ public void AddRune (Rune rune) Contents [Row, Col - 1].Attribute = CurrentAttribute; Contents [Row, Col - 1].IsDirty = true; - Col--; + //Col--; } else { Contents [Row, Col].Attribute = CurrentAttribute; Contents [Row, Col].IsDirty = true; diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index d4423397ee..7c220312d2 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -29,7 +29,7 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("ScrollView")] public class CharacterMap : Scenario { CharMap _charMap; - Label _errorLabel; + public Label _errorLabel; TableView _categoryList; // Don't create a Window, just return the top-level view @@ -52,7 +52,7 @@ public override void Setup () Application.Top.Add (jumpLabel); var jumpEdit = new TextField () { X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, Caption = "e.g. 01BE3" }; Application.Top.Add (jumpEdit); - _errorLabel = new Label ("") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] }; + _errorLabel = new Label ("err") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] }; Application.Top.Add (_errorLabel); jumpEdit.TextChanged += JumpEdit_TextChanged; @@ -122,6 +122,10 @@ public override void Setup () }) }); Application.Top.Add (menu); + + //_charMap.Hover += (s, a) => { + // _errorLabel.Text = $"U+{a.Item:x5} {(Rune)a.Item}"; + //}; } MenuItem CreateMenuShowWidth () @@ -209,7 +213,7 @@ private void JumpEdit_TextChanged (object sender, TextChangedEventArgs e) _errorLabel.Text = $"Beyond maximum codepoint"; return; } - _errorLabel.Text = $"U+{result:x4}"; + _errorLabel.Text = $"U+{result:x5}"; var table = (EnumerableTableSource)_categoryList.Table; _categoryList.SelectedRow = table.Data @@ -271,6 +275,8 @@ public int SelectedCodePoint { } } + public event EventHandler Hover; + /// /// Gets the coordinates of the Cursor based on the SelectedCodePoint in screen coordinates /// @@ -442,7 +448,7 @@ public override void OnDrawContentComplete (Rect contentArea) var firstColumnX = viewport.X + RowLabelWidth; for (int y = 1; y < Bounds.Height; y++) { // What row is this? - var row = (y - ContentOffset.Y) / _rowHeight; + var row = (y - ContentOffset.Y - 1) / _rowHeight; var val = (row) * 16; if (val > MaxCodePoint) { @@ -492,20 +498,20 @@ public override void OnDrawContentComplete (Rect contentArea) void Handle_MouseClick (object sender, MouseEventEventArgs args) { var me = args.MouseEvent; - if (me.Flags == MouseFlags.ReportMousePosition || (me.Flags != MouseFlags.Button1Clicked && - me.Flags != MouseFlags.Button1DoubleClicked)) { + if (me.Flags != MouseFlags.ReportMousePosition && me.Flags != MouseFlags.Button1Clicked && + me.Flags != MouseFlags.Button1DoubleClicked) { return; } if (me.Y == 0) { - me.Y = Cursor.Y ; + me.Y = Cursor.Y; } if (me.Y > 0) { } if (me.X < RowLabelWidth || me.X > RowLabelWidth + (16 * COLUMN_WIDTH) - 1) { - me.X = Cursor.X ; + me.X = Cursor.X; } var row = (me.Y - 1 - ContentOffset.Y) / _rowHeight; // -1 for header @@ -520,6 +526,10 @@ void Handle_MouseClick (object sender, MouseEventEventArgs args) return; } + if (me.Flags == MouseFlags.ReportMousePosition) { + Hover?.Invoke (this, new ListViewItemEventArgs (val, null)); + } + if (me.Flags == MouseFlags.Button1Clicked) { SelectedCodePoint = val; return; @@ -605,6 +615,7 @@ void ShowDetails () }; Application.Run (waitIndicator); + if (!string.IsNullOrEmpty (decResponse)) { string name = string.Empty; @@ -621,19 +632,151 @@ void ShowDetails () //&& property3Element.TryGetProperty ("nestedProperty", out JsonElement nestedPropertyElement)) { // Console.WriteLine (nestedPropertyElement.GetString ()); //} + decResponse = JsonSerializer.Serialize (document.RootElement, new + JsonSerializerOptions { + WriteIndented = true + }); } - var title = $"{ToCamelCase (name)} - {new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x4}"; - switch (MessageBox.Query (title, decResponse, "Copy _Glyph", "Copy Code _Point", "Cancel")) { - case 0: + var title = $"{ToCamelCase (name)} - {new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x5}"; + + var copyGlyph = new Button ("Copy _Glyph"); + var copyCP = new Button ("Copy Code _Point"); + var cancel = new Button ("Cancel"); + + var dlg = new Dialog (copyGlyph, copyCP, cancel) { + Title = title + }; + + copyGlyph.Clicked += (s, a) => { CopyGlyph (); - break; - case 1: + dlg.RequestStop (); + }; + copyCP.Clicked += (s, a) => { CopyCodePoint (); - break; - } + dlg.RequestStop (); + }; + cancel.Clicked += (s, a) => dlg.RequestStop (); + + var rune = (Rune)SelectedCodePoint; + var label = new Label () { + Text = "IsAscii: ", + X = 0, + Y = 0 + }; + dlg.Add (label); + + label = new Label () { + Text = $"{rune.IsAscii}", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = ", Bmp: ", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = $"{rune.IsBmp}", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = ", CombiningMark: ", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = $"{rune.IsCombiningMark ()}", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = ", SurrogatePair: ", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = $"{rune.IsSurrogatePair ()}", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = ", Plane: ", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = $"{rune.Plane}", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = "Columns: ", + X = 0, + Y = Pos.Bottom (label) + }; + dlg.Add (label); + + label = new Label () { + Text = $"{rune.GetColumns ()}", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = ", Utf16SequenceLength: ", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + + label = new Label () { + Text = $"{rune.Utf16SequenceLength}", + X = Pos.Right (label), + Y = Pos.Top (label) + }; + dlg.Add (label); + label = new Label () { + Text = $"Code Point Information from {UcdApiClient.BaseUrl}codepoint/dec/{SelectedCodePoint}:", + X = 0, + Y = Pos.Bottom (label) + }; + dlg.Add (label); + + var json = new TextView () { + X = 0, + Y = Pos.Bottom (label), + Width = Dim.Fill (), + Height = Dim.Fill (2), + ReadOnly = true, + Text = decResponse + }; + dlg.Add (json); + + Application.Run (dlg); + } else { - MessageBox.ErrorQuery ("Code Point API", $"{UcdApiClient.BaseUrl} did not return a result.", "Ok"); + MessageBox.ErrorQuery ("Code Point API", $"{UcdApiClient.BaseUrl}codepoint/dec/{SelectedCodePoint} did not return a result for\r\n {new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x5}.", "Ok"); } // BUGBUG: This is a workaround for some weird ScrollView related mouse grab bug Application.GrabMouse (this); diff --git a/UnitTests/ConsoleDrivers/AddRuneTests.cs b/UnitTests/ConsoleDrivers/AddRuneTests.cs index b4cc5e5ad3..33a4036b4f 100644 --- a/UnitTests/ConsoleDrivers/AddRuneTests.cs +++ b/UnitTests/ConsoleDrivers/AddRuneTests.cs @@ -1,4 +1,5 @@ -using System.Buffers; +using Microsoft.VisualStudio.TestPlatform.Utilities; +using System.Buffers; using System.Text; using Xunit; using Xunit.Abstractions; @@ -24,8 +25,8 @@ public void AddRune () driver.Init (() => { }); driver.AddRune (new Rune ('a')); - Assert.Equal ((Rune)'a', driver.Contents[0,0].Runes[0]); - + Assert.Equal ((Rune)'a', driver.Contents [0, 0].Runes [0]); + driver.End (); Application.Shutdown (); } @@ -39,7 +40,7 @@ public void AddRune_InvalidLocation_DoesNothing () driver.Move (driver.Cols, driver.Rows); driver.AddRune ('a'); - + for (var col = 0; col < driver.Cols; col++) { for (var row = 0; row < driver.Rows; row++) { Assert.Equal ((Rune)' ', driver.Contents [row, col].Runes [0]); @@ -103,7 +104,7 @@ public void AddRune_MovesToNextColumn_Wide () Assert.Equal (OperationStatus.Done, operationStatus); Assert.Equal (charsConsumed, rune.Utf16SequenceLength); Assert.Equal (2, rune.GetColumns ()); - + driver.AddRune (rune); Assert.Equal (rune, driver.Contents [0, 0].Runes [0]); Assert.Equal (0, driver.Row); @@ -137,4 +138,59 @@ public void AddRune_MovesToNextColumn_Wide () driver.End (); Application.Shutdown (); } + + + [Fact] + public void AddRune_Accented_Letter_With_Three_Combining_Unicode_Chars () + { + var driver = new FakeDriver (); + Application.Init (driver); + driver.Init (() => { }); + + var expected = new Rune ('ắ'); + + var text = "\u1eaf"; + driver.AddStr (text); + Assert.Equal (expected, driver.Contents [0, 0].Runes [0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes [0]); + + driver.ClearContents (); + driver.Move (0, 0); + + text = "\u0103\u0301"; + driver.AddStr (text); + Assert.Equal (expected, driver.Contents [0, 0].Runes [0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes [0]); + + driver.ClearContents (); + driver.Move (0, 0); + + text = "\u0061\u0306\u0301"; + driver.AddStr (text); + Assert.Equal (expected, driver.Contents [0, 0].Runes [0]); + Assert.Equal ((Rune)' ', driver.Contents [0, 1].Runes [0]); + + // var s = "a\u0301\u0300\u0306"; + + + // TestHelpers.AssertDriverContentsWithFrameAre (@" + //ắ", output); + + // tf.Text = "\u1eaf"; + // Application.Refresh (); + // TestHelpers.AssertDriverContentsWithFrameAre (@" + //ắ", output); + + // tf.Text = "\u0103\u0301"; + // Application.Refresh (); + // TestHelpers.AssertDriverContentsWithFrameAre (@" + //ắ", output); + + // tf.Text = "\u0061\u0306\u0301"; + // Application.Refresh (); + // TestHelpers.AssertDriverContentsWithFrameAre (@" + //ắ", output); + driver.End (); + Application.Shutdown (); + } } diff --git a/UnitTests/Text/RuneTests.cs b/UnitTests/Text/RuneTests.cs index 5eaf025f29..699a3ba225 100644 --- a/UnitTests/Text/RuneTests.cs +++ b/UnitTests/Text/RuneTests.cs @@ -743,7 +743,7 @@ public void Rune_GetColumns_Versus_String_GetColumns_With_Non_Printable_Characte sumConsoleWidth += ((Rune)(i)).ToString ().GetColumns (); } - Assert.Equal (-32, sumRuneWidth); + Assert.Equal (-31, sumRuneWidth); Assert.Equal (0, sumConsoleWidth); } diff --git a/UnitTests/Text/UnicodeTests.cs b/UnitTests/Text/UnicodeTests.cs index 2a69b93861..12f8c9f4e6 100644 --- a/UnitTests/Text/UnicodeTests.cs +++ b/UnitTests/Text/UnicodeTests.cs @@ -1,4 +1,5 @@ -using Xunit; +using Microsoft.VisualStudio.TestPlatform.Utilities; +using Xunit; using Xunit.Abstractions; // Alias Console to MockConsole so we don't accidentally use Console From 24450e9c8691ab6e605ce39325f966272564c1e3 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 9 Aug 2023 13:56:07 -0600 Subject: [PATCH 99/99] Final attempt at fixing curses --- .../ConsoleDrivers/CursesDriver/CursesDriver.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 24938cea09..54fda896f1 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -215,15 +215,14 @@ public override void UpdateScreen () var rune = Contents [row, col].Runes [0]; if (rune.IsBmp) { // BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell. - Curses.mvaddch (row, col, rune.Value); - if (rune.GetColumns() == 2 && col + 1 < Cols) { - col++; - // BUGBUG: - //Curses.mvaddwstr (row, col, ' '); + if (rune.GetColumns () < 2) { + Curses.mvaddch (row, col, rune.Value); + } else /*if (col + 1 < Cols)*/ { + Curses.mvaddwstr (row, col, rune.ToString ()); } } else { - Curses.mvaddch (row, col, '#'); + Curses.mvaddwstr (row, col, rune.ToString()); if (rune.GetColumns () > 1 && col + 1 < Cols) { // TODO: This is a hack to deal with non-BMP and wide characters. //col++;