Quick.Console.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. { ***************************************************************************
  2. Copyright (c) 2016-2018 Kike Pérez
  3. Unit : Quick.Console
  4. Description : Console output with colors and optional file log
  5. Author : Kike Pérez
  6. Version : 1.8
  7. Created : 10/05/2017
  8. Modified : 09/03/2018
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Console;
  22. {$i QuickLib.inc}
  23. {$IFDEF CONDITIONALEXPRESSIONS}
  24. {$ifndef VER140}
  25. {$ifndef LINUX}
  26. {$define WITHUXTHEME}
  27. {$endif}
  28. {$endif}
  29. {$IFDEF DELPHI2005_UP}
  30. {$DEFINE INLINES}
  31. {$ENDIF}
  32. {$IF RTLVersion >= 14.0}
  33. {$DEFINE HASERROUTPUT}
  34. {$ENDIF}
  35. {$ENDIF}
  36. interface
  37. uses
  38. Classes,
  39. {$IFDEF MSWINDOWS}
  40. Windows,
  41. Messages,
  42. {$ELSE}
  43. {$IFDEF FPC}
  44. crt,
  45. {$ENDIF}
  46. {$ENDIF}
  47. {$IFDEF DELPHILINUX}
  48. Quick.SyncObjs.Linux.Compatibility,
  49. {$ENDIF}
  50. SysUtils,
  51. Quick.Commons,
  52. Quick.Log;
  53. type
  54. //text colors
  55. TConsoleColor = (
  56. ccBlack = 0,
  57. ccBlue = 1,
  58. ccGreen = 2,
  59. ccCyan = 3,
  60. ccRed = 4,
  61. ccMagenta = 5,
  62. ccBrown = 6,
  63. ccLightGray = 7,
  64. ccDarkGray = 8,
  65. ccLightBlue = 9,
  66. ccLightGreen = 10,
  67. ccLightCyan = 11,
  68. ccLightRed = 12,
  69. ccLightMagenta = 13,
  70. ccYellow = 14,
  71. ccWhite = 15);
  72. TConsoleProperties = record
  73. LogVerbose : TLogVerbose;
  74. Log : TQuickLog;
  75. end;
  76. {$IFNDEF FPC}
  77. TOutputProc<T> = reference to procedure(const aLine : T);
  78. TExecuteProc = reference to procedure;
  79. {$ELSE}
  80. TOutputProc<T> = procedure(const aLine : T) of object;
  81. TExecuteProc = procedure of object;
  82. {$ENDIF}
  83. {$IF DEFINED(FPCLINUX) OR DEFINED(DELPHILINUX)}
  84. {$IFDEF DELPHILINUX}
  85. tcrtcoord = Byte;
  86. {$ENDIF}
  87. TCoord = record
  88. X : tcrtcoord;
  89. Y : tcrtcoord;
  90. end;
  91. TSmallRect = record
  92. Left : Byte;
  93. Top : Byte;
  94. Right : Byte;
  95. Bottom : Byte;
  96. end;
  97. {$ENDIF}
  98. {$IFDEF MSWINDOWS}
  99. TConsoleMenuOption = record
  100. private
  101. fCaption : string;
  102. fKey : Word;
  103. fOnKeyPressed : TExecuteProc;
  104. public
  105. property Caption : string read fCaption write fCaption;
  106. property Key : Word read fKey write fKey;
  107. property OnKeyPressed : TExecuteProc read fOnKeyPressed write fOnKeyPressed;
  108. procedure DoKeyPressed;
  109. end;
  110. TConsoleMenu = class
  111. private
  112. fConsoleMenu : array of TConsoleMenuOption;
  113. fMenuColor : TConsoleColor;
  114. fIsActive : Boolean;
  115. procedure WriteMenu;
  116. public
  117. constructor Create;
  118. property MenuColor : TConsoleColor read fMenuColor write fMenuColor;
  119. property IsActive : Boolean read fIsActive;
  120. procedure AddMenu(const cMenuCaption : string; const cMenuKey : Word; MenuAction : TExecuteProc); overload;
  121. procedure AddMenu(MenuOption : TConsoleMenuOption); overload;
  122. procedure Refresh(aClearScreen : Boolean = False);
  123. procedure WaitForKeys;
  124. end;
  125. {$ENDIF}
  126. procedure cout(const cMsg : Integer; cEventType : TLogEventType); overload;
  127. procedure cout(const cMsg : Double; cEventType : TLogEventType); overload;
  128. procedure cout(const cMsg : string; cEventType : TLogEventType); overload;
  129. procedure cout(const cMsg : string; cColor : TConsoleColor); overload;
  130. procedure cout(const cMsg : string; params : array of const; cEventType : TLogEventType); overload;
  131. procedure coutXY(x,y : Integer; const cMsg : string; cEventType : TLogEventType); overload;
  132. procedure coutXY(x,y : Integer; const cMsg : string; cColor : TConsoleColor); overload;
  133. procedure coutXY(x,y : Integer; const cMsg : string; params : array of const; cEventType : TLogEventType); overload;
  134. procedure coutXY(x,y : Integer; const cMsg : string; params : array of const; cColor : TConsoleColor); overload;
  135. procedure coutTL(const cMsg : string; cEventType : TLogEventType); overload;
  136. procedure coutTL(const cMsg : string; cColor : TConsoleColor); overload;
  137. procedure coutBL(const cMsg : string; cEventType : TLogEventType); overload;
  138. procedure coutBL(const cMsg : string; cColor : TConsoleColor); overload;
  139. procedure coutFmt(const cMsg : string; params : array of const; cEventType : TLogEventType);
  140. procedure TextColor(Color: TConsoleColor); overload;
  141. procedure TextColor(Color: Byte); overload;
  142. procedure TextBackground(Color: TConsoleColor); overload;
  143. procedure TextBackground(Color: Byte); overload;
  144. procedure ResetColors;
  145. {$IFDEF MSWINDOWS}
  146. procedure ConsoleResize(Width, Height : Integer);
  147. {$ENDIF}
  148. procedure ClearScreen;
  149. procedure ClearLine; overload;
  150. procedure ClearLine(Y : Integer); overload;
  151. procedure ShowCursor;
  152. procedure HideCursor;
  153. function GetCursorX: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  154. function GetCursorY: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  155. function GetCursorMaxBottom : Integer;
  156. procedure SetCursorPos(NewCoord : TCoord); overload;
  157. procedure SetCursorPos(x ,y : Integer); overload;
  158. {$IFDEF MSWINDOWS}
  159. procedure ProcessMessages;
  160. {$ENDIF}
  161. procedure ConsoleWaitForEnterKey;
  162. {$IFDEF MSWINDOWS}
  163. procedure RunConsoleCommand(const aCommand, aParameters : String; CallBack : TOutputProc<PAnsiChar> = nil; OutputLines : TStrings = nil);
  164. procedure InitConsole;
  165. {$ENDIF}
  166. var
  167. Console : TConsoleProperties;
  168. CSConsole : TRTLCriticalSection;
  169. LastMode : Word;
  170. DefConsoleColor : Byte;
  171. TextAttr : Byte;
  172. hStdOut: THandle;
  173. hStdErr: THandle;
  174. ConsoleRect: TSmallRect;
  175. {$IFDEF MSWINDOWS}
  176. ScreenBufInfo : TConsoleScreenBufferInfo;
  177. CursorInfo : TConsoleCursorInfo;
  178. {$ENDIF}
  179. implementation
  180. procedure cout(const cMsg : Integer; cEventType : TLogEventType);
  181. var
  182. FmtSets : TFormatSettings;
  183. begin
  184. try
  185. {$IFNDEF FPC}
  186. FmtSets := TFormatSettings.Create;
  187. {$ENDIF}
  188. FmtSets.ThousandSeparator := '.';
  189. FmtSets.DecimalSeparator := ',';
  190. cout(FormatFloat('0,',cMsg,FmtSets),cEventType);
  191. except
  192. cout(cMsg.ToString,cEventType);
  193. end;
  194. end;
  195. procedure cout(const cMsg : Double; cEventType : TLogEventType);
  196. var
  197. FmtSets : TFormatSettings;
  198. begin
  199. try
  200. {$IFNDEF FPC}
  201. FmtSets := TFormatSettings.Create;
  202. {$ENDIF}
  203. FmtSets.ThousandSeparator := '.';
  204. FmtSets.DecimalSeparator := ',';
  205. cout(FormatFloat('.0###,',cMsg,FmtSets),cEventType);
  206. except
  207. cout(cMsg.ToString,cEventType);
  208. end;
  209. end;
  210. procedure cout(const cMsg : string; cEventType : TLogEventType);
  211. begin
  212. if cEventType in Console.LogVerbose then
  213. begin
  214. EnterCriticalSection(CSConsole);
  215. try
  216. {$IFDEF MSWINDOWS}
  217. if hStdOut <> 0 then
  218. {$ENDIF}
  219. begin
  220. case cEventType of
  221. etError : TextColor(ccLightRed);
  222. etInfo : TextColor(ccWhite);
  223. etSuccess : TextColor(ccLightGreen);
  224. etWarning : TextColor(ccYellow);
  225. etDebug : TextColor(ccLightCyan);
  226. etTrace : TextColor(ccLightMagenta);
  227. else TextColor(ccWhite);
  228. end;
  229. Writeln(cMsg{$IFDEF LINUX} +#13{$ENDIF});
  230. TextColor(LastMode);
  231. end;
  232. finally
  233. LeaveCriticalSection(CSConsole);
  234. end;
  235. if Assigned(Console.Log) then Console.Log.Add(cMsg,cEventType);
  236. end;
  237. end;
  238. procedure cout(const cMsg : string; cColor : TConsoleColor);
  239. begin
  240. EnterCriticalSection(CSConsole);
  241. try
  242. {$IFDEF MSWINDOWS}
  243. if hStdOut <> 0 then
  244. {$ENDIF}
  245. begin
  246. TextColor(cColor);
  247. Writeln(cMsg{$IFDEF LINUX} +#13{$ENDIF});
  248. TextColor(LastMode);
  249. end;
  250. finally
  251. LeaveCriticalSection(CSConsole);
  252. end;
  253. end;
  254. procedure cout(const cMsg : string; params : array of const; cEventType : TLogEventType);
  255. begin
  256. cout(Format(cMsg,params),cEventType);
  257. end;
  258. function GetCursorX: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  259. {$IFDEF MSWINDOWS}
  260. var
  261. BufferInfo: TConsoleScreenBufferInfo;
  262. begin
  263. GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
  264. Result := BufferInfo.dwCursorPosition.X;
  265. end;
  266. {$ELSE}
  267. begin
  268. Result := WhereX;
  269. end;
  270. {$ENDIF}
  271. function GetCursorY: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  272. {$IFDEF MSWINDOWS}
  273. var
  274. BufferInfo: TConsoleScreenBufferInfo;
  275. begin
  276. GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
  277. Result := BufferInfo.dwCursorPosition.Y;
  278. end;
  279. {$ELSE}
  280. begin
  281. Result := WhereY;
  282. end;
  283. {$ENDIF}
  284. function GetCursorMaxBottom : Integer;
  285. {$IFDEF MSWINDOWS}
  286. var
  287. BufferInfo: TConsoleScreenBufferInfo;
  288. begin
  289. GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
  290. Result := BufferInfo.srWindow.Bottom;
  291. end;
  292. {$ELSE}
  293. begin
  294. Result := 80;
  295. end;
  296. {$ENDIF}
  297. procedure SetCursorPos(NewCoord : TCoord);
  298. begin
  299. {$IFDEF MSWINDOWS}
  300. SetConsoleCursorPosition(hStdOut, NewCoord);
  301. {$ELSE}
  302. GotoXY(NewCoord.X,NewCoord.Y);
  303. {$ENDIF}
  304. end;
  305. procedure SetCursorPos(x ,y : Integer);
  306. var
  307. NewCoord : TCoord;
  308. begin
  309. NewCoord.X := x;
  310. NewCoord.Y := y;
  311. SetCursorPos(NewCoord);
  312. end;
  313. procedure coutXY(x,y : Integer; const cMsg : string; cEventType : TLogEventType);
  314. var
  315. NewCoord : TCoord;
  316. LastCoord : TCoord;
  317. begin
  318. {$IFDEF MSWINDOWS}
  319. if hStdOut = 0 then Exit;
  320. {$ENDIF}
  321. LastCoord.X := GetCursorX;
  322. LastCoord.Y := GetCursorY;
  323. NewCoord.X := x;
  324. NewCoord.Y := y;
  325. ClearLine(Y);
  326. SetCursorPos(NewCoord);
  327. try
  328. cout(cMsg,cEventType);
  329. finally
  330. SetCursorPos(LastCoord);
  331. end;
  332. end;
  333. procedure coutXY(x,y : Integer; const cMsg : string; cColor : TConsoleColor); overload;
  334. var
  335. NewCoord : TCoord;
  336. LastCoord : TCoord;
  337. begin
  338. {$IFDEF MSWINDOWS}
  339. if hStdOut = 0 then Exit;
  340. {$ENDIF}
  341. LastCoord.X := GetCursorX;
  342. LastCoord.Y := GetCursorY;
  343. NewCoord.X := x;
  344. NewCoord.Y := y;
  345. ClearLine(Y);
  346. SetCursorPos(NewCoord);
  347. try
  348. cout(cMsg,cColor);
  349. finally
  350. SetCursorPos(LastCoord);
  351. end;
  352. end;
  353. procedure coutXY(x,y : Integer; const cMsg : string; params : array of const; cEventType : TLogEventType);
  354. begin
  355. coutXY(x,y,Format(cMsg,params),cEventType);
  356. end;
  357. procedure coutXY(x,y : Integer; const cMsg : string; params : array of const; cColor : TConsoleColor);
  358. begin
  359. coutXY(x,y,Format(cMsg,params),cColor);
  360. end;
  361. procedure coutTL(const cMsg : string; cEventType : TLogEventType);
  362. begin
  363. coutXY(0,0,cMsg,cEventType);
  364. end;
  365. procedure coutTL(const cMsg : string; cColor : TConsoleColor);
  366. begin
  367. coutXY(0,0,cMsg,cColor);
  368. end;
  369. procedure coutBL(const cMsg : string; cEventType : TLogEventType);
  370. begin
  371. coutXY(0,GetCursorMaxBottom - 1,cMsg,cEventType);
  372. end;
  373. procedure coutBL(const cMsg : string; cColor : TConsoleColor);
  374. begin
  375. coutXY(0,GetCursorMaxBottom - 1,cMsg,cColor);
  376. end;
  377. procedure coutFmt(const cMsg : string; params : array of const; cEventType : TLogEventType);
  378. begin
  379. cout(Format(cMsg,params),cEventType);
  380. end;
  381. procedure TextColor(Color: TConsoleColor);
  382. begin
  383. TextColor(Integer(Color));
  384. end;
  385. procedure TextColor(Color: Byte);
  386. begin
  387. {$IFDEF MSWINDOWS}
  388. if hStdOut = 0 then Exit;
  389. LastMode := TextAttr;
  390. TextAttr := (TextAttr and $F0) or (Color and $0F);
  391. if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
  392. {$ELSE}
  393. crt.TextColor(Color);
  394. {$ENDIF}
  395. end;
  396. procedure TextBackground(Color: TConsoleColor);
  397. begin
  398. TextBackground(Integer(Color));
  399. end;
  400. procedure TextBackground(Color: Byte);
  401. begin
  402. {$IFDEF MSWINDOWS}
  403. if hStdOut = 0 then Exit;
  404. LastMode := TextAttr;
  405. TextAttr := (TextAttr and $0F) or ((Color shl 4) and $F0);
  406. if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
  407. {$ELSE}
  408. crt.TextBackground(Color);
  409. {$ENDIF}
  410. end;
  411. procedure ResetColors;
  412. begin
  413. {$IFDEF MSWINDOWS}
  414. SetConsoleTextAttribute(hStdOut, DefConsoleColor);
  415. TextAttr := DefConsoleColor;
  416. {$ELSE}
  417. TextColor(DefConsoleColor);
  418. TextBackground(ccBlack);
  419. {$ENDIF}
  420. end;
  421. {$IFDEF MSWINDOWS}
  422. procedure ConsoleResize(Width, Height : Integer);
  423. var
  424. Rect: TSmallRect;
  425. Coord: TCoord;
  426. begin
  427. Rect.Left := 1;
  428. Rect.Top := 1;
  429. Rect.Right := Width;
  430. Rect.Bottom := Height;
  431. Coord.X := Rect.Right + 1 - Rect.Left;
  432. Coord.y := Rect.Bottom + 1 - Rect.Top;
  433. SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), Coord);
  434. SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), True, Rect);
  435. end;
  436. {$ENDIF}
  437. procedure ClearScreen;
  438. {$IFDEF MSWINDOWS}
  439. var
  440. stdout: THandle;
  441. bufinfo: TConsoleScreenBufferInfo;
  442. ConsoleSize: DWORD;
  443. NumWritten: DWORD;
  444. Origin: TCoord;
  445. begin
  446. stdout := GetStdHandle(STD_OUTPUT_HANDLE);
  447. if stdout<>INVALID_HANDLE_VALUE then
  448. begin
  449. GetConsoleScreenBufferInfo(stdout,bufinfo);
  450. ConsoleSize := bufinfo.dwSize.X * bufinfo.dwSize.Y;
  451. Origin.X := 0;
  452. Origin.Y := 0;
  453. FillConsoleOutputCharacter(stdout,' ',ConsoleSize,Origin,NumWritten);
  454. FillConsoleOutputAttribute(stdout,bufinfo.wAttributes,ConsoleSize,Origin,NumWritten);
  455. SetConsoleCursorPosition(stdout, Origin);
  456. end;
  457. end;
  458. {$ELSE}
  459. begin
  460. ClrScr;
  461. end;
  462. {$ENDIF}
  463. procedure ClearLine;
  464. begin
  465. ClearLine(GetCursorY);
  466. end;
  467. procedure ClearLine(Y : Integer);
  468. {$IFDEF MSWINDOWS}
  469. var
  470. dwWriteCoord: TCoord;
  471. dwCount, dwSize: DWord;
  472. begin
  473. if hStdOut = 0 then Exit;
  474. dwWriteCoord.X := 0;
  475. dwWriteCoord.Y := Y;
  476. dwSize := ConsoleRect.Right + 1;
  477. FillConsoleOutputAttribute(hStdOut, TextAttr, dwSize, dwWriteCoord, dwCount);
  478. FillConsoleOutputCharacter(hStdOut, ' ', dwSize, dwWriteCoord, dwCount);
  479. end;
  480. {$ELSE}
  481. begin
  482. GotoXY(1,Y);
  483. DelLine;
  484. GotoXY(1,Y);
  485. end;
  486. {$ENDIF}
  487. procedure ShowCursor;
  488. begin
  489. {$IFDEF MSWINDOWS}
  490. GetConsoleCursorInfo(hStdOut,CursorInfo);
  491. CursorInfo.bVisible := True;
  492. SetConsoleCursorInfo(hStdOut,CursorInfo);
  493. {$ELSE}
  494. CursorOn;
  495. {$ENDIF}
  496. end;
  497. procedure HideCursor;
  498. begin
  499. {$IFDEF MSWINDOWS}
  500. GetConsoleCursorInfo(hStdOut,CursorInfo);
  501. CursorInfo.bVisible := False;
  502. SetConsoleCursorInfo(hStdOut,CursorInfo);
  503. {$ELSE}
  504. CursorOff;
  505. {$ENDIF}
  506. end;
  507. function ConsoleKeyPressed(ExpectedKey: Word): Boolean;
  508. {$IFDEF MSWINDOWS}
  509. var
  510. lpNumberOfEvents: DWORD;
  511. lpBuffer: TInputRecord;
  512. lpNumberOfEventsRead : DWORD;
  513. nStdHandle: THandle;
  514. begin
  515. Result := False;
  516. nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
  517. lpNumberOfEvents := 0;
  518. GetNumberOfConsoleInputEvents(nStdHandle, lpNumberOfEvents);
  519. if lpNumberOfEvents <> 0 then
  520. begin
  521. PeekConsoleInput(nStdHandle, lpBuffer, 1, lpNumberOfEventsRead);
  522. if lpNumberOfEventsRead <> 0 then
  523. begin
  524. if lpBuffer.EventType = KEY_EVENT then
  525. begin
  526. if lpBuffer.Event.KeyEvent.bKeyDown and ((ExpectedKey = 0) or (lpBuffer.Event.KeyEvent.wVirtualKeyCode = ExpectedKey)) then Result := true
  527. else FlushConsoleInputBuffer(nStdHandle);
  528. end
  529. else FlushConsoleInputBuffer(nStdHandle);
  530. end;
  531. end;
  532. end;
  533. {$ELSE}
  534. var
  535. kp : Char;
  536. begin
  537. repeat
  538. kp := Readkey;
  539. until kp = Char(ExpectedKey);
  540. Result := True;
  541. end;
  542. {$ENDIF}
  543. function GetConsoleKeyPressed : Word;
  544. {$IFDEF MSWINDOWS}
  545. var
  546. lpNumberOfEvents: DWORD;
  547. lpBuffer: TInputRecord;
  548. lpNumberOfEventsRead : DWORD;
  549. nStdHandle: THandle;
  550. begin
  551. Result := 0;
  552. nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
  553. lpNumberOfEvents := 0;
  554. GetNumberOfConsoleInputEvents(nStdHandle, lpNumberOfEvents);
  555. if lpNumberOfEvents <> 0 then
  556. begin
  557. PeekConsoleInput(nStdHandle, lpBuffer, 1, lpNumberOfEventsRead);
  558. if lpNumberOfEventsRead <> 0 then
  559. begin
  560. if lpBuffer.EventType = KEY_EVENT then
  561. begin
  562. Result := lpBuffer.Event.KeyEvent.wVirtualKeyCode;
  563. FlushConsoleInputBuffer(nStdHandle);
  564. end
  565. else FlushConsoleInputBuffer(nStdHandle);
  566. end;
  567. end;
  568. end;
  569. {$ELSE}
  570. begin
  571. Result := Ord(ReadKey);
  572. end;
  573. {$ENDIF}
  574. {$IFDEF MSWINDOWS}
  575. procedure ProcessMessages;
  576. var
  577. Msg: TMsg;
  578. begin
  579. while integer(PeekMessage(Msg, 0, 0, 0, PM_REMOVE)) <> 0 do begin
  580. TranslateMessage(Msg);
  581. DispatchMessage(Msg);
  582. end;
  583. end;
  584. {$ENDIF}
  585. {$IFDEF MSWINDOWS}
  586. procedure ConsoleWaitForEnterKey;
  587. var
  588. msg: TMsg;
  589. begin
  590. while not ConsoleKeyPressed(VK_RETURN) do
  591. begin
  592. {$ifndef LVCL}
  593. {$IFDEF FPC}
  594. if GetCurrentThreadID = MainThreadID then
  595. begin
  596. CheckSynchronize;
  597. Sleep(1);
  598. end
  599. else
  600. {$ELSE}
  601. if GetCurrentThreadID = MainThreadID then CheckSynchronize{$IFDEF DELPHI7_UP}(1000){$ENDIF} else
  602. {$ENDIF}
  603. {$endif}
  604. WaitMessage;
  605. while PeekMessage(msg,0,0,0,PM_REMOVE) do
  606. begin
  607. if Msg.Message = WM_QUIT then Exit
  608. else
  609. begin
  610. TranslateMessage(Msg);
  611. DispatchMessage(Msg);
  612. end;
  613. end;
  614. end;
  615. end;
  616. {$ELSE}
  617. procedure ConsoleWaitForEnterKey;
  618. begin
  619. ReadLn;
  620. end;
  621. {$ENDIF}
  622. {$IFDEF MSWINDOWS}
  623. procedure RunConsoleCommand(const aCommand, aParameters : String; CallBack : TOutputProc<PAnsiChar> = nil; OutputLines : TStrings = nil);
  624. const
  625. CReadBuffer = 2400;
  626. var
  627. saSecurity: Windows.TSecurityAttributes;
  628. hRead: THandle;
  629. hWrite: THandle;
  630. suiStartup: TStartupInfo;
  631. piProcess: TProcessInformation;
  632. pBuffer: array [0..CReadBuffer] of AnsiChar;
  633. dBuffer: array [0..CReadBuffer] of AnsiChar;
  634. dRead: DWORD;
  635. dRunning: DWORD;
  636. dAvailable: DWORD;
  637. begin
  638. saSecurity.nLength := SizeOf(Windows.TSecurityAttributes);
  639. saSecurity.bInheritHandle := true;
  640. saSecurity.lpSecurityDescriptor := nil;
  641. if CreatePipe(hRead,hWrite,@saSecurity, 0) then
  642. begin
  643. try
  644. FillChar(suiStartup, SizeOf(TStartupInfo), #0);
  645. suiStartup.cb := SizeOf(TStartupInfo);
  646. suiStartup.hStdInput := hRead;
  647. suiStartup.hStdOutput := hWrite;
  648. suiStartup.hStdError := hWrite;
  649. suiStartup.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  650. suiStartup.wShowWindow := SW_HIDE;
  651. if CreateProcess(nil,PChar(Format('%s %s',[aCommand,aParameters])),
  652. @saSecurity,
  653. @saSecurity,
  654. True,
  655. NORMAL_PRIORITY_CLASS,
  656. nil,
  657. nil,
  658. suiStartup,
  659. piProcess) then
  660. begin
  661. try
  662. repeat
  663. dRunning := WaitForSingleObject(piProcess.hProcess,100);
  664. PeekNamedPipe(hRead,nil,0,nil,@dAvailable,nil);
  665. if (dAvailable > 0) then
  666. begin
  667. repeat
  668. dRead := 0;
  669. ReadFile(hRead,pBuffer[0],CReadBuffer,dRead,nil);
  670. pBuffer[dRead] := #0;
  671. OemToCharA(pBuffer,dBuffer);
  672. if Assigned(CallBack) then CallBack(dBuffer);
  673. if Assigned(OutputLines) then OutputLines.Add(dBuffer);
  674. until (dRead < CReadBuffer);
  675. end;
  676. //Application.ProcessMessages;
  677. until (dRunning <> WAIT_TIMEOUT);
  678. finally
  679. CloseHandle(piProcess.hProcess);
  680. CloseHandle(piProcess.hThread);
  681. end;
  682. end;
  683. finally
  684. CloseHandle(hRead);
  685. CloseHandle(hWrite);
  686. end;
  687. end
  688. else raise Exception.Create('Can''t create pipe!');
  689. end;
  690. {$ENDIF}
  691. {$IFDEF MSWINDOWS}
  692. procedure InitConsole;
  693. var
  694. BufferInfo: TConsoleScreenBufferInfo;
  695. begin
  696. Console.LogVerbose := LOG_ALL;
  697. Rewrite(Output);
  698. hStdOut := TTextRec(Output).Handle;
  699. {$IFDEF HASERROUTPUT}
  700. Rewrite(ErrOutput);
  701. hStdErr := TTextRec(ErrOutput).Handle;
  702. {$ELSE}
  703. hStdErr := GetStdHandle(STD_ERROR_HANDLE);
  704. {$ENDIF}
  705. if not GetConsoleScreenBufferInfo(hStdOut, BufferInfo) then
  706. begin
  707. {$IFNDEF FPC}
  708. SetInOutRes(GetLastError);
  709. {$ENDIF}
  710. Exit;
  711. end;
  712. ConsoleRect.Left := 0;
  713. ConsoleRect.Top := 0;
  714. ConsoleRect.Right := BufferInfo.dwSize.X - 1;
  715. ConsoleRect.Bottom := BufferInfo.dwSize.Y - 1;
  716. TextAttr := BufferInfo.wAttributes and $FF;
  717. DefConsoleColor := TextAttr;
  718. LastMode := 3; //CO80;
  719. end;
  720. {$ELSE}
  721. //AssignCrt(stderr);
  722. //Rewrite(stderr);
  723. {$ENDIF}
  724. { TConsoleMenu }
  725. {$IFDEF MSWINDOWS}
  726. procedure TConsoleMenu.AddMenu(const cMenuCaption: string; const cMenuKey: Word; MenuAction: TExecuteProc);
  727. var
  728. conmenu : TConsoleMenuOption;
  729. begin
  730. conmenu.Caption := cMenuCaption;
  731. conmenu.Key := cMenuKey;
  732. conmenu.OnKeyPressed := MenuAction;
  733. {$IFDEF DELPHIXE7_UP}
  734. fConsoleMenu := fConsoleMenu + [conmenu];
  735. {$ELSE}
  736. SetLength(fConsoleMenu,High(fConsoleMenu)+1);
  737. fConsoleMenu[High(fConsoleMenu)] := conmenu;
  738. {$ENDIF}
  739. end;
  740. procedure TConsoleMenu.AddMenu(MenuOption: TConsoleMenuOption);
  741. begin
  742. {$IFDEF DELPHIXE7_UP}
  743. fConsoleMenu := fConsoleMenu + [MenuOption];
  744. {$ELSE}
  745. SetLength(fConsoleMenu,High(fConsoleMenu)+1);
  746. fConsoleMenu[High(fConsoleMenu)] := MenuOption;
  747. {$ENDIF}
  748. end;
  749. constructor TConsoleMenu.Create;
  750. begin
  751. fMenuColor := ccLightCyan;
  752. fIsActive := False;
  753. end;
  754. procedure TConsoleMenu.Refresh(aClearScreen: Boolean);
  755. begin
  756. if aClearScreen then ClearScreen;
  757. WriteMenu;
  758. end;
  759. procedure TConsoleMenu.WaitForKeys;
  760. var
  761. msg: TMsg;
  762. conmenu : TConsoleMenuOption;
  763. keypressed : Word;
  764. begin
  765. fIsActive := True;
  766. HideCursor;
  767. WriteMenu;
  768. while True do
  769. begin
  770. //check key pressed
  771. keypressed := GetConsoleKeyPressed;
  772. for conmenu in fConsoleMenu do
  773. begin
  774. if keypressed = conmenu.Key then
  775. begin
  776. ClearScreen;
  777. WriteMenu;
  778. conmenu.DoKeyPressed;
  779. end;
  780. end;
  781. if keypressed = VK_ESCAPE then
  782. begin
  783. coutXY(50,12,'Exiting...',etInfo);
  784. Exit;
  785. end;
  786. {$ifndef LVCL}
  787. if GetCurrentThreadID=MainThreadID then CheckSynchronize{$ifdef WITHUXTHEME}(1000){$endif} else
  788. {$endif}
  789. WaitMessage;
  790. while PeekMessage(msg,0,0,0,PM_REMOVE) do
  791. begin
  792. if Msg.Message = WM_QUIT then Exit
  793. else
  794. begin
  795. TranslateMessage(Msg);
  796. DispatchMessage(Msg);
  797. end;
  798. end;
  799. end;
  800. ShowCursor;
  801. fIsActive := False;
  802. end;
  803. function GetCharFromVirtualKey(Key: Word): string;
  804. var
  805. keyboardState: TKeyboardState;
  806. asciiResult: Integer;
  807. begin
  808. GetKeyboardState(keyboardState) ;
  809. SetLength(Result, 2) ;
  810. asciiResult := ToAscii(key, MapVirtualKey(key, 0), keyboardState, @Result[1], 0) ;
  811. case asciiResult of
  812. 0: Result := '';
  813. 1: SetLength(Result, 1) ;
  814. 2:;
  815. else
  816. Result := '';
  817. end;
  818. end;
  819. procedure TConsoleMenu.WriteMenu;
  820. var
  821. conmenu : TConsoleMenuOption;
  822. ckey : string;
  823. coord : TCoord;
  824. oldcoord : TCoord;
  825. begin
  826. oldcoord.X := GetCursorX;
  827. oldcoord.Y := GetCursorY;
  828. coord.X := 0;
  829. coord.Y := 0;
  830. SetCursorPos(coord);
  831. TextColor(fMenuColor);
  832. ClearLine(0);
  833. for conmenu in fConsoleMenu do
  834. begin
  835. case conmenu.Key of
  836. VK_F1 : ckey := 'F1';
  837. VK_F2 : ckey := 'F2';
  838. VK_F3 : ckey := 'F3';
  839. VK_F4 : ckey := 'F4';
  840. VK_F5 : ckey := 'F5';
  841. VK_F6 : ckey := 'F6';
  842. VK_F7 : ckey := 'F7';
  843. VK_F8 : ckey := 'F8';
  844. VK_F9 : ckey := 'F9';
  845. VK_F10 : ckey := 'F10';
  846. VK_F11 : ckey := 'F11';
  847. VK_F12 : ckey := 'F12';
  848. else ckey := GetCharFromVirtualKey(conmenu.Key);
  849. end;
  850. TextColor(ccWhite);
  851. Write(Format('[%s]',[ckey]));
  852. TextColor(Self.MenuColor);
  853. Write(Format(' %s ',[conmenu.Caption]));
  854. end;
  855. TextColor(ccWhite);
  856. Write('[ESC]');
  857. TextColor(Self.MenuColor);
  858. Write(' Exit');
  859. TextColor(LastMode);
  860. SetCursorPos(oldcoord);
  861. end;
  862. { TConsoleMenuOption }
  863. procedure TConsoleMenuOption.DoKeyPressed;
  864. begin
  865. if Assigned(fOnKeyPressed) then fOnKeyPressed;
  866. end;
  867. {$ENDIF}
  868. initialization
  869. {$IF DEFINED(FPC) AND DEFINED(LINUX)}
  870. InitCriticalSection(CSConsole);
  871. {$ELSE}
  872. InitializeCriticalSection(CSConsole);
  873. //init stdout if not a service
  874. if GetStdHandle(STD_OUTPUT_HANDLE) <> 0 then InitConsole;
  875. {$ENDIF}
  876. finalization
  877. {$IF DEFINED(FPC) AND DEFINED(LINUX)}
  878. DoneCriticalsection(CSConsole);
  879. {$ELSE}
  880. DeleteCriticalSection(CSConsole);
  881. {$ENDIF}
  882. end.