crt.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. unit WinCrt;
  2. interface
  3. Uses Windows;
  4. type
  5. WinReadKeyRecord = record
  6. KeyStatus: byte;
  7. AsciiChar: char;
  8. KeyCode: word;
  9. end;
  10. const
  11. // Foreground color constants
  12. fBlack = 0;
  13. fBlue = FOREGROUND_BLUE;
  14. fGreen = FOREGROUND_GREEN;
  15. fCyan = FOREGROUND_BLUE OR FOREGROUND_GREEN;
  16. fRed = FOREGROUND_RED;
  17. fMagenta = FOREGROUND_BLUE OR FOREGROUND_RED;
  18. fBrown = FOREGROUND_GREEN OR FOREGROUND_RED;
  19. fLightGray = FOREGROUND_BLUE OR FOREGROUND_GREEN OR FOREGROUND_RED;
  20. fDarkGray = fBlack OR FOREGROUND_INTENSITY;
  21. fLightBlue = fBlue OR FOREGROUND_INTENSITY;
  22. fLightGreen = fGreen OR FOREGROUND_INTENSITY;
  23. fLightCyan = fCyan OR FOREGROUND_INTENSITY;
  24. fLightRed = fRed OR FOREGROUND_INTENSITY;
  25. fLightMagenta = fMagenta OR FOREGROUND_INTENSITY;
  26. fYellow = fBrown OR FOREGROUND_INTENSITY;
  27. fWhite = fLightGray OR FOREGROUND_INTENSITY;
  28. // Background color constants
  29. bBlack = 0;
  30. bBlue = BACKGROUND_BLUE;
  31. bGreen = BACKGROUND_GREEN;
  32. bCyan = BACKGROUND_BLUE OR BACKGROUND_GREEN;
  33. bRed = BACKGROUND_RED;
  34. bMagenta = BACKGROUND_BLUE OR BACKGROUND_RED;
  35. bBrown = BACKGROUND_GREEN OR BACKGROUND_RED;
  36. bLightGray = BACKGROUND_BLUE OR BACKGROUND_GREEN OR BACKGROUND_RED;
  37. bDarkGray = bBlack OR BACKGROUND_INTENSITY;
  38. bLightBlue = bBlue OR BACKGROUND_INTENSITY;
  39. bLightGreen = bGreen OR BACKGROUND_INTENSITY;
  40. bLightCyan = bCyan OR BACKGROUND_INTENSITY;
  41. bLightRed = bRed OR BACKGROUND_INTENSITY;
  42. bLightMagenta = bMagenta OR BACKGROUND_INTENSITY;
  43. bYellow = bBrown OR BACKGROUND_INTENSITY;
  44. bWhite = bLightGray OR BACKGROUND_INTENSITY;
  45. // Constants designating input events
  46. NO_EVENT = 0;
  47. KEY_EVENT_IN_PROGRESS = $100;
  48. _MOUSE_EVENT_IN_PROGRESS = $200;
  49. procedure ClrEol;
  50. { Clears all characters from cursor position to end of line without
  51. moving the cursor by filling character cells with blanks
  52. and attribute cells with the current screen buffer attribute.
  53. }
  54. procedure ClrScr;
  55. { Clears screen buffer by filling character cells with blanks
  56. and attribute cells with the current screen buffer attribute.
  57. The cursor is positioned in the top left corner of the screen
  58. buffer
  59. }
  60. procedure FlushInputBuffer;
  61. function GetTextBackground: byte;
  62. function GetTextColor: byte;
  63. Procedure GotoXY(X, Y: integer);
  64. Procedure HighVideo;
  65. Procedure HighVideoBackground;
  66. Function InputEvent: word;
  67. { Returns
  68. NO_EVENT if input buffer is empty ;
  69. KEY_EVENT if there is a pending key event with
  70. key released again,
  71. and key is not one of the control keys;
  72. KEY_EVENT_IN_PROGRESS if there is another pending key event;
  73. _MOUSE_EVENT if there is a pending mouse event
  74. without moving the mouse;
  75. _MOUSE_EVENT_IN_PROGRESS if there is another pending mouse event;
  76. WINDOW_BUFFER_SIZE_EVENT is the user resized the screen buffer
  77. and window input is enabled (default mode disabled).
  78. }
  79. function KeyPressed: boolean;
  80. { Returns
  81. TRUE if there is a pending key event with
  82. key released again,
  83. and key is not one of the control keys;
  84. FALSE otherwise.
  85. }
  86. Procedure LowVideo;
  87. Procedure LowVideoBackground;
  88. Procedure NormVideo;
  89. Procedure NormVideoBackground;
  90. Function ReadKey: char;
  91. Procedure TextBackground (Color: Byte);
  92. Procedure TextColor (Color: Byte);
  93. Function WhereX: integer;
  94. Function WhereY: integer;
  95. Function WinReadKey: WinReadKeyRecord;
  96. { Return value in KeyStatus element:
  97. - bit 0: shift key pressed
  98. - bit 1: ctrl key pressed
  99. - bit 2: alt key pressed
  100. The KeyCode element has the virtual key code.
  101. N.B. nog regelen: extended ASCII via Alt-keypad toetsen.
  102. }
  103. implementation
  104. type
  105. PInputBuffer = ^TInputBuffer;
  106. TInputBuffer = array[word] of TInputRecord;
  107. var
  108. StartTextIntensity, StartBackgroundIntensity: byte;
  109. pCsbi: PConsoleScreenBufferInfo;
  110. function GetScreenInfo: TConsoleScreenBufferInfo; forward;
  111. Function RemapScanCode (ScanCode: byte; CtrlKeyState: byte): byte; forward;
  112. procedure ClrEol;
  113. var
  114. hConsoleOutput: THandle;
  115. cCharacter: Char;
  116. wAttribute: word;
  117. nLength: dword;
  118. dwWriteCoord: TCoord;
  119. lpWritten: dword;
  120. begin
  121. hConsoleOutput := GetStdHandle(STD_OUTPUT_HANDLE);
  122. cCharacter := ' ';
  123. New(pCsbi);
  124. GetConsoleScreenBufferInfo(hConsoleOutput, pCsbi^);
  125. wAttribute := pCsbi^.wAttributes;
  126. nLength := pCsbi^.dwSize.X - pCsbi^.dwCursorPosition.X + 1;
  127. dwWriteCoord.X := pCsbi^.dwCursorPosition.X;
  128. dwWriteCoord.Y := pCsbi^.dwCursorPosition.Y;
  129. Dispose(pCsbi);
  130. FillConsoleOutputCharacter(hConsoleOutput, cCharacter, nLength,
  131. dwWriteCoord, lpWritten);
  132. FillConsoleOutputAttribute(hConsoleOutput, wAttribute, nLength,
  133. dwWriteCoord, lpWritten);
  134. end;
  135. procedure ClrScr;
  136. var
  137. hConsoleOutput: THandle;
  138. cCharacter: Char;
  139. wAttribute: word;
  140. nLength: dword;
  141. dwWriteCoord: TCoord;
  142. lpWritten: dword;
  143. begin
  144. hConsoleOutput := GetStdHandle(STD_OUTPUT_HANDLE);
  145. cCharacter := ' ';
  146. New(pCsbi);
  147. GetConsoleScreenBufferInfo(hConsoleOutput, pCsbi^);
  148. wAttribute := pCsbi^.wAttributes;
  149. nLength := pCsbi^.dwSize.X * pCsbi^.dwSize.Y;
  150. Dispose(pCsbi);
  151. dwWriteCoord.X := 0;
  152. dwWriteCoord.Y := 0;
  153. FillConsoleOutputCharacter(hConsoleOutput, cCharacter, nLength,
  154. dwWriteCoord, lpWritten);
  155. FillConsoleOutputAttribute(hConsoleOutput, wAttribute, nLength,
  156. dwWriteCoord, lpWritten);
  157. SetConsoleCursorPosition(hConsoleOutput, dwWriteCoord);
  158. end;
  159. procedure FlushInputBuffer;
  160. begin
  161. FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
  162. end;
  163. function GetTextBackground: byte;
  164. begin
  165. Result := GetScreenInfo.wAttributes AND bWhite;
  166. end;
  167. function GetTextColor: byte;
  168. begin
  169. Result := GetScreenInfo.wAttributes AND fWhite;
  170. end;
  171. function GetScreenInfo: TConsoleScreenBufferInfo;
  172. begin
  173. GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), Result);
  174. end;
  175. Procedure GotoXY(X, Y: integer);
  176. var
  177. CoordCursor: TCoord;
  178. begin
  179. CoordCursor.X := X - 1;
  180. CoordCursor.Y := Y - 1;
  181. SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), CoordCursor);
  182. end;
  183. Procedure HighVideo;
  184. var
  185. Attribute: word;
  186. begin
  187. Attribute := GetScreenInfo.wAttributes;
  188. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  189. Attribute OR FOREGROUND_INTENSITY);
  190. end;
  191. Procedure HighVideoBackground;
  192. var
  193. Attribute: word;
  194. begin
  195. Attribute := GetScreenInfo.wAttributes;
  196. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  197. Attribute OR BACKGROUND_INTENSITY);
  198. end;
  199. Function InputEvent: word;
  200. var
  201. hConsoleInput: THandle;
  202. pInput: pInputBuffer;
  203. lpNumberOfEvents: dword;
  204. lpNumberRead: integer;
  205. i: word;
  206. const
  207. KeysToSkip: set of byte =
  208. [VK_SHIFT, VK_CONTROL, VK_MENU, VK_CAPITAL, VK_NUMLOCK, VK_SCROLL];
  209. begin
  210. hConsoleInput := GetStdHandle(STD_INPUT_HANDLE);
  211. GetNumberOfConsoleInputEvents(hConsoleInput, lpNumberOfEvents);
  212. Result := NO_EVENT;
  213. if lpNumberOfEvents > 0 then
  214. try
  215. GetMem(pInput, lpNumberOfEvents * SizeOf(TInputRecord));
  216. PeekConsoleInput(hConsoleInput, pInput^[0], lpNumberOfEvents, lpNumberRead);
  217. i := 0;
  218. repeat
  219. with pInput^[i] do begin
  220. case EventType of
  221. KEY_EVENT:
  222. if (KeyEvent.bKeyDown = false) and
  223. not (KeyEvent.wVirtualKeyCode in KeysToSkip) then
  224. Result := EventType
  225. else
  226. Result := KEY_EVENT_IN_PROGRESS;
  227. _MOUSE_EVENT:
  228. if (MouseEvent.dwEventFlags <> MOUSE_MOVED) then
  229. Result := EventType
  230. else
  231. Result := _MOUSE_EVENT_IN_PROGRESS;
  232. else
  233. Result := EventType;
  234. end;
  235. end;
  236. inc(i);
  237. until (Result <> NO_EVENT) or (i >= lpNumberOfEvents);
  238. finally
  239. FreeMem(pInput);
  240. end;
  241. end;
  242. Function KeyPressed: boolean;
  243. var
  244. hConsoleInput: THandle;
  245. pInput: pInputBuffer;
  246. lpNumberOfEvents: dword;
  247. lpNumberRead: integer;
  248. i: word;
  249. const
  250. KeysToSkip: set of byte =
  251. [VK_SHIFT, VK_CONTROL, VK_MENU, VK_CAPITAL, VK_NUMLOCK, VK_SCROLL];
  252. begin
  253. hConsoleInput := GetStdHandle(STD_INPUT_HANDLE);
  254. GetNumberOfConsoleInputEvents(hConsoleInput, lpNumberOfEvents);
  255. Result := FALSE;
  256. if lpNumberOfEvents > 0 then
  257. try
  258. GetMem(pInput, lpNumberOfEvents * SizeOf(TInputRecord));
  259. PeekConsoleInput(hConsoleInput, pInput^[0], lpNumberOfEvents, lpNumberRead);
  260. i := 0;
  261. repeat
  262. with pInput^[i] do begin
  263. if EventType = KEY_EVENT then
  264. Result := (KeyEvent.bKeyDown = false) and
  265. not (KeyEvent.wVirtualKeyCode in KeysToSkip);
  266. end;
  267. inc(i);
  268. until (Result = TRUE) or (i >= lpNumberOfEvents);
  269. finally
  270. FreeMem(pInput);
  271. end;
  272. end;
  273. Procedure LowVideo;
  274. var
  275. Attribute: word;
  276. begin
  277. Attribute := GetScreenInfo.wAttributes;
  278. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  279. Attribute AND NOT FOREGROUND_INTENSITY);
  280. end;
  281. Procedure LowVideoBackground;
  282. var
  283. Attribute: word;
  284. begin
  285. Attribute := GetScreenInfo.wAttributes;
  286. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  287. Attribute AND NOT BACKGROUND_INTENSITY);
  288. end;
  289. Procedure NormVideo;
  290. var
  291. Attribute: word;
  292. begin
  293. Attribute := GetScreenInfo.wAttributes;
  294. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  295. Attribute AND (fLightGray OR bWhite) OR StartTextIntensity);
  296. end;
  297. Procedure NormVideoBackground;
  298. var
  299. Attribute: word;
  300. begin
  301. Attribute := GetScreenInfo.wAttributes;
  302. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  303. Attribute AND (fWhite OR bLightGray) OR StartBackgroundIntensity);
  304. end;
  305. Function ReadKey: char;
  306. var
  307. hConsoleInput: THandle;
  308. pInput: pInputRecord;
  309. lpcRead: integer;
  310. AltKey, CtrlKey, ShiftKey: boolean;
  311. const
  312. ExtendedChar: boolean = false;
  313. Scancode: byte = 0;
  314. {
  315. Scancodes to skip:
  316. $1D - Ctrl keys
  317. $2A - left Shift key
  318. $36 - right Shift key
  319. $38 - Alt keys
  320. $3A - Caps lock key
  321. $45 - Num lock key
  322. $46 - Scroll lock key
  323. }
  324. ScanCodesToSkip: set of 0..255 =
  325. [$1D, $2A, $36, $38, $3A, $45, $46];
  326. begin
  327. if not ExtendedChar then begin
  328. hConsoleInput := GetStdHandle(STD_INPUT_HANDLE);
  329. try
  330. New(pInput);
  331. with pInput^.KeyEvent do begin
  332. Repeat
  333. ReadConsoleInput(hConsoleInput, pInput^, 1, lpcRead);
  334. until (pInput^.EventType = KEY_EVENT)
  335. and (bKeyDown = false)
  336. and not (wVirtualScanCode in ScanCodesToSkip);
  337. { Get state of control keys }
  338. AltKey := ((dwControlKeyState AND
  339. (RIGHT_ALT_PRESSED OR LEFT_ALT_PRESSED)) > 0);
  340. CtrlKey := ((dwControlKeyState AND
  341. (RIGHT_CTRL_PRESSED OR LEFT_CTRL_PRESSED)) > 0);
  342. ShiftKey := ((dwControlKeyState AND SHIFT_PRESSED) > 0);
  343. { Get key value, making some corrections to comply with MSDOS}
  344. if AltKey then
  345. Result := #0
  346. else begin
  347. Result := AsciiChar;
  348. if CtrlKey then
  349. case wVirtualScanCode of
  350. $07: Result := #$1E; // ^_6 (Win32 gives ASCII = 0)
  351. $0C: Result := #$1F; // ^_- (Win32 gives ASCII = 0)
  352. end
  353. else if ShiftKey then
  354. case wVirtualScanCode of
  355. $01: Result := #$1B; // Shift Esc (Win32 gives ASCII = 0)
  356. $0F: Result := #0; // Shift Tab (Win32 gives ASCII = 9)
  357. end;
  358. end;
  359. {Save scancode of non-ASCII keys for second call}
  360. if (Result = #0) then begin
  361. ExtendedChar := true;
  362. ScanCode := RemapScanCode(wVirtualScanCode, dwControlKeyState);
  363. end;
  364. end;
  365. finally
  366. Dispose(pInput);
  367. end;
  368. end
  369. else begin
  370. Result := char(ScanCode);
  371. ExtendedChar := false;
  372. end;
  373. end;
  374. Function RemapScanCode (ScanCode: byte; CtrlKeyState: byte): byte;
  375. { Several remappings of scancodes are necessary to comply with what
  376. we get with MSDOS. Special Windows keys, as Alt-Tab, Ctrl-Esc etc.
  377. are excluded }
  378. var
  379. AltKey, CtrlKey, ShiftKey: boolean;
  380. const
  381. {
  382. Keypad key scancodes:
  383. Ctrl Norm
  384. $77 $47 - Home
  385. $8D $48 - Up arrow
  386. $84 $49 - PgUp
  387. $8E $4A - -
  388. $73 $4B - Left Arrow
  389. $8F $4C - 5
  390. $74 $4D - Right arrow
  391. $4E $4E - +
  392. $75 $4F - End
  393. $91 $50 - Down arrow
  394. $76 $51 - PgDn
  395. $92 $52 - Ins
  396. $93 $53 - Del
  397. }
  398. CtrlKeypadKeys: array[$47..$53] of byte =
  399. ($77, $8D, $84, $8E, $73, $8F, $74, $4E, $75, $91, $76, $92, $93);
  400. begin
  401. AltKey := ((CtrlKeyState AND
  402. (RIGHT_ALT_PRESSED OR LEFT_ALT_PRESSED)) > 0);
  403. CtrlKey := ((CtrlKeyState AND
  404. (RIGHT_CTRL_PRESSED OR LEFT_CTRL_PRESSED)) > 0);
  405. ShiftKey := ((CtrlKeyState AND SHIFT_PRESSED) > 0);
  406. if AltKey then
  407. case ScanCode of
  408. // Digits, -, =
  409. $02..$0D: inc(ScanCode, $76);
  410. // Function keys
  411. $3B..$44: inc(Scancode, $2D);
  412. $57..$58: inc(Scancode, $34);
  413. // Extended cursor block keys
  414. $47..$49, $4B, $4D, $4F..$53:
  415. inc(Scancode, $50);
  416. // Other keys
  417. $1C: Scancode := $A6; // Enter
  418. $35: Scancode := $A4; // / (keypad and normal!)
  419. end
  420. else if CtrlKey then
  421. case Scancode of
  422. // Tab key
  423. $0F: Scancode := $94;
  424. // Function keys
  425. $3B..$44: inc(Scancode, $23);
  426. $57..$58: inc(Scancode, $32);
  427. // Keypad keys
  428. $35: Scancode := $95; // \
  429. $37: Scancode := $96; // *
  430. $47..$53: Scancode := CtrlKeypadKeys[Scancode];
  431. end
  432. else if ShiftKey then
  433. case Scancode of
  434. // Function keys
  435. $3B..$44: inc(Scancode, $19);
  436. $57..$58: inc(Scancode, $30);
  437. end
  438. else
  439. case Scancode of
  440. // Function keys
  441. $57..$58: inc(Scancode, $2E); // F11 and F12
  442. end;
  443. Result := ScanCode;
  444. end;
  445. Procedure TextBackground (Color: Byte);
  446. var
  447. Background, Foreground: byte;
  448. begin
  449. Background := Color AND bWhite;
  450. Foreground := GetScreenInfo.wAttributes AND fWhite;
  451. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  452. Background OR Foreground);
  453. end;
  454. Procedure TextColor (Color: Byte);
  455. var
  456. Background, Foreground: byte;
  457. begin
  458. Background := GetScreenInfo.wAttributes AND bWhite;
  459. Foreground := Color AND fWhite;
  460. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  461. Background OR Foreground);
  462. end;
  463. Function WhereX: integer;
  464. begin
  465. Result := GetScreenInfo.dwCursorPosition.X + 1;
  466. end;
  467. Function WhereY: integer;
  468. begin
  469. Result := GetScreenInfo.dwCursorPosition.Y + 1;
  470. end;
  471. Function WinReadKey: WinReadKeyRecord;
  472. var
  473. hConsoleInput: THandle;
  474. pInput: pInputRecord;
  475. lpcRead: integer;
  476. const
  477. KeysToSkip: set of byte =
  478. [VK_SHIFT, VK_CONTROL, VK_MENU, VK_CAPITAL, VK_NUMLOCK, VK_SCROLL];
  479. begin
  480. hConsoleInput := GetStdHandle(STD_INPUT_HANDLE);
  481. try
  482. New(pInput);
  483. with pInput^.KeyEvent do begin
  484. Repeat
  485. ReadConsoleInput(hConsoleInput, pInput^, 1, lpcRead);
  486. until (pInput^.EventType = KEY_EVENT)
  487. and (bKeyDown = TRUE)
  488. and not (wVirtualKeyCode in KeysToSkip);
  489. { Get key value }
  490. with Result do begin
  491. KeyStatus := 0;
  492. AsciiChar := pInput^.KeyEvent.AsciiChar;
  493. KeyCode := wVirtualKeyCode;
  494. { Set bits 0..2 of KeyStatus to indicate control key state}
  495. if ((dwControlKeyState AND SHIFT_PRESSED) > 0) then
  496. KeyStatus := (KeyStatus OR $01);
  497. if ((dwControlKeyState AND
  498. (RIGHT_CTRL_PRESSED OR LEFT_CTRL_PRESSED)) > 0) then
  499. KeyStatus := (KeyStatus OR $02);
  500. if ((dwControlKeyState AND
  501. (RIGHT_ALT_PRESSED OR LEFT_ALT_PRESSED)) > 0) then
  502. KeyStatus := (KeyStatus OR $04);
  503. end;
  504. end;
  505. finally
  506. Dispose(pInput);
  507. end;
  508. end;
  509. begin
  510. New(pCsbi);
  511. pCsbi^ := GetScreenInfo;
  512. StartTextIntensity := pCsbi^.wAttributes AND FOREGROUND_INTENSITY;
  513. StartBackgroundIntensity := pCsbi^.wAttributes AND BACKGROUND_INTENSITY;
  514. Dispose(pCsbi);
  515. end.