unit WinCrt; interface Uses Windows; type WinReadKeyRecord = record KeyStatus: byte; AsciiChar: char; KeyCode: word; end; const // Foreground color constants fBlack = 0; fBlue = FOREGROUND_BLUE; fGreen = FOREGROUND_GREEN; fCyan = FOREGROUND_BLUE OR FOREGROUND_GREEN; fRed = FOREGROUND_RED; fMagenta = FOREGROUND_BLUE OR FOREGROUND_RED; fBrown = FOREGROUND_GREEN OR FOREGROUND_RED; fLightGray = FOREGROUND_BLUE OR FOREGROUND_GREEN OR FOREGROUND_RED; fDarkGray = fBlack OR FOREGROUND_INTENSITY; fLightBlue = fBlue OR FOREGROUND_INTENSITY; fLightGreen = fGreen OR FOREGROUND_INTENSITY; fLightCyan = fCyan OR FOREGROUND_INTENSITY; fLightRed = fRed OR FOREGROUND_INTENSITY; fLightMagenta = fMagenta OR FOREGROUND_INTENSITY; fYellow = fBrown OR FOREGROUND_INTENSITY; fWhite = fLightGray OR FOREGROUND_INTENSITY; // Background color constants bBlack = 0; bBlue = BACKGROUND_BLUE; bGreen = BACKGROUND_GREEN; bCyan = BACKGROUND_BLUE OR BACKGROUND_GREEN; bRed = BACKGROUND_RED; bMagenta = BACKGROUND_BLUE OR BACKGROUND_RED; bBrown = BACKGROUND_GREEN OR BACKGROUND_RED; bLightGray = BACKGROUND_BLUE OR BACKGROUND_GREEN OR BACKGROUND_RED; bDarkGray = bBlack OR BACKGROUND_INTENSITY; bLightBlue = bBlue OR BACKGROUND_INTENSITY; bLightGreen = bGreen OR BACKGROUND_INTENSITY; bLightCyan = bCyan OR BACKGROUND_INTENSITY; bLightRed = bRed OR BACKGROUND_INTENSITY; bLightMagenta = bMagenta OR BACKGROUND_INTENSITY; bYellow = bBrown OR BACKGROUND_INTENSITY; bWhite = bLightGray OR BACKGROUND_INTENSITY; // Constants designating input events NO_EVENT = 0; KEY_EVENT_IN_PROGRESS = $100; _MOUSE_EVENT_IN_PROGRESS = $200; procedure ClrEol; { Clears all characters from cursor position to end of line without moving the cursor by filling character cells with blanks and attribute cells with the current screen buffer attribute. } procedure ClrScr; { Clears screen buffer by filling character cells with blanks and attribute cells with the current screen buffer attribute. The cursor is positioned in the top left corner of the screen buffer } procedure FlushInputBuffer; function GetTextBackground: byte; function GetTextColor: byte; Procedure GotoXY(X, Y: integer); Procedure HighVideo; Procedure HighVideoBackground; Function InputEvent: word; { Returns NO_EVENT if input buffer is empty ; KEY_EVENT if there is a pending key event with key released again, and key is not one of the control keys; KEY_EVENT_IN_PROGRESS if there is another pending key event; _MOUSE_EVENT if there is a pending mouse event without moving the mouse; _MOUSE_EVENT_IN_PROGRESS if there is another pending mouse event; WINDOW_BUFFER_SIZE_EVENT is the user resized the screen buffer and window input is enabled (default mode disabled). } function KeyPressed: boolean; { Returns TRUE if there is a pending key event with key released again, and key is not one of the control keys; FALSE otherwise. } Procedure LowVideo; Procedure LowVideoBackground; Procedure NormVideo; Procedure NormVideoBackground; Function ReadKey: char; Procedure TextBackground (Color: Byte); Procedure TextColor (Color: Byte); Function WhereX: integer; Function WhereY: integer; Function WinReadKey: WinReadKeyRecord; { Return value in KeyStatus element: - bit 0: shift key pressed - bit 1: ctrl key pressed - bit 2: alt key pressed The KeyCode element has the virtual key code. N.B. nog regelen: extended ASCII via Alt-keypad toetsen. } implementation type PInputBuffer = ^TInputBuffer; TInputBuffer = array[word] of TInputRecord; var StartTextIntensity, StartBackgroundIntensity: byte; pCsbi: PConsoleScreenBufferInfo; function GetScreenInfo: TConsoleScreenBufferInfo; forward; Function RemapScanCode (ScanCode: byte; CtrlKeyState: byte): byte; forward; procedure ClrEol; var hConsoleOutput: THandle; cCharacter: Char; wAttribute: word; nLength: dword; dwWriteCoord: TCoord; lpWritten: dword; begin hConsoleOutput := GetStdHandle(STD_OUTPUT_HANDLE); cCharacter := ' '; New(pCsbi); GetConsoleScreenBufferInfo(hConsoleOutput, pCsbi^); wAttribute := pCsbi^.wAttributes; nLength := pCsbi^.dwSize.X - pCsbi^.dwCursorPosition.X + 1; dwWriteCoord.X := pCsbi^.dwCursorPosition.X; dwWriteCoord.Y := pCsbi^.dwCursorPosition.Y; Dispose(pCsbi); FillConsoleOutputCharacter(hConsoleOutput, cCharacter, nLength, dwWriteCoord, lpWritten); FillConsoleOutputAttribute(hConsoleOutput, wAttribute, nLength, dwWriteCoord, lpWritten); end; procedure ClrScr; var hConsoleOutput: THandle; cCharacter: Char; wAttribute: word; nLength: dword; dwWriteCoord: TCoord; lpWritten: dword; begin hConsoleOutput := GetStdHandle(STD_OUTPUT_HANDLE); cCharacter := ' '; New(pCsbi); GetConsoleScreenBufferInfo(hConsoleOutput, pCsbi^); wAttribute := pCsbi^.wAttributes; nLength := pCsbi^.dwSize.X * pCsbi^.dwSize.Y; Dispose(pCsbi); dwWriteCoord.X := 0; dwWriteCoord.Y := 0; FillConsoleOutputCharacter(hConsoleOutput, cCharacter, nLength, dwWriteCoord, lpWritten); FillConsoleOutputAttribute(hConsoleOutput, wAttribute, nLength, dwWriteCoord, lpWritten); SetConsoleCursorPosition(hConsoleOutput, dwWriteCoord); end; procedure FlushInputBuffer; begin FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); end; function GetTextBackground: byte; begin Result := GetScreenInfo.wAttributes AND bWhite; end; function GetTextColor: byte; begin Result := GetScreenInfo.wAttributes AND fWhite; end; function GetScreenInfo: TConsoleScreenBufferInfo; begin GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), Result); end; Procedure GotoXY(X, Y: integer); var CoordCursor: TCoord; begin CoordCursor.X := X - 1; CoordCursor.Y := Y - 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), CoordCursor); end; Procedure HighVideo; var Attribute: word; begin Attribute := GetScreenInfo.wAttributes; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), Attribute OR FOREGROUND_INTENSITY); end; Procedure HighVideoBackground; var Attribute: word; begin Attribute := GetScreenInfo.wAttributes; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), Attribute OR BACKGROUND_INTENSITY); end; Function InputEvent: word; var hConsoleInput: THandle; pInput: pInputBuffer; lpNumberOfEvents: dword; lpNumberRead: integer; i: word; const KeysToSkip: set of byte = [VK_SHIFT, VK_CONTROL, VK_MENU, VK_CAPITAL, VK_NUMLOCK, VK_SCROLL]; begin hConsoleInput := GetStdHandle(STD_INPUT_HANDLE); GetNumberOfConsoleInputEvents(hConsoleInput, lpNumberOfEvents); Result := NO_EVENT; if lpNumberOfEvents > 0 then try GetMem(pInput, lpNumberOfEvents * SizeOf(TInputRecord)); PeekConsoleInput(hConsoleInput, pInput^[0], lpNumberOfEvents, lpNumberRead); i := 0; repeat with pInput^[i] do begin case EventType of KEY_EVENT: if (KeyEvent.bKeyDown = false) and not (KeyEvent.wVirtualKeyCode in KeysToSkip) then Result := EventType else Result := KEY_EVENT_IN_PROGRESS; _MOUSE_EVENT: if (MouseEvent.dwEventFlags <> MOUSE_MOVED) then Result := EventType else Result := _MOUSE_EVENT_IN_PROGRESS; else Result := EventType; end; end; inc(i); until (Result <> NO_EVENT) or (i >= lpNumberOfEvents); finally FreeMem(pInput); end; end; Function KeyPressed: boolean; var hConsoleInput: THandle; pInput: pInputBuffer; lpNumberOfEvents: dword; lpNumberRead: integer; i: word; const KeysToSkip: set of byte = [VK_SHIFT, VK_CONTROL, VK_MENU, VK_CAPITAL, VK_NUMLOCK, VK_SCROLL]; begin hConsoleInput := GetStdHandle(STD_INPUT_HANDLE); GetNumberOfConsoleInputEvents(hConsoleInput, lpNumberOfEvents); Result := FALSE; if lpNumberOfEvents > 0 then try GetMem(pInput, lpNumberOfEvents * SizeOf(TInputRecord)); PeekConsoleInput(hConsoleInput, pInput^[0], lpNumberOfEvents, lpNumberRead); i := 0; repeat with pInput^[i] do begin if EventType = KEY_EVENT then Result := (KeyEvent.bKeyDown = false) and not (KeyEvent.wVirtualKeyCode in KeysToSkip); end; inc(i); until (Result = TRUE) or (i >= lpNumberOfEvents); finally FreeMem(pInput); end; end; Procedure LowVideo; var Attribute: word; begin Attribute := GetScreenInfo.wAttributes; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), Attribute AND NOT FOREGROUND_INTENSITY); end; Procedure LowVideoBackground; var Attribute: word; begin Attribute := GetScreenInfo.wAttributes; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), Attribute AND NOT BACKGROUND_INTENSITY); end; Procedure NormVideo; var Attribute: word; begin Attribute := GetScreenInfo.wAttributes; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), Attribute AND (fLightGray OR bWhite) OR StartTextIntensity); end; Procedure NormVideoBackground; var Attribute: word; begin Attribute := GetScreenInfo.wAttributes; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), Attribute AND (fWhite OR bLightGray) OR StartBackgroundIntensity); end; Function ReadKey: char; var hConsoleInput: THandle; pInput: pInputRecord; lpcRead: integer; AltKey, CtrlKey, ShiftKey: boolean; const ExtendedChar: boolean = false; Scancode: byte = 0; { Scancodes to skip: $1D - Ctrl keys $2A - left Shift key $36 - right Shift key $38 - Alt keys $3A - Caps lock key $45 - Num lock key $46 - Scroll lock key } ScanCodesToSkip: set of 0..255 = [$1D, $2A, $36, $38, $3A, $45, $46]; begin if not ExtendedChar then begin hConsoleInput := GetStdHandle(STD_INPUT_HANDLE); try New(pInput); with pInput^.KeyEvent do begin Repeat ReadConsoleInput(hConsoleInput, pInput^, 1, lpcRead); until (pInput^.EventType = KEY_EVENT) and (bKeyDown = false) and not (wVirtualScanCode in ScanCodesToSkip); { Get state of control keys } AltKey := ((dwControlKeyState AND (RIGHT_ALT_PRESSED OR LEFT_ALT_PRESSED)) > 0); CtrlKey := ((dwControlKeyState AND (RIGHT_CTRL_PRESSED OR LEFT_CTRL_PRESSED)) > 0); ShiftKey := ((dwControlKeyState AND SHIFT_PRESSED) > 0); { Get key value, making some corrections to comply with MSDOS} if AltKey then Result := #0 else begin Result := AsciiChar; if CtrlKey then case wVirtualScanCode of $07: Result := #$1E; // ^_6 (Win32 gives ASCII = 0) $0C: Result := #$1F; // ^_- (Win32 gives ASCII = 0) end else if ShiftKey then case wVirtualScanCode of $01: Result := #$1B; // Shift Esc (Win32 gives ASCII = 0) $0F: Result := #0; // Shift Tab (Win32 gives ASCII = 9) end; end; {Save scancode of non-ASCII keys for second call} if (Result = #0) then begin ExtendedChar := true; ScanCode := RemapScanCode(wVirtualScanCode, dwControlKeyState); end; end; finally Dispose(pInput); end; end else begin Result := char(ScanCode); ExtendedChar := false; end; end; Function RemapScanCode (ScanCode: byte; CtrlKeyState: byte): byte; { Several remappings of scancodes are necessary to comply with what we get with MSDOS. Special Windows keys, as Alt-Tab, Ctrl-Esc etc. are excluded } var AltKey, CtrlKey, ShiftKey: boolean; const { Keypad key scancodes: Ctrl Norm $77 $47 - Home $8D $48 - Up arrow $84 $49 - PgUp $8E $4A - - $73 $4B - Left Arrow $8F $4C - 5 $74 $4D - Right arrow $4E $4E - + $75 $4F - End $91 $50 - Down arrow $76 $51 - PgDn $92 $52 - Ins $93 $53 - Del } CtrlKeypadKeys: array[$47..$53] of byte = ($77, $8D, $84, $8E, $73, $8F, $74, $4E, $75, $91, $76, $92, $93); begin AltKey := ((CtrlKeyState AND (RIGHT_ALT_PRESSED OR LEFT_ALT_PRESSED)) > 0); CtrlKey := ((CtrlKeyState AND (RIGHT_CTRL_PRESSED OR LEFT_CTRL_PRESSED)) > 0); ShiftKey := ((CtrlKeyState AND SHIFT_PRESSED) > 0); if AltKey then case ScanCode of // Digits, -, = $02..$0D: inc(ScanCode, $76); // Function keys $3B..$44: inc(Scancode, $2D); $57..$58: inc(Scancode, $34); // Extended cursor block keys $47..$49, $4B, $4D, $4F..$53: inc(Scancode, $50); // Other keys $1C: Scancode := $A6; // Enter $35: Scancode := $A4; // / (keypad and normal!) end else if CtrlKey then case Scancode of // Tab key $0F: Scancode := $94; // Function keys $3B..$44: inc(Scancode, $23); $57..$58: inc(Scancode, $32); // Keypad keys $35: Scancode := $95; // \ $37: Scancode := $96; // * $47..$53: Scancode := CtrlKeypadKeys[Scancode]; end else if ShiftKey then case Scancode of // Function keys $3B..$44: inc(Scancode, $19); $57..$58: inc(Scancode, $30); end else case Scancode of // Function keys $57..$58: inc(Scancode, $2E); // F11 and F12 end; Result := ScanCode; end; Procedure TextBackground (Color: Byte); var Background, Foreground: byte; begin Background := Color AND bWhite; Foreground := GetScreenInfo.wAttributes AND fWhite; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), Background OR Foreground); end; Procedure TextColor (Color: Byte); var Background, Foreground: byte; begin Background := GetScreenInfo.wAttributes AND bWhite; Foreground := Color AND fWhite; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), Background OR Foreground); end; Function WhereX: integer; begin Result := GetScreenInfo.dwCursorPosition.X + 1; end; Function WhereY: integer; begin Result := GetScreenInfo.dwCursorPosition.Y + 1; end; Function WinReadKey: WinReadKeyRecord; var hConsoleInput: THandle; pInput: pInputRecord; lpcRead: integer; const KeysToSkip: set of byte = [VK_SHIFT, VK_CONTROL, VK_MENU, VK_CAPITAL, VK_NUMLOCK, VK_SCROLL]; begin hConsoleInput := GetStdHandle(STD_INPUT_HANDLE); try New(pInput); with pInput^.KeyEvent do begin Repeat ReadConsoleInput(hConsoleInput, pInput^, 1, lpcRead); until (pInput^.EventType = KEY_EVENT) and (bKeyDown = TRUE) and not (wVirtualKeyCode in KeysToSkip); { Get key value } with Result do begin KeyStatus := 0; AsciiChar := pInput^.KeyEvent.AsciiChar; KeyCode := wVirtualKeyCode; { Set bits 0..2 of KeyStatus to indicate control key state} if ((dwControlKeyState AND SHIFT_PRESSED) > 0) then KeyStatus := (KeyStatus OR $01); if ((dwControlKeyState AND (RIGHT_CTRL_PRESSED OR LEFT_CTRL_PRESSED)) > 0) then KeyStatus := (KeyStatus OR $02); if ((dwControlKeyState AND (RIGHT_ALT_PRESSED OR LEFT_ALT_PRESSED)) > 0) then KeyStatus := (KeyStatus OR $04); end; end; finally Dispose(pInput); end; end; begin New(pCsbi); pCsbi^ := GetScreenInfo; StartTextIntensity := pCsbi^.wAttributes AND FOREGROUND_INTENSITY; StartBackgroundIntensity := pCsbi^.wAttributes AND BACKGROUND_INTENSITY; Dispose(pCsbi); end.