Quick.Console.pas 27 KB

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