Quick.Console.pas 26 KB

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