2007-04-18 Jeffrey Stedfast <fejj@novell.com>
[mono.git] / mcs / class / corlib / System / TermInfoDriver.cs
index 1be77b5c40a932d4b2ef7c2134a027de40c9d7b1..583722b3cb45248a823fbaa3d7518cb5bbaf5697 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 #if NET_2_0
+//#define DEBUG
 using System.Collections;
 using System.IO;
 using System.Text;
 using System.Runtime.InteropServices;
 namespace System {
        class TermInfoDriver : IConsoleDriver {
+               /* Do not rename this field, its looked up from the runtime */
+               static bool need_window_dimensions = true;
+               
                static string [] locations = { "/etc/terminfo", "/usr/share/terminfo", "/usr/lib/terminfo" };
 
                TermInfoReader reader;
@@ -66,7 +70,6 @@ namespace System {
                bool controlCAsInput;
                bool inited;
                bool initKeys;
-               bool noEcho;
                string origPair;
                string origColors;
                string cursorAddress;
@@ -78,6 +81,10 @@ namespace System {
                Hashtable keymap;
                ByteMatcher rootmap;
                bool home_1_1; // if true, we have to add 1 to x and y when using cursorAddress
+               int rl_startx = -1, rl_starty = -1;
+#if DEBUG
+               StreamWriter logger;
+#endif
 
                static string SearchTerminfo (string term)
                {
@@ -121,6 +128,10 @@ namespace System {
 
                public TermInfoDriver (string term)
                {
+#if DEBUG
+                       //File.Delete ("console.log");
+                       logger = new StreamWriter (File.OpenWrite ("console.log"));
+#endif
                        this.term = term;
 
                        if (term == "xterm") {
@@ -140,7 +151,7 @@ namespace System {
                public bool Initialized {
                        get { return inited; }
                }
-               
+
                public void Init ()
                {
                        if (inited)
@@ -151,7 +162,9 @@ namespace System {
                                throw new IOException ("Not a tty.");
 
                        inited = true;
+
                        ConsoleDriver.SetEcho (false);
+
                        string endString = null;
                        keypadXmit = reader.Get (TermInfoStrings.KeypadXmit);
                        keypadLocal = reader.Get (TermInfoStrings.KeypadLocal);
@@ -185,7 +198,7 @@ namespace System {
                                csrVisible = reader.Get (TermInfoStrings.CursorVisible);
 
                        csrInvisible = reader.Get (TermInfoStrings.CursorInvisible);
-                       if (term == "cygwin" || term == "linux" || term.StartsWith ("xterm") ||
+                       if (term == "cygwin" || term == "linux" || (term != null && term.StartsWith ("xterm")) ||
                                term == "rxvt" || term == "dtterm") {
                                titleFormat = "\x1b]0;{0}\x7"; // icon + window title
                        } else if (term == "iris-ansi") {
@@ -202,6 +215,10 @@ namespace System {
                        }
 
                        GetCursorPosition ();
+#if DEBUG
+                       logger.WriteLine ("noGetPosition: {0} left: {1} top: {2}", noGetPosition, cursorLeft, cursorTop);
+                       logger.Flush ();
+#endif
                        if (noGetPosition) {
                                WriteConsole (clear);
                                cursorLeft = 0;
@@ -252,406 +269,279 @@ namespace System {
                        return 0;
                }
 
-               public bool NotifyWrite (ConsoleKeyInfo key)
+               void IncrementX ()
                {
-                       if (key.Key >= ConsoleKey.A && key.Key <= ConsoleKey.Z) {
-                               cursorLeft++;
-                               if (cursorLeft >= WindowWidth) {
-                                       cursorTop++;
-                                       cursorLeft = 0;
-                                       if (cursorTop >= WindowHeight) {
-                                               cursorTop--;
-                                       }
+                       cursorLeft++;
+                       if (cursorLeft >= WindowWidth) {
+                               cursorTop++;
+                               cursorLeft = 0;
+                               if (cursorTop >= WindowHeight) {
+                                       // Writing beyond the initial screen
+                                       if (rl_starty != -1) rl_starty--;
+                                       cursorTop--;
                                }
-                               return true;
-                       }       
+                       }
+               }
+
+               // Should never get called unless inited
+               public void WriteSpecialKey (ConsoleKeyInfo key)
+               {
                        switch (key.Key) {
                        case ConsoleKey.Backspace:
                                if (cursorLeft > 0) {
+                                       if (cursorLeft <= rl_startx && cursorTop == rl_starty)
+                                               break;
                                        cursorLeft--;
                                        SetCursorPosition (cursorLeft, cursorTop);
                                        WriteConsole (" ");
                                        SetCursorPosition (cursorLeft, cursorTop);
                                }
-                               return false;
+#if DEBUG
+                               logger.WriteLine ("BS left: {0} top: {1}", cursorLeft, cursorTop);
+                               logger.Flush ();
+#endif
+                               break;
                        case ConsoleKey.Tab:
+                               int n = 8 - (cursorLeft % 8);
+                               for (int i = 0; i < n; i++)
+                                       IncrementX ();
                                break;
                        case ConsoleKey.Clear:
                                break;
                        case ConsoleKey.Enter:
                                break;
-                       case ConsoleKey.Pause:
-                               break;
-                       case ConsoleKey.Escape:
-                               break;
-                       case ConsoleKey.Spacebar:
-                               break;
-                       case ConsoleKey.PageUp:
-                               break;
-                       case ConsoleKey.PageDown:
-                               break;
-                       case ConsoleKey.End:
-                               break;
-                       case ConsoleKey.Home:
-                               break;
-                       case ConsoleKey.LeftArrow:
-                               break;
-                       case ConsoleKey.UpArrow:
-                               break;
-                       case ConsoleKey.RightArrow:
-                               break;
-                       case ConsoleKey.DownArrow:
-                               break;
-                       case ConsoleKey.Select:
-                               break;
-                       case ConsoleKey.Print:
-                               break;
-                       case ConsoleKey.Execute:
-                               break;
-                       case ConsoleKey.PrintScreen:
-                               break;
-                       case ConsoleKey.Insert:
-                               break;
-                       case ConsoleKey.Delete:
-                               break;
-                       case ConsoleKey.Help:
-                               break;
-                       case ConsoleKey.D0:
-                               break;
-                       case ConsoleKey.D1:
-                               break;
-                       case ConsoleKey.D2:
-                               break;
-                       case ConsoleKey.D3:
-                               break;
-                       case ConsoleKey.D4:
-                               break;
-                       case ConsoleKey.D5:
-                               break;
-                       case ConsoleKey.D6:
-                               break;
-                       case ConsoleKey.D7:
-                               break;
-                       case ConsoleKey.D8:
-                               break;
-                       case ConsoleKey.D9:
-                               break;
-                       case ConsoleKey.LeftWindows:
-                               break;
-                       case ConsoleKey.RightWindows:
-                               break;
-                       case ConsoleKey.Applications:
-                               break;
-                       case ConsoleKey.Sleep:
-                               break;
-                       case ConsoleKey.NumPad0:
-                               break;
-                       case ConsoleKey.NumPad1:
-                               break;
-                       case ConsoleKey.NumPad2:
-                               break;
-                       case ConsoleKey.NumPad3:
-                               break;
-                       case ConsoleKey.NumPad4:
-                               break;
-                       case ConsoleKey.NumPad5:
-                               break;
-                       case ConsoleKey.NumPad6:
-                               break;
-                       case ConsoleKey.NumPad7:
-                               break;
-                       case ConsoleKey.NumPad8:
-                               break;
-                       case ConsoleKey.NumPad9:
-                               break;
-                       case ConsoleKey.Multiply:
-                               break;
-                       case ConsoleKey.Add:
-                               break;
-                       case ConsoleKey.Separator:
-                               break;
-                       case ConsoleKey.Subtract:
-                               break;
-                       case ConsoleKey.Decimal:
-                               break;
-                       case ConsoleKey.Divide:
-                               break;
-                       case ConsoleKey.F1:
-                               break;
-                       case ConsoleKey.F2:
-                               break;
-                       case ConsoleKey.F3:
-                               break;
-                       case ConsoleKey.F4:
-                               break;
-                       case ConsoleKey.F5:
-                               break;
-                       case ConsoleKey.F6:
-                               break;
-                       case ConsoleKey.F7:
-                               break;
-                       case ConsoleKey.F8:
-                               break;
-                       case ConsoleKey.F9:
-                               break;
-                       case ConsoleKey.F10:
-                               break;
-                       case ConsoleKey.F11:
-                               break;
-                       case ConsoleKey.F12:
-                               break;
-                       case ConsoleKey.F13:
-                               break;
-                       case ConsoleKey.F14:
-                               break;
-                       case ConsoleKey.F15:
-                               break;
-                       case ConsoleKey.F16:
-                               break;
-                       case ConsoleKey.F17:
-                               break;
-                       case ConsoleKey.F18:
-                               break;
-                       case ConsoleKey.F19:
-                               break;
-                       case ConsoleKey.F20:
-                               break;
-                       case ConsoleKey.F21:
-                               break;
-                       case ConsoleKey.F22:
-                               break;
-                       case ConsoleKey.F23:
-                               break;
-                       case ConsoleKey.F24:
-                               break;
-                       case ConsoleKey.BrowserBack:
-                               break;
-                       case ConsoleKey.BrowserForward:
-                               break;
-                       case ConsoleKey.BrowserRefresh:
-                               break;
-                       case ConsoleKey.BrowserStop:
-                               break;
-                       case ConsoleKey.BrowserSearch:
-                               break;
-                       case ConsoleKey.BrowserFavorites:
-                               break;
-                       case ConsoleKey.BrowserHome:
-                               break;
-                       case ConsoleKey.VolumeMute:
-                               break;
-                       case ConsoleKey.VolumeDown:
-                               break;
-                       case ConsoleKey.VolumeUp:
-                               break;
-                       case ConsoleKey.MediaNext:
-                               break;
-                       case ConsoleKey.MediaPrevious:
-                               break;
-                       case ConsoleKey.MediaStop:
-                               break;
-                       case ConsoleKey.MediaPlay:
-                               break;
-                       case ConsoleKey.LaunchMail:
-                               break;
-                       case ConsoleKey.LaunchMediaSelect:
-                               break;
-                       case ConsoleKey.LaunchApp1:
-                               break;
-                       case ConsoleKey.LaunchApp2:
-                               break;
-                       case ConsoleKey.Oem1:
-                               break;
-                       case ConsoleKey.OemPlus:
-                               break;
-                       case ConsoleKey.OemComma:
-                               break;
-                       case ConsoleKey.OemMinus:
-                               break;
-                       case ConsoleKey.OemPeriod:
-                               break;
-                       case ConsoleKey.Oem2:
-                               break;
-                       case ConsoleKey.Oem3:
-                               break;
-                       case ConsoleKey.Oem4:
-                               break;
-                       case ConsoleKey.Oem5:
-                               break;
-                       case ConsoleKey.Oem6:
-                               break;
-                       case ConsoleKey.Oem7:
-                               break;
-                       case ConsoleKey.Oem8:
-                               break;
-                       case ConsoleKey.Oem102:
-                               break;
-                       case ConsoleKey.Process:
-                               break;
-                       case ConsoleKey.Packet:
-                               break;
-                       case ConsoleKey.Attention:
-                               break;
-                       case ConsoleKey.CrSel:
-                               break;
-                       case ConsoleKey.ExSel:
-                               break;
-                       case ConsoleKey.EraseEndOfFile:
-                               break;
-                       case ConsoleKey.Play:
-                               break;
-                       case ConsoleKey.Zoom:
-                               break;
-                       case ConsoleKey.NoName:
-                               break;
-                       case ConsoleKey.Pa1:
-                               break;
-                       case ConsoleKey.OemClear:
-                               break;
                        default:
-                               return true;
+                               break;
                        }
-                       return true;
+#if DEBUG
+                       logger.WriteLine ("left: {0} top: {1}", cursorLeft, cursorTop);
+                       logger.Flush ();
+#endif
                }
 
-               public bool NotifyWrite (char val)
+               // Should never get called unless inited
+               public void WriteSpecialKey (char c)
                {
-                       if (val == verase) {
-                               string str = reader.Get (TermInfoStrings.EraseChars);
-                               if (str != null) {
-                                       Console.Error.WriteLine ("{0}", TermInfoReader.Escape (str));
-                               } else {
-                                       Console.Error.WriteLine ("FUCK");
-                               }
+                       WriteSpecialKey (CreateKeyInfoFromInt (c));
+               }
+
+               public bool IsSpecialKey (ConsoleKeyInfo key)
+               {
+                       if (!inited)
                                return false;
-                       } else {
-                               //TODO: compute cursor position
+
+                       switch (key.Key) {
+                       case ConsoleKey.Backspace:
                                return true;
+                       case ConsoleKey.Tab:
+                               return true;
+                       case ConsoleKey.Clear:
+                               return true;
+                       case ConsoleKey.Enter:
+                               cursorLeft = 0;
+                               cursorTop++;
+                               if (cursorTop >= WindowHeight) {
+                                       cursorTop--;
+                                       //TODO: scroll up
+                               }
+                               return false;
+                       default:
+                               // CStreamWriter will handle writing this key
+                               IncrementX ();
+                               return false;
                        }
                }
 
+               public bool IsSpecialKey (char c)
+               {
+                       return IsSpecialKey (CreateKeyInfoFromInt (c));
+               }
+
                public ConsoleColor BackgroundColor {
-                       get { return bgcolor; }
+                       get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
+                               return bgcolor;
+                       }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                bgcolor = value;
                                WriteConsole (String.Format (setabcolor, TranslateColor (value)));
                        }
                }
 
                public ConsoleColor ForegroundColor {
-                       get { return fgcolor; }
+                       get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
+                               return fgcolor;
+                       }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                fgcolor = value;
                                WriteConsole (String.Format (setafcolor, TranslateColor (value)));
                        }
                }
 
-               // Only used once.
                void GetCursorPosition ()
                {
                        int row = 0, col = 0;
-                       bool prevEcho = Echo;
-                       Echo = false;
-                       try {
-                               WriteConsole ("\x1b[6n");
-                               if (ConsoleDriver.InternalKeyAvailable (1000) <= 0) {
-                                       noGetPosition = true;
-                                       return;
-                               }
 
-                               int b = stdin.ReadByte ();
-                               while (b != '\x1b') {
-                                       AddToBuffer (b);
-                                       if (ConsoleDriver.InternalKeyAvailable (100) <= 0)
-                                               return;
-                                       b = stdin.ReadByte ();
-                               }
+                       WriteConsole ("\x1b[6n");
+                       if (ConsoleDriver.InternalKeyAvailable (1000) <= 0) {
+                               noGetPosition = true;
+                               return;
+                       }
 
-                               b = stdin.ReadByte ();
-                               if (b != '[') {
-                                       AddToBuffer ('\x1b');
-                                       AddToBuffer (b);
+                       int b = stdin.ReadByte ();
+                       while (b != '\x1b') {
+                               AddToBuffer (b);
+                               if (ConsoleDriver.InternalKeyAvailable (100) <= 0)
                                        return;
-                               }
+                               b = stdin.ReadByte ();
+                       }
+
+                       b = stdin.ReadByte ();
+                       if (b != '[') {
+                               AddToBuffer ('\x1b');
+                               AddToBuffer (b);
+                               return;
+                       }
 
+                       b = stdin.ReadByte ();
+                       if (b != ';') {
+                               row = b - '0';
                                b = stdin.ReadByte ();
-                               if (b != ';') {
-                                       row = b - '0';
+                               while ((b >= '0') && (b <= '9')) {
+                                       row = row * 10 + b - '0';
                                        b = stdin.ReadByte ();
-                                       while ((b >= '0') && (b <= '9')) {
-                                               row = row * 10 + b - '0';
-                                               b = stdin.ReadByte ();
-                                       }
-                                       // Row/col is 0 based
-                                       row --;
                                }
+                               // Row/col is 0 based
+                               row --;
+                       }
 
+                       b = stdin.ReadByte ();
+                       if (b != 'R') {
+                               col = b - '0';
                                b = stdin.ReadByte ();
-                               if (b != 'R') {
-                                       col = b - '0';
+                               while ((b >= '0') && (b <= '9')) {
+                                       col = col * 10 + b - '0';
                                        b = stdin.ReadByte ();
-                                       while ((b >= '0') && (b <= '9')) {
-                                               col = col * 10 + b - '0';
-                                               b = stdin.ReadByte ();
-                                       }
-                                       // Row/col is 0 based
-                                       col --;
                                }
-                       } finally {
-                               Echo = prevEcho;
+                               // Row/col is 0 based
+                               col --;
                        }
 
+#if DEBUG
+                       logger.WriteLine ("GetCursorPosition: {0}, {1}", col, row);
+                       logger.Flush ();
+#endif
+
                        cursorLeft = col;
                        cursorTop = row;
                }
 
                public int BufferHeight {
                        get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                return bufferHeight;
                        }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                throw new NotSupportedException ();
                        }
                }
 
                public int BufferWidth {
                        get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                return bufferWidth;
                        }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                throw new NotSupportedException ();
                        }
                }
 
                public bool CapsLock {
-                       get { throw new NotSupportedException (); }
+                       get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
+                               throw new NotSupportedException ();
+                       }
                }
 
                public int CursorLeft {
                        get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                return cursorLeft;
                        }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                SetCursorPosition (value, CursorTop);
-                               cursorLeft = value;
                        }
                }
 
                public int CursorTop {
                        get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                return cursorTop;
                        }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                SetCursorPosition (CursorLeft, value);
-                               cursorTop = value;
                        }
                }
 
                public bool CursorVisible {
                        get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                return cursorVisible;
                        }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                cursorVisible = value;
                                WriteConsole ((value ? csrVisible : csrInvisible));
                        }
@@ -660,22 +550,26 @@ namespace System {
                // we have CursorNormal vs. CursorVisible...
                [MonoTODO]
                public int CursorSize {
-                       get { return 1; }
-                       set {}
-               }
-
-               public bool Echo {
-                       get { return !noEcho; }
+                       get {
+                               if (!inited) {
+                                       Init ();
+                               }
+                               return 1;
+                       }
                        set {
-                               if (value != noEcho)
-                                       return;
-
-                               noEcho = !value;
+                               if (!inited) {
+                                       Init ();
+                               }
                        }
+
                }
 
                public bool KeyAvailable {
                        get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                return (writepos > readpos || ConsoleDriver.InternalKeyAvailable (0) > 0);
                        }
                }
@@ -690,21 +584,45 @@ namespace System {
                }
 
                public bool NumberLock {
-                       get { throw new NotSupportedException (); }
+                       get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
+                               throw new NotSupportedException ();
+                       }
                }
 
                public string Title {
-                       get { return title; }
+                       get {
+                               if (!inited) {
+                                       Init ();
+                               }
+                               return title;
+                       }
                        
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                title = value;
                                WriteConsole (String.Format (titleFormat, value));
                        }
                }
 
                public bool TreatControlCAsInput {
-                       get { return controlCAsInput; }
+                       get {
+                               if (!inited) {
+                                       Init ();
+                               }
+                               return controlCAsInput;
+                       }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                if (controlCAsInput == value)
                                        return;
 
@@ -713,7 +631,6 @@ namespace System {
                        }
                }
 
-               // TODO: use this at the beginning and on every SIGWINCH
                void GetWindowDimensions ()
                {
                        /* Try the ioctl first */
@@ -739,55 +656,98 @@ namespace System {
 
                        bufferHeight = windowHeight;
                        bufferWidth = windowWidth;
+                       need_window_dimensions = false;
                }
 
                public int WindowHeight {
                        get {
-                               GetWindowDimensions ();
+                               if (!inited) {
+                                       Init ();
+                               }
+
+                               if (need_window_dimensions)
+                                       GetWindowDimensions ();
                                return windowHeight;
                        }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                throw new NotSupportedException ();
                        }
                }
 
                public int WindowLeft {
                        get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                //GetWindowDimensions ();
                                return 0;
                        }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                throw new NotSupportedException ();
                        }
                }
 
                public int WindowTop {
                        get {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                //GetWindowDimensions ();
                                return 0;
                        }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                throw new NotSupportedException ();
                        }
                }
 
                public int WindowWidth {
                        get {
-                               GetWindowDimensions ();
+                               if (!inited) {
+                                       Init ();
+                               }
+
+                               if (need_window_dimensions)
+                                       GetWindowDimensions ();
                                return windowWidth;
                        }
                        set {
+                               if (!inited) {
+                                       Init ();
+                               }
+
                                throw new NotSupportedException ();
                        }
                }
 
                public void Clear ()
                {
+                       if (!inited) {
+                               Init ();
+                       }
+
                        WriteConsole (clear);
                }
 
                public void Beep (int frequency, int duration)
                {
+                       if (!inited) {
+                               Init ();
+                       }
+
                        WriteConsole (bell);
                }
 
@@ -795,17 +755,33 @@ namespace System {
                                        int targetLeft, int targetTop, Char sourceChar,
                                        ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
                {
+                       if (!inited) {
+                               Init ();
+                       }
+
                        throw new NotImplementedException ();
                }
 
                void AddToBuffer (int b)
                {
-                       if (buffer == null)
+                       if (buffer == null) {
                                buffer = new byte [1024];
+                       } else if (writepos >= buffer.Length) {
+                               byte [] newbuf = new byte [buffer.Length * 2];
+                               Buffer.BlockCopy (buffer, 0, newbuf, 0, buffer.Length);
+                               buffer = newbuf;
+                       }
 
                        buffer [writepos++] = (byte) b;
                }
 
+               void AdjustBuffer ()
+               {
+                       if (readpos >= writepos) {
+                               readpos = writepos = 0;
+                       }
+               }
+
                ConsoleKeyInfo CreateKeyInfoFromInt (int n)
                {
                        char c = (char) n;
@@ -844,6 +820,7 @@ namespace System {
                        int next = buffer [readpos];
                        if (!cooked || !rootmap.StartsWith (next)) {
                                readpos++;
+                               AdjustBuffer ();
                                return CreateKeyInfoFromInt (next);
                        }
 
@@ -857,86 +834,219 @@ namespace System {
                                key = (ConsoleKeyInfo) keymap [str];
                        } else {
                                readpos++;
+                               AdjustBuffer ();
                                return CreateKeyInfoFromInt (next);
                        }
 
                        readpos += used;
-                       if (readpos >= writepos)
-                               readpos = writepos = 0;
-
+                       AdjustBuffer ();
                        return key;
                }
 
-               ConsoleKeyInfo ReadKeyInternal ()
+               ConsoleKeyInfo ReadKeyInternal (out bool fresh)
                {
+                       if (!inited)
+                               Init ();
+
                        InitKeys ();
-                       object o = null;
-                       while (o == null) {
-                               o = GetKeyFromBuffer (true);
-                               if (o == null) {
+
+                       object o;
+
+                       if ((o = GetKeyFromBuffer (true)) == null) {
+                               do {
                                        if (ConsoleDriver.InternalKeyAvailable (150) > 0) {
                                                do {
                                                        AddToBuffer (stdin.ReadByte ());
                                                } while (ConsoleDriver.InternalKeyAvailable (0) > 0);
                                        } else {
-                                               o = GetKeyFromBuffer (false);
-                                               if (o == null)
-                                                       AddToBuffer (stdin.ReadByte ());
+                                               if ((o = GetKeyFromBuffer (false)) != null)
+                                                       break;
+                                               
+                                               AddToBuffer (stdin.ReadByte ());
                                        }
-                               }
+                                       
+                                       o = GetKeyFromBuffer (true);
+                               } while (o == null);
+
+                               // freshly read character
+                               fresh = true;
+                       } else {
+                               // this char was pre-buffered (e.g. not fresh)
+                               fresh = false;
                        }
 
                        return (ConsoleKeyInfo) o;
                }
 
+               public int Read ([In, Out] char [] dest, int index, int count)
+               {
+                       CStreamWriter writer = Console.stdout as CStreamWriter;
+                       StringBuilder sbuf;
+                       ConsoleKeyInfo key;
+                       int BoL = 0;  // Beginning-of-Line marker (can't backspace beyond this)
+                       bool fresh;
+                       object o;
+                       char c;
+
+                       sbuf = new StringBuilder ();
+
+                       // consume buffered keys first (do not echo, these have already been echo'd)
+                       while (true) {
+                               if ((o = GetKeyFromBuffer (true)) == null)
+                                       break;
+
+                               key = (ConsoleKeyInfo) o;
+                               c = key.KeyChar;
+
+                               if (key.Key != ConsoleKey.Backspace) {
+                                       if (key.Key == ConsoleKey.Enter)
+                                               BoL = sbuf.Length;
+
+                                       sbuf.Append (c);
+                               } else if (sbuf.Length > BoL) {
+                                       sbuf.Length--;
+                               }
+                       }
+
+                       // continue reading until Enter is hit
+                       rl_startx = cursorLeft;
+                       rl_starty = cursorTop;
+
+                       do {
+                               key = ReadKeyInternal (out fresh);
+                               c = key.KeyChar;
+
+                               if (key.Key != ConsoleKey.Backspace) {
+                                       if (key.Key == ConsoleKey.Enter)
+                                               BoL = sbuf.Length;
+
+                                       sbuf.Append (c);
+                               } else if (sbuf.Length > BoL) {
+                                       sbuf.Length--;
+                               } else {
+                                       continue;
+                               }
+
+                               if (fresh) {
+                                       // echo fresh keys back to the console
+                                       if (writer != null)
+                                               writer.WriteKey (key);
+                                       else
+                                               Console.stdout.Write (c);
+                               }
+                       } while (key.Key != ConsoleKey.Enter);
+
+                       rl_startx = -1;
+                       rl_starty = -1;
+
+                       // copy up to count chars into dest
+                       int nread = 0;
+                       while (count > 0 && nread < sbuf.Length) {
+                               dest[index + nread] = sbuf[nread];
+                               nread++;
+                               count--;
+                       }
+
+                       // put the rest back into our key buffer
+                       for (int i = nread; i < sbuf.Length; i++)
+                               AddToBuffer (sbuf[i]);
+
+                       return nread;
+               }
+
                public ConsoleKeyInfo ReadKey (bool intercept)
                {
-                       bool prevEcho = Echo;
-                       if (prevEcho == intercept)
-                               Echo  = !intercept;
+                       bool fresh;
 
-                       ConsoleKeyInfo key = ReadKeyInternal ();
-                       if (prevEcho == intercept)
-                               Echo = prevEcho;
+                       ConsoleKeyInfo key = ReadKeyInternal (out fresh);
+
+                       if (!intercept && fresh) {
+                               // echo the fresh key back to the console
+                               CStreamWriter writer = Console.stdout as CStreamWriter;
+
+                               if (writer != null)
+                                       writer.WriteKey (key);
+                               else
+                                       Console.stdout.Write (key.KeyChar);
+                       }
 
                        return key;
                }
 
                public string ReadLine ()
                {
-                       bool prevEcho = Echo;
-                       if (prevEcho == false)
-                               Echo  = true;
+                       if (!inited)
+                               Init ();
+
+                       // Hack to make Iron Python work (since it goes behind our backs
+                       // when writing to the console thus preventing us from keeping
+                       // cursor state normally).
+                       GetCursorPosition ();
+
+                       CStreamWriter writer = Console.stdout as CStreamWriter;
                        StringBuilder builder = new StringBuilder ();
-                       bool exit = false;
-                       CStreamWriter writer = (CStreamWriter) Console.stdout;
+                       ConsoleKeyInfo key;
+                       bool fresh;
+                       char c;
+
+                       rl_startx = cursorLeft;
+                       rl_starty = cursorTop;
+
                        do {
-                               ConsoleKeyInfo key = ReadKeyInternal ();
-                               char c = key.KeyChar;
-                               exit = (c == '\n');
-                               if (!exit)
-                                       builder.Append (c);
-                               writer.WriteKey (key);
-                       } while (!exit);
-                       if (prevEcho == false)
-                               Echo = prevEcho;
+                               key = ReadKeyInternal (out fresh);
+                               c = key.KeyChar;
+                               if (key.Key != ConsoleKey.Enter) {
+                                       if (key.Key != ConsoleKey.Backspace)
+                                               builder.Append (c);
+                                       else if (builder.Length > 0)
+                                               builder.Length--;
+                                       else {
+                                               // skips over echoing the key to the console
+                                               continue;
+                                       }
+                               }
+
+                               if (fresh) {
+                                       // echo fresh keys back to the console
+                                       if (writer != null)
+                                               writer.WriteKey (key);
+                                       else
+                                               Console.stdout.Write (c);
+                               }
+                       } while (key.Key != ConsoleKey.Enter);
+
+                       rl_startx = -1;
+                       rl_starty = -1;
+
                        return builder.ToString ();
                }
 
                public void ResetColor ()
                {
+                       if (!inited) {
+                               Init ();
+                       }
+
                        string str = (origPair != null) ? origPair : origColors;
                        WriteConsole (str);
                }
 
                public void SetBufferSize (int width, int height)
                {
+                       if (!inited) {
+                               Init ();
+                       }
+
                        throw new NotImplementedException (String.Empty);
                }
 
                public void SetCursorPosition (int left, int top)
                {
-                       if (bufferWidth == 0)
+                       if (!inited) {
+                               Init ();
+                       }
+
+                       if (bufferWidth == 0 && need_window_dimensions)
                                GetWindowDimensions ();
 
                        if (left < 0 || left >= bufferWidth)
@@ -958,12 +1068,20 @@ namespace System {
 
                public void SetWindowPosition (int left, int top)
                {
+                       if (!inited) {
+                               Init ();
+                       }
+
                        // No need to throw exceptions here.
                        //throw new NotSupportedException ();
                }
 
                public void SetWindowSize (int width, int height)
                {
+                       if (!inited) {
+                               Init ();
+                       }
+
                        // No need to throw exceptions here.
                        //throw new NotSupportedException ();
                }