using System; using System.Collections.Generic; using System.Drawing; using System.Threading; using System.Runtime.InteropServices; namespace ManagedConsole { /// /// Color codes for console /// public enum ColorCode { BLACK = 0, DBLUE, GREEN, CYAN, RED, PUR, BROWN, NORM, GRAY, BLUE, BGREEN, BCYAN, BRED, BPUR, YEL, YELLOW = YEL, WHITE, } public partial class Console { bool allocated = false; IntPtr input; IntPtr output; CONSOLE_CURSOR_INFO curinfo = new CONSOLE_CURSOR_INFO(); CHAR_INFO [] buf; Size consize; Point pos; Point cursor; List windows = new List(); Thread thread; bool stopPolling = false; public void Allocate(string title, Size size) { if (allocated) return; buf = new CHAR_INFO[size.Width * size.Height]; consize = new Size(size.Width, size.Height); pos = new Point(0, 0); cursor = new Point(0, 0); stopPolling = false; for (int i=0; i= consize.Height) break; if (text[i] == '\n') { pos.X = x; pos.Y++; } else if (text[i] == '\t') { int numToRound = pos.X + 1; int multiple = 4; int untilX = ((((numToRound) + (multiple) - 1) / (multiple)) * (multiple)); int numspaces = untilX - pos.X; while (numspaces-- != 0) { PrintChar(' ', color); } } else { PrintChar(text[i], color); } } } private void PrintChar(char ch, ColorCode color) { ushort oldAttr = buf[pos.Y * consize.Width + pos.X].Attributes; oldAttr = (ushort)((oldAttr & 0xf0) | ((int)color)); buf[pos.Y * consize.Width + pos.X].Attributes = oldAttr; buf[pos.Y * consize.Width + pos.X].AsciiChar = ch; pos.X++; if (pos.X >= consize.Width) { pos.X = 0; pos.Y++; if (pos.Y >= consize.Height) { pos.Y = consize.Height - 1; } } } // Fill the console region with the specified color public void Fill(Rectangle rect, ColorCode backColor) { for (int y=rect.Y; y= consize.Height) break; for (int x =rect.X; x= consize.Width) break; ushort oldAttr = buf[y * consize.Width + x].Attributes; oldAttr = (ushort)((oldAttr & 0xf) | ((int)backColor << 4)); buf[y * consize.Width + x].Attributes = oldAttr; } } } public void Invalidate() { Blit(new Rectangle(0, 0, consize.Width, consize.Height)); } public void Blit (Rectangle rect) { COORD sz = new COORD(); sz.X = (short)consize.Width; sz.Y = (short)consize.Height; COORD pos = new COORD(); pos.X = (short)rect.X; pos.Y = (short)rect.Y; SMALL_RECT rgn = new SMALL_RECT(); rgn.Left = (short)rect.X; rgn.Top = (short)rect.Y; rgn.Right = (short)(rect.X + rect.Width - 1); rgn.Bottom = (short)(rect.Y + rect.Height - 1); bool success = WriteConsoleOutput(output, buf, sz, pos, ref rgn); } public void SetCursor (Point pos) { COORD cr = new COORD(); cr.X = (short)pos.X; cr.Y = (short)pos.Y; cursor.X = pos.X; cursor.Y = pos.Y; SetConsoleCursorPosition(output, cr); } public Point GetCursor () { return new Point(cursor.X, cursor.Y); } #endregion "Console Output" #region "Console Input" public enum Key { None = 0, Esc, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, Tilda, Key1, Key2, Key3, Key4, Key5, Key6, Key7, Key8, Key9, Key0, KeyMinus, KeyPlus, Backspace, Enter, Space, Up, Down, Left, Right, NumSlash, NumAsterisk, NumMinus, NumPlus, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M, LSquare, RSquare, Colon, Quotes, BackSlash, LAngle, RAngle, Slash, Ins, Del, Home, End, PageUp, PageDown, } public class KeyInfo { public Key KeyCode = Key.None; public bool Shift = false; public bool Ctrl = false; public bool Alt = false; public override string ToString() { switch (KeyCode) { case Key.Tilda: return Shift ? "~" : "`"; case Key.Key1: return Shift ? "!" : "1"; case Key.Key2: return Shift ? "@" : "2"; case Key.Key3: return Shift ? "#" : "3"; case Key.Key4: return Shift ? "$" : "4"; case Key.Key5: return Shift ? "%" : "5"; case Key.Key6: return Shift ? "^" : "6"; case Key.Key7: return Shift ? "&" : "7"; case Key.Key8: return Shift ? "*" : "8"; case Key.Key9: return Shift ? "(" : "9"; case Key.Key0: return Shift ? ")" : "0"; case Key.KeyMinus: return Shift ? "_" : "-"; case Key.KeyPlus: return Shift ? "+" : "="; case Key.Q: return Shift ? "Q" : "q"; case Key.W: return Shift ? "W" : "w"; case Key.E: return Shift ? "E" : "e"; case Key.R: return Shift ? "R" : "r"; case Key.T: return Shift ? "T" : "t"; case Key.Y: return Shift ? "Y" : "y"; case Key.U: return Shift ? "U" : "u"; case Key.I: return Shift ? "I" : "i"; case Key.O: return Shift ? "O" : "o"; case Key.P: return Shift ? "P" : "p"; case Key.A: return Shift ? "A" : "a"; case Key.S: return Shift ? "S" : "s"; case Key.D: return Shift ? "D" : "d"; case Key.F: return Shift ? "F" : "f"; case Key.G: return Shift ? "G" : "g"; case Key.H: return Shift ? "H" : "h"; case Key.J: return Shift ? "J" : "j"; case Key.K: return Shift ? "K" : "k"; case Key.L: return Shift ? "L" : "l"; case Key.Z: return Shift ? "Z" : "z"; case Key.X: return Shift ? "X" : "x"; case Key.C: return Shift ? "C" : "c"; case Key.V: return Shift ? "V" : "v"; case Key.B: return Shift ? "B" : "b"; case Key.N: return Shift ? "N" : "n"; case Key.M: return Shift ? "M" : "m"; case Key.Space: return " "; case Key.NumSlash: return "/"; case Key.NumAsterisk: return "*"; case Key.NumMinus: return "-"; case Key.NumPlus: return "+"; case Key.Num0: return "0"; case Key.Num1: return "1"; case Key.Num2: return "2"; case Key.Num3: return "3"; case Key.Num4: return "4"; case Key.Num5: return "5"; case Key.Num6: return "6"; case Key.Num7: return "7"; case Key.Num8: return "8"; case Key.Num9: return "9"; case Key.LSquare: return Shift ? "{" : "["; case Key.RSquare: return Shift ? "}" : "]"; case Key.Colon: return Shift ? ":" : ";"; case Key.Quotes: return Shift ? "\"" : "'"; case Key.BackSlash: return Shift ? "|" : "\\"; case Key.LAngle: return Shift ? "<" : ","; case Key.RAngle: return Shift ? ">" : "."; case Key.Slash: return Shift ? "?" : "/"; } return ""; } } // Key Press Event public virtual void OnKeyInput(KeyInfo key) { } private KeyInfo PollKeyboard () { INPUT_RECORD [] record = new INPUT_RECORD[1]; uint count; PeekConsoleInput(input, record, 1, out count); if (count == 0) return null; ReadConsoleInput(input, record, 1, out count); if (count == 0) return null; if (record[0].EventType == KEY_EVENT && record[0].KeyEvent.bKeyDown) { return Translate(record[0]); } return null; } private KeyInfo Translate(INPUT_RECORD record) { KeyInfo key = new KeyInfo(); key.Shift = (record.KeyEvent.dwControlKeyState & SHIFT_PRESSED) != 0; key.Alt = ((record.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED) != 0) || ((record.KeyEvent.dwControlKeyState & RIGHT_ALT_PRESSED) != 0); key.Ctrl = ((record.KeyEvent.dwControlKeyState & LEFT_CTRL_PRESSED) != 0) || ((record.KeyEvent.dwControlKeyState & RIGHT_CTRL_PRESSED) != 0); switch (record.KeyEvent.wVirtualKeyCode) { case 0x1B: key.KeyCode = Key.Esc; break; case 0x08: key.KeyCode = Key.Backspace; break; case 0x20: key.KeyCode = Key.Space; break; case 0x0D: key.KeyCode = Key.Enter; break; case 0x70: key.KeyCode = Key.F1; break; case 0x71: key.KeyCode = Key.F2; break; case 0x72: key.KeyCode = Key.F3; break; case 0x73: key.KeyCode = Key.F4; break; case 0x74: key.KeyCode = Key.F5; break; case 0x75: key.KeyCode = Key.F6; break; case 0x76: key.KeyCode = Key.F7; break; case 0x77: key.KeyCode = Key.F8; break; case 0x78: key.KeyCode = Key.F9; break; case 0x79: key.KeyCode = Key.F10; break; case 0x7A: key.KeyCode = Key.F11; break; case 0x7B: key.KeyCode = Key.F12; break; case 0x31: key.KeyCode = Key.Key1; break; case 0x32: key.KeyCode = Key.Key2; break; case 0x33: key.KeyCode = Key.Key3; break; case 0x34: key.KeyCode = Key.Key4; break; case 0x35: key.KeyCode = Key.Key5; break; case 0x36: key.KeyCode = Key.Key6; break; case 0x37: key.KeyCode = Key.Key7; break; case 0x38: key.KeyCode = Key.Key8; break; case 0x39: key.KeyCode = Key.Key9; break; case 0x30: key.KeyCode = Key.Key0; break; case 0xC0: key.KeyCode = Key.Tilda; break; case 0xBD: key.KeyCode = Key.KeyMinus; break; case 0xBB: key.KeyCode = Key.KeyPlus; break; case 0x51: key.KeyCode = Key.Q; break; case 0x57: key.KeyCode = Key.W; break; case 0x45: key.KeyCode = Key.E; break; case 0x52: key.KeyCode = Key.R; break; case 0x54: key.KeyCode = Key.T; break; case 0x59: key.KeyCode = Key.Y; break; case 0x55: key.KeyCode = Key.U; break; case 0x49: key.KeyCode = Key.I; break; case 0x4F: key.KeyCode = Key.O; break; case 0x50: key.KeyCode = Key.P; break; case 0x41: key.KeyCode = Key.A; break; case 0x53: key.KeyCode = Key.S; break; case 0x44: key.KeyCode = Key.D; break; case 0x46: key.KeyCode = Key.F; break; case 0x47: key.KeyCode = Key.G; break; case 0x48: key.KeyCode = Key.H; break; case 0x4A: key.KeyCode = Key.J; break; case 0x4B: key.KeyCode = Key.K; break; case 0x4C: key.KeyCode = Key.L; break; case 0x5A: key.KeyCode = Key.Z; break; case 0x58: key.KeyCode = Key.X; break; case 0x43: key.KeyCode = Key.C; break; case 0x56: key.KeyCode = Key.V; break; case 0x42: key.KeyCode = Key.B; break; case 0x4E: key.KeyCode = Key.N; break; case 0x4D: key.KeyCode = Key.M; break; case 0x60: key.KeyCode = Key.Num0; break; case 0x61: key.KeyCode = Key.Num1; break; case 0x62: key.KeyCode = Key.Num2; break; case 0x63: key.KeyCode = Key.Num3; break; case 0x64: key.KeyCode = Key.Num4; break; case 0x65: key.KeyCode = Key.Num5; break; case 0x66: key.KeyCode = Key.Num6; break; case 0x67: key.KeyCode = Key.Num7; break; case 0x68: key.KeyCode = Key.Num8; break; case 0x69: key.KeyCode = Key.Num9; break; case 0x6F: key.KeyCode = Key.NumSlash; break; case 0x6A: key.KeyCode = Key.NumAsterisk; break; case 0x6D: key.KeyCode = Key.NumMinus; break; case 0x6B: key.KeyCode = Key.NumPlus; break; case 0x26: key.KeyCode = Key.Up; break; case 0x28: key.KeyCode = Key.Down; break; case 0x25: key.KeyCode = Key.Left; break; case 0x27: key.KeyCode = Key.Right; break; case 0xDB: key.KeyCode = Key.LSquare; break; case 0xDD: key.KeyCode = Key.RSquare; break; case 0xBA: key.KeyCode = Key.Colon; break; case 0xDE: key.KeyCode = Key.Quotes; break; case 0xDC: key.KeyCode = Key.BackSlash; break; case 0xBC: key.KeyCode = Key.LAngle; break; case 0xBE: key.KeyCode = Key.RAngle; break; case 0xBF: key.KeyCode = Key.Slash; break; case 0x2D: key.KeyCode = Key.Ins; break; case 0x2E: key.KeyCode = Key.Del; break; case 0x24: key.KeyCode = Key.Home; break; case 0x23: key.KeyCode = Key.End; break; case 0x21: key.KeyCode = Key.PageUp; break; case 0x22: key.KeyCode = Key.PageDown; break; } return key; } private void PollingThreadProc() { while (!stopPolling) { KeyInfo key = PollKeyboard(); if (key != null) { if (key.KeyCode != Key.None) { OnKeyInput(key); foreach (var w in windows) { if (w.IsActive()) w.OnKeyInput(key); } } } Thread.Sleep(10); } } #endregion "Console Input" } }