Quick.Console.pas 23 KB

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