[Android] Enable access to up-to-date tzdata on Android 10+ (#20350)
[mono-project.git] / mcs / class / corlib / System / WindowsConsoleDriver.cs
blobbcf2dd0e08cda105c44ba191331b965b7862e561
1 //
2 // System.WindowsConsoleDriver
3 //
4 // Author:
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (C) 2005 Novell, Inc. (http://www.novell.com)
8 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 #if MONO_FEATURE_CONSOLE
30 using System.Runtime.InteropServices;
31 using System.Text;
32 namespace System {
33 struct ConsoleCursorInfo {
34 public int Size;
35 public bool Visible;
38 #pragma warning disable 169
39 struct InputRecord {
40 public short EventType;
41 // This is KEY_EVENT_RECORD
42 public bool KeyDown;
43 public short RepeatCount;
44 public short VirtualKeyCode;
45 public short VirtualScanCode;
46 public char Character;
47 public int ControlKeyState;
48 int pad1;
49 bool pad2;
52 #pragma warning restore 169
54 struct CharInfo {
55 public char Character;
56 public short Attributes;
59 struct Coord {
60 public short X;
61 public short Y;
63 public Coord (int x, int y)
65 X = (short) x;
66 Y = (short) y;
70 struct SmallRect {
71 public short Left;
72 public short Top;
73 public short Right;
74 public short Bottom;
76 public SmallRect (int left, int top, int right, int bottom)
78 Left = (short) left;
79 Top = (short) top;
80 Right = (short) right;
81 Bottom = (short) bottom;
85 struct ConsoleScreenBufferInfo {
86 public Coord Size;
87 public Coord CursorPosition;
88 public short Attribute;
89 public SmallRect Window;
90 public Coord MaxWindowSize;
93 enum Handles {
94 STD_INPUT = -10,
95 STD_OUTPUT = -11,
96 STD_ERROR = -12
99 unsafe class WindowsConsoleDriver : IConsoleDriver {
100 IntPtr inputHandle;
101 IntPtr outputHandle;
102 short defaultAttribute;
104 public WindowsConsoleDriver ()
106 outputHandle = GetStdHandle (Handles.STD_OUTPUT);
107 inputHandle = GetStdHandle (Handles.STD_INPUT);
108 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
109 GetConsoleScreenBufferInfo (outputHandle, out info);
110 defaultAttribute = info.Attribute; // Not sure about this...
113 // FOREGROUND_BLUE 1
114 // FOREGROUND_GREEN 2
115 // FOREGROUND_RED 4
116 // FOREGROUND_INTENSITY 8
117 // BACKGROUND_BLUE 16
118 // BACKGROUND_GREEN 32
119 // BACKGROUND_RED 64
120 // BACKGROUND_INTENSITY 128
121 static ConsoleColor GetForeground (short attr)
123 attr &= 0x0F;
124 return (ConsoleColor) attr;
127 static ConsoleColor GetBackground (short attr)
129 attr &= 0xF0;
130 attr >>= 4;
131 return (ConsoleColor) attr;
134 static short GetAttrForeground (int attr, ConsoleColor color)
136 attr &= ~15;
137 return (short) (attr | (int) color);
140 static short GetAttrBackground (int attr, ConsoleColor color)
142 attr &= ~0xf0;
143 int c = ((int) color) << 4;
144 return (short) (attr | c);
147 public ConsoleColor BackgroundColor {
148 get {
149 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
150 GetConsoleScreenBufferInfo (outputHandle, out info);
151 return GetBackground (info.Attribute);
153 set {
154 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
155 GetConsoleScreenBufferInfo (outputHandle, out info);
156 short attr = GetAttrBackground (info.Attribute, value);
157 SetConsoleTextAttribute (outputHandle, attr);
161 public int BufferHeight {
162 get {
163 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
164 GetConsoleScreenBufferInfo (outputHandle, out info);
165 return info.Size.Y;
167 set { SetBufferSize (BufferWidth, value); }
170 public int BufferWidth {
171 get {
172 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
173 GetConsoleScreenBufferInfo (outputHandle, out info);
174 return info.Size.X;
176 set { SetBufferSize (value, BufferHeight); }
179 public bool CapsLock {
180 get {
181 short state = GetKeyState (20); // VK_CAPITAL
182 return ((state & 1) == 1);
186 public int CursorLeft {
187 get {
188 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
189 GetConsoleScreenBufferInfo (outputHandle, out info);
190 return info.CursorPosition.X;
192 set { SetCursorPosition (value, CursorTop); }
195 public int CursorSize {
196 get {
197 ConsoleCursorInfo info = new ConsoleCursorInfo ();
198 GetConsoleCursorInfo (outputHandle, out info);
199 return info.Size;
201 set {
202 if (value < 1 || value > 100)
203 throw new ArgumentOutOfRangeException ("value");
205 ConsoleCursorInfo info = new ConsoleCursorInfo ();
206 GetConsoleCursorInfo (outputHandle, out info);
207 info.Size = value;
208 if (!SetConsoleCursorInfo (outputHandle, ref info))
209 throw new Exception ("SetConsoleCursorInfo failed");
213 public int CursorTop {
214 get {
215 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
216 GetConsoleScreenBufferInfo (outputHandle, out info);
217 return info.CursorPosition.Y;
219 set { SetCursorPosition (CursorLeft, value); }
222 public bool CursorVisible {
223 get {
224 ConsoleCursorInfo info = new ConsoleCursorInfo ();
225 GetConsoleCursorInfo (outputHandle, out info);
226 return info.Visible;
228 set {
229 ConsoleCursorInfo info = new ConsoleCursorInfo ();
230 GetConsoleCursorInfo (outputHandle, out info);
231 if (info.Visible == value)
232 return;
234 info.Visible = value;
235 if (!SetConsoleCursorInfo (outputHandle, ref info))
236 throw new Exception ("SetConsoleCursorInfo failed");
240 public ConsoleColor ForegroundColor {
241 get {
242 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
243 GetConsoleScreenBufferInfo (outputHandle, out info);
244 return GetForeground (info.Attribute);
246 set {
247 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
248 GetConsoleScreenBufferInfo (outputHandle, out info);
249 short attr = GetAttrForeground (info.Attribute, value);
250 SetConsoleTextAttribute (outputHandle, attr);
254 public bool KeyAvailable {
255 get {
256 int eventsRead;
257 InputRecord record = new InputRecord ();
258 while (true) {
259 // Use GetNumberOfConsoleInputEvents and remove the while?
260 if (!PeekConsoleInput (inputHandle, out record, 1, out eventsRead))
261 throw new InvalidOperationException ("Error in PeekConsoleInput " +
262 Marshal.GetLastWin32Error ());
264 if (eventsRead == 0)
265 return false;
267 //KEY_EVENT == 1
268 if (record.EventType == 1 && record.KeyDown && !IsModifierKey (record.VirtualKeyCode))
269 return true;
271 if (!ReadConsoleInput (inputHandle, out record, 1, out eventsRead))
272 throw new InvalidOperationException ("Error in ReadConsoleInput " +
273 Marshal.GetLastWin32Error ());
278 public bool Initialized { // Not useful on windows, so false.
279 get { return false; }
282 public int LargestWindowHeight {
283 get {
284 Coord coord = GetLargestConsoleWindowSize (outputHandle);
285 if (coord.X == 0 && coord.Y == 0)
286 throw new Exception ("GetLargestConsoleWindowSize" + Marshal.GetLastWin32Error ());
288 return coord.Y;
292 public int LargestWindowWidth {
293 get {
294 Coord coord = GetLargestConsoleWindowSize (outputHandle);
295 if (coord.X == 0 && coord.Y == 0)
296 throw new Exception ("GetLargestConsoleWindowSize" + Marshal.GetLastWin32Error ());
298 return coord.X;
302 public bool NumberLock {
303 get {
304 short state = GetKeyState (144); // VK_NUMLOCK
305 return ((state & 1) == 1);
309 public string Title {
310 get {
311 StringBuilder sb = new StringBuilder (1024); // hope this is enough
312 if (GetConsoleTitle (sb, 1024) == 0) {
313 // Try the maximum
314 sb = new StringBuilder (26001);
315 if (GetConsoleTitle (sb, 26000) == 0)
316 throw new Exception ("Got " + Marshal.GetLastWin32Error ());
319 return sb.ToString ();
321 set {
322 if (value == null)
323 throw new ArgumentNullException ("value");
325 if (!SetConsoleTitle (value))
326 throw new Exception ("Got " + Marshal.GetLastWin32Error ());
330 public bool TreatControlCAsInput {
331 get {
332 int mode;
333 if (!GetConsoleMode (inputHandle, out mode))
334 throw new Exception ("Failed in GetConsoleMode: " + Marshal.GetLastWin32Error ());
336 // ENABLE_PROCESSED_INPUT
337 return ((mode & 1) == 0);
340 set {
341 int mode;
342 if (!GetConsoleMode (inputHandle, out mode))
343 throw new Exception ("Failed in GetConsoleMode: " + Marshal.GetLastWin32Error ());
345 bool cAsInput = ((mode & 1) == 0);
346 if (cAsInput == value)
347 return;
349 if (value)
350 mode &= ~1;
351 else
352 mode |= 1;
354 if (!SetConsoleMode (inputHandle, mode))
355 throw new Exception ("Failed in SetConsoleMode: " + Marshal.GetLastWin32Error ());
359 public int WindowHeight {
360 get {
361 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
362 GetConsoleScreenBufferInfo (outputHandle, out info);
363 return info.Window.Bottom - info.Window.Top + 1;
365 set { SetWindowSize (WindowWidth, value); }
368 public int WindowLeft {
369 get {
370 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
371 GetConsoleScreenBufferInfo (outputHandle, out info);
372 return info.Window.Left;
374 set { SetWindowPosition (value, WindowTop); }
377 public int WindowTop {
378 get {
379 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
380 GetConsoleScreenBufferInfo (outputHandle, out info);
381 return info.Window.Top;
383 set { SetWindowPosition (WindowLeft, value); }
386 public int WindowWidth {
387 get {
388 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
389 GetConsoleScreenBufferInfo (outputHandle, out info);
390 return info.Window.Right - info.Window.Left + 1;
392 set { SetWindowSize (value, WindowHeight); }
395 public void Beep (int frequency, int duration)
397 _Beep (frequency, duration);
400 public void Clear ()
402 Coord coord = new Coord ();
403 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
404 GetConsoleScreenBufferInfo (outputHandle, out info);
406 int size = info.Size.X * info.Size.Y;
407 int written;
408 FillConsoleOutputCharacter (outputHandle, ' ', size, coord, out written);
410 GetConsoleScreenBufferInfo (outputHandle, out info);
412 FillConsoleOutputAttribute (outputHandle, info.Attribute, size, coord, out written);
413 SetConsoleCursorPosition (outputHandle, coord);
416 public void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight,
417 int targetLeft, int targetTop, Char sourceChar,
418 ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
420 if (sourceForeColor < 0)
421 throw new ArgumentException ("Cannot be less than 0.", "sourceForeColor");
423 if (sourceBackColor < 0)
424 throw new ArgumentException ("Cannot be less than 0.", "sourceBackColor");
426 if (sourceWidth == 0 || sourceHeight == 0)
427 return;
429 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
430 GetConsoleScreenBufferInfo (outputHandle, out info);
431 CharInfo [] buffer = new CharInfo [sourceWidth * sourceHeight];
432 Coord bsize = new Coord (sourceWidth, sourceHeight);
433 Coord bpos = new Coord (0, 0);
434 SmallRect region = new SmallRect (sourceLeft, sourceTop, sourceLeft + sourceWidth - 1, sourceTop + sourceHeight - 1);
435 fixed (void *ptr = &buffer [0]) {
436 if (!ReadConsoleOutput (outputHandle, ptr, bsize, bpos, ref region))
437 throw new ArgumentException (String.Empty, "Cannot read from the specified coordinates.");
440 int written;
441 short attr = GetAttrForeground (0, sourceForeColor);
442 attr = GetAttrBackground (attr, sourceBackColor);
443 bpos = new Coord (sourceLeft, sourceTop);
444 for (int i = 0; i < sourceHeight; i++, bpos.Y++) {
445 FillConsoleOutputCharacter (outputHandle, sourceChar, sourceWidth, bpos, out written);
446 FillConsoleOutputAttribute (outputHandle, attr, sourceWidth, bpos, out written);
449 bpos = new Coord (0, 0);
450 region = new SmallRect (targetLeft, targetTop, targetLeft + sourceWidth - 1, targetTop + sourceHeight - 1);
451 if (!WriteConsoleOutput (outputHandle, buffer, bsize, bpos, ref region))
452 throw new ArgumentException (String.Empty, "Cannot write to the specified coordinates.");
455 public void Init ()
459 public string ReadLine ()
461 StringBuilder builder = new StringBuilder ();
462 bool exit = false;
463 do {
464 ConsoleKeyInfo key = ReadKey (false);
465 char c = key.KeyChar;
466 exit = (c == '\n');
467 if (!exit)
468 builder.Append (key.KeyChar);
469 } while (!exit);
470 return builder.ToString ();
473 public ConsoleKeyInfo ReadKey (bool intercept)
475 int eventsRead;
476 InputRecord record = new InputRecord ();
477 for (;;) {
478 if (!ReadConsoleInput (inputHandle, out record, 1, out eventsRead))
479 throw new InvalidOperationException ("Error in ReadConsoleInput " +
480 Marshal.GetLastWin32Error ());
481 if (record.KeyDown && record.EventType == 1 && !IsModifierKey (record.VirtualKeyCode))
482 break;
485 // RIGHT_ALT_PRESSED 1
486 // LEFT_ALT_PRESSED 2
487 // RIGHT_CTRL_PRESSED 4
488 // LEFT_CTRL_PRESSED 8
489 // SHIFT_PRESSED 16
490 bool alt = ((record.ControlKeyState & 3) != 0);
491 bool ctrl = ((record.ControlKeyState & 12) != 0);
492 bool shift = ((record.ControlKeyState & 16) != 0);
493 return new ConsoleKeyInfo (record.Character, (ConsoleKey) record.VirtualKeyCode, shift, alt, ctrl);
496 public void ResetColor ()
498 SetConsoleTextAttribute (outputHandle, defaultAttribute);
501 public void SetBufferSize (int width, int height)
503 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
504 GetConsoleScreenBufferInfo (outputHandle, out info);
506 if (width - 1 > info.Window.Right)
507 throw new ArgumentOutOfRangeException ("width");
509 if (height - 1 > info.Window.Bottom)
510 throw new ArgumentOutOfRangeException ("height");
512 Coord coord = new Coord (width, height);
513 if (!SetConsoleScreenBufferSize (outputHandle, coord))
514 throw new ArgumentOutOfRangeException ("height/width", "Cannot be smaller than the window size.");
517 public void SetCursorPosition (int left, int top)
519 Coord coord = new Coord (left, top);
520 SetConsoleCursorPosition (outputHandle, coord);
523 public void SetWindowPosition (int left, int top)
525 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
526 GetConsoleScreenBufferInfo (outputHandle, out info);
527 SmallRect rect = info.Window;
528 rect.Left = (short) left;
529 rect.Top = (short) top;
530 if (!SetConsoleWindowInfo (outputHandle, true, ref rect))
531 throw new ArgumentOutOfRangeException ("left/top", "Windows error " + Marshal.GetLastWin32Error ());
534 public void SetWindowSize (int width, int height)
536 ConsoleScreenBufferInfo info = new ConsoleScreenBufferInfo ();
537 GetConsoleScreenBufferInfo (outputHandle, out info);
538 SmallRect rect = info.Window;
539 rect.Right = (short) (rect.Left + width - 1);
540 rect.Bottom = (short) (rect.Top + height - 1);
541 if (!SetConsoleWindowInfo (outputHandle, true, ref rect))
542 throw new ArgumentOutOfRangeException ("left/top", "Windows error " + Marshal.GetLastWin32Error ());
545 static bool IsModifierKey (short virtualKeyCode)
547 // 0x10 through 0x14 is shift/control/alt/pause/capslock
548 // 0x2C is print screen, 0x90 is numlock, 0x91 is scroll lock
549 switch (virtualKeyCode) {
550 case 0x10:
551 case 0x11:
552 case 0x12:
553 case 0x14:
554 case 0x90:
555 case 0x91:
556 return true;
557 default:
558 return false;
563 // Imports
565 [DllImport ("kernel32.dll", EntryPoint="GetStdHandle", SetLastError=true, CharSet=CharSet.Unicode)]
566 extern static IntPtr GetStdHandle (Handles handle);
568 [DllImport ("kernel32.dll", EntryPoint="Beep", SetLastError=true, CharSet=CharSet.Unicode)]
569 extern static void _Beep (int frequency, int duration);
571 [DllImport ("kernel32.dll", EntryPoint="GetConsoleScreenBufferInfo", SetLastError=true, CharSet=CharSet.Unicode)]
572 extern static bool GetConsoleScreenBufferInfo (IntPtr handle, out ConsoleScreenBufferInfo info);
574 [DllImport ("kernel32.dll", EntryPoint="FillConsoleOutputCharacter", SetLastError=true, CharSet=CharSet.Unicode)]
575 extern static bool FillConsoleOutputCharacter (IntPtr handle, char c, int size, Coord coord, out int written);
577 [DllImport ("kernel32.dll", EntryPoint="FillConsoleOutputAttribute", SetLastError=true, CharSet=CharSet.Unicode)]
578 extern static bool FillConsoleOutputAttribute (IntPtr handle, short c, int size, Coord coord, out int written);
580 [DllImport ("kernel32.dll", EntryPoint="SetConsoleCursorPosition", SetLastError=true, CharSet=CharSet.Unicode)]
581 extern static bool SetConsoleCursorPosition (IntPtr handle, Coord coord);
583 [DllImport ("kernel32.dll", EntryPoint="SetConsoleTextAttribute", SetLastError=true, CharSet=CharSet.Unicode)]
584 extern static bool SetConsoleTextAttribute (IntPtr handle, short attribute);
586 [DllImport ("kernel32.dll", EntryPoint="SetConsoleScreenBufferSize", SetLastError=true, CharSet=CharSet.Unicode)]
587 extern static bool SetConsoleScreenBufferSize (IntPtr handle, Coord newSize);
589 [DllImport ("kernel32.dll", EntryPoint="SetConsoleWindowInfo", SetLastError=true, CharSet=CharSet.Unicode)]
590 extern static bool SetConsoleWindowInfo (IntPtr handle, bool absolute, ref SmallRect rect);
592 [DllImport ("kernel32.dll", EntryPoint="GetConsoleTitle", SetLastError=true, CharSet=CharSet.Unicode)]
593 extern static int GetConsoleTitle (StringBuilder sb, int size);
595 [DllImport ("kernel32.dll", EntryPoint="SetConsoleTitle", SetLastError=true, CharSet=CharSet.Unicode)]
596 extern static bool SetConsoleTitle (string title);
598 [DllImport ("kernel32.dll", EntryPoint="GetConsoleCursorInfo", SetLastError=true, CharSet=CharSet.Unicode)]
599 extern static bool GetConsoleCursorInfo (IntPtr handle, out ConsoleCursorInfo info);
601 [DllImport ("kernel32.dll", EntryPoint="SetConsoleCursorInfo", SetLastError=true, CharSet=CharSet.Unicode)]
602 extern static bool SetConsoleCursorInfo (IntPtr handle, ref ConsoleCursorInfo info);
604 [DllImport ("user32.dll", EntryPoint="GetKeyState", SetLastError=true, CharSet=CharSet.Unicode)]
605 extern static short GetKeyState (int virtKey);
607 [DllImport ("kernel32.dll", EntryPoint="GetConsoleMode", SetLastError=true, CharSet=CharSet.Unicode)]
608 extern static bool GetConsoleMode (IntPtr handle, out int mode);
610 [DllImport ("kernel32.dll", EntryPoint="SetConsoleMode", SetLastError=true, CharSet=CharSet.Unicode)]
611 extern static bool SetConsoleMode (IntPtr handle, int mode);
613 [DllImport ("kernel32.dll", EntryPoint="PeekConsoleInput", SetLastError=true, CharSet=CharSet.Unicode)]
614 extern static bool PeekConsoleInput (IntPtr handle, out InputRecord record, int length, out int eventsRead);
616 [DllImport ("kernel32.dll", EntryPoint="ReadConsoleInput", SetLastError=true, CharSet=CharSet.Unicode)]
617 extern static bool ReadConsoleInput (IntPtr handle, out InputRecord record, int length, out int nread);
619 [DllImport ("kernel32.dll", EntryPoint="GetLargestConsoleWindowSize", SetLastError=true, CharSet=CharSet.Unicode)]
620 extern static Coord GetLargestConsoleWindowSize (IntPtr handle);
622 [DllImport ("kernel32.dll", EntryPoint="ReadConsoleOutput", SetLastError=true, CharSet=CharSet.Unicode)]
623 extern static bool ReadConsoleOutput (IntPtr handle, void *buffer, Coord bsize, Coord bpos, ref SmallRect region);
625 [DllImport ("kernel32.dll", EntryPoint="WriteConsoleOutput", SetLastError=true, CharSet=CharSet.Unicode)]
626 extern static bool WriteConsoleOutput (IntPtr handle, CharInfo [] buffer, Coord bsize, Coord bpos, ref SmallRect region);
629 #endif