Quick.Console.pas 26 KB

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