2 // System.WindowsConsoleDriver
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2005 Novell, Inc. (http://www.novell.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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
;
33 struct ConsoleCursorInfo
{
38 #pragma warning disable 169
40 public short EventType
;
41 // This is KEY_EVENT_RECORD
43 public short RepeatCount
;
44 public short VirtualKeyCode
;
45 public short VirtualScanCode
;
46 public char Character
;
47 public int ControlKeyState
;
52 #pragma warning restore 169
55 public char Character
;
56 public short Attributes
;
63 public Coord (int x
, int y
)
76 public SmallRect (int left
, int top
, int right
, int bottom
)
80 Right
= (short) right
;
81 Bottom
= (short) bottom
;
85 struct ConsoleScreenBufferInfo
{
87 public Coord CursorPosition
;
88 public short Attribute
;
89 public SmallRect Window
;
90 public Coord MaxWindowSize
;
99 unsafe class WindowsConsoleDriver
: IConsoleDriver
{
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...
114 // FOREGROUND_GREEN 2
116 // FOREGROUND_INTENSITY 8
117 // BACKGROUND_BLUE 16
118 // BACKGROUND_GREEN 32
120 // BACKGROUND_INTENSITY 128
121 static ConsoleColor
GetForeground (short attr
)
124 return (ConsoleColor
) attr
;
127 static ConsoleColor
GetBackground (short attr
)
131 return (ConsoleColor
) attr
;
134 static short GetAttrForeground (int attr
, ConsoleColor color
)
137 return (short) (attr
| (int) color
);
140 static short GetAttrBackground (int attr
, ConsoleColor color
)
143 int c
= ((int) color
) << 4;
144 return (short) (attr
| c
);
147 public ConsoleColor BackgroundColor
{
149 ConsoleScreenBufferInfo info
= new ConsoleScreenBufferInfo ();
150 GetConsoleScreenBufferInfo (outputHandle
, out info
);
151 return GetBackground (info
.Attribute
);
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
{
163 ConsoleScreenBufferInfo info
= new ConsoleScreenBufferInfo ();
164 GetConsoleScreenBufferInfo (outputHandle
, out info
);
167 set { SetBufferSize (BufferWidth, value); }
170 public int BufferWidth
{
172 ConsoleScreenBufferInfo info
= new ConsoleScreenBufferInfo ();
173 GetConsoleScreenBufferInfo (outputHandle
, out info
);
176 set { SetBufferSize (value, BufferHeight); }
179 public bool CapsLock
{
181 short state
= GetKeyState (20); // VK_CAPITAL
182 return ((state
& 1) == 1);
186 public int CursorLeft
{
188 ConsoleScreenBufferInfo info
= new ConsoleScreenBufferInfo ();
189 GetConsoleScreenBufferInfo (outputHandle
, out info
);
190 return info
.CursorPosition
.X
;
192 set { SetCursorPosition (value, CursorTop); }
195 public int CursorSize
{
197 ConsoleCursorInfo info
= new ConsoleCursorInfo ();
198 GetConsoleCursorInfo (outputHandle
, out info
);
202 if (value < 1 || value > 100)
203 throw new ArgumentOutOfRangeException ("value");
205 ConsoleCursorInfo info
= new ConsoleCursorInfo ();
206 GetConsoleCursorInfo (outputHandle
, out info
);
208 if (!SetConsoleCursorInfo (outputHandle
, ref info
))
209 throw new Exception ("SetConsoleCursorInfo failed");
213 public int CursorTop
{
215 ConsoleScreenBufferInfo info
= new ConsoleScreenBufferInfo ();
216 GetConsoleScreenBufferInfo (outputHandle
, out info
);
217 return info
.CursorPosition
.Y
;
219 set { SetCursorPosition (CursorLeft, value); }
222 public bool CursorVisible
{
224 ConsoleCursorInfo info
= new ConsoleCursorInfo ();
225 GetConsoleCursorInfo (outputHandle
, out info
);
229 ConsoleCursorInfo info
= new ConsoleCursorInfo ();
230 GetConsoleCursorInfo (outputHandle
, out info
);
231 if (info
.Visible
== value)
234 info
.Visible
= value;
235 if (!SetConsoleCursorInfo (outputHandle
, ref info
))
236 throw new Exception ("SetConsoleCursorInfo failed");
240 public ConsoleColor ForegroundColor
{
242 ConsoleScreenBufferInfo info
= new ConsoleScreenBufferInfo ();
243 GetConsoleScreenBufferInfo (outputHandle
, out info
);
244 return GetForeground (info
.Attribute
);
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
{
257 InputRecord record
= new InputRecord ();
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 ());
268 if (record
.EventType
== 1 && record
.KeyDown
&& !IsModifierKey (record
.VirtualKeyCode
))
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
{
284 Coord coord
= GetLargestConsoleWindowSize (outputHandle
);
285 if (coord
.X
== 0 && coord
.Y
== 0)
286 throw new Exception ("GetLargestConsoleWindowSize" + Marshal
.GetLastWin32Error ());
292 public int LargestWindowWidth
{
294 Coord coord
= GetLargestConsoleWindowSize (outputHandle
);
295 if (coord
.X
== 0 && coord
.Y
== 0)
296 throw new Exception ("GetLargestConsoleWindowSize" + Marshal
.GetLastWin32Error ());
302 public bool NumberLock
{
304 short state
= GetKeyState (144); // VK_NUMLOCK
305 return ((state
& 1) == 1);
309 public string Title
{
311 StringBuilder sb
= new StringBuilder (1024); // hope this is enough
312 if (GetConsoleTitle (sb
, 1024) == 0) {
314 sb
= new StringBuilder (26001);
315 if (GetConsoleTitle (sb
, 26000) == 0)
316 throw new Exception ("Got " + Marshal
.GetLastWin32Error ());
319 return sb
.ToString ();
323 throw new ArgumentNullException ("value");
325 if (!SetConsoleTitle (value))
326 throw new Exception ("Got " + Marshal
.GetLastWin32Error ());
330 public bool TreatControlCAsInput
{
333 if (!GetConsoleMode (inputHandle
, out mode
))
334 throw new Exception ("Failed in GetConsoleMode: " + Marshal
.GetLastWin32Error ());
336 // ENABLE_PROCESSED_INPUT
337 return ((mode
& 1) == 0);
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)
354 if (!SetConsoleMode (inputHandle
, mode
))
355 throw new Exception ("Failed in SetConsoleMode: " + Marshal
.GetLastWin32Error ());
359 public int WindowHeight
{
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
{
370 ConsoleScreenBufferInfo info
= new ConsoleScreenBufferInfo ();
371 GetConsoleScreenBufferInfo (outputHandle
, out info
);
372 return info
.Window
.Left
;
374 set { SetWindowPosition (value, WindowTop); }
377 public int WindowTop
{
379 ConsoleScreenBufferInfo info
= new ConsoleScreenBufferInfo ();
380 GetConsoleScreenBufferInfo (outputHandle
, out info
);
381 return info
.Window
.Top
;
383 set { SetWindowPosition (WindowLeft, value); }
386 public int WindowWidth
{
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
);
402 Coord coord
= new Coord ();
403 ConsoleScreenBufferInfo info
= new ConsoleScreenBufferInfo ();
404 GetConsoleScreenBufferInfo (outputHandle
, out info
);
406 int size
= info
.Size
.X
* info
.Size
.Y
;
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)
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.");
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.");
459 public string ReadLine ()
461 StringBuilder builder
= new StringBuilder ();
464 ConsoleKeyInfo key
= ReadKey (false);
465 char c
= key
.KeyChar
;
468 builder
.Append (key
.KeyChar
);
470 return builder
.ToString ();
473 public ConsoleKeyInfo
ReadKey (bool intercept
)
476 InputRecord record
= new InputRecord ();
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
))
485 // RIGHT_ALT_PRESSED 1
486 // LEFT_ALT_PRESSED 2
487 // RIGHT_CTRL_PRESSED 4
488 // LEFT_CTRL_PRESSED 8
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
) {
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
);