2
0

Quick.Console.pas 26 KB

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