Quick.Console.pas 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  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 : 29/03/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. procedure RunConsoleCommand(const aCommand, aParameters : String; CallBack : TOutputProc<PAnsiChar> = nil; OutputLines : TStrings = nil);
  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,0,';',Color+10*10);
  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. TextColor(DefConsoleColor);
  513. TextBackground(ccBlack);
  514. {$ENDIF}
  515. end;
  516. {$IFDEF MSWINDOWS}
  517. procedure ConsoleResize(Width, Height : Integer);
  518. var
  519. Rect: TSmallRect;
  520. Coord: TCoord;
  521. begin
  522. Rect.Left := 1;
  523. Rect.Top := 1;
  524. Rect.Right := Width;
  525. Rect.Bottom := Height;
  526. Coord.X := Rect.Right + 1 - Rect.Left;
  527. Coord.y := Rect.Bottom + 1 - Rect.Top;
  528. SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), Coord);
  529. SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), True, Rect);
  530. end;
  531. {$ENDIF}
  532. procedure ClearScreen;
  533. {$IFDEF MSWINDOWS}
  534. var
  535. stdout: THandle;
  536. bufinfo: TConsoleScreenBufferInfo;
  537. ConsoleSize: DWORD;
  538. NumWritten: DWORD;
  539. Origin: TCoord;
  540. begin
  541. stdout := GetStdHandle(STD_OUTPUT_HANDLE);
  542. if stdout<>INVALID_HANDLE_VALUE then
  543. begin
  544. GetConsoleScreenBufferInfo(stdout,bufinfo);
  545. ConsoleSize := bufinfo.dwSize.X * bufinfo.dwSize.Y;
  546. Origin.X := 0;
  547. Origin.Y := 0;
  548. FillConsoleOutputCharacter(stdout,' ',ConsoleSize,Origin,NumWritten);
  549. FillConsoleOutputAttribute(stdout,bufinfo.wAttributes,ConsoleSize,Origin,NumWritten);
  550. SetConsoleCursorPosition(stdout, Origin);
  551. end;
  552. end;
  553. {$ELSE}
  554. begin
  555. {$IF DEFINED(DELPHILINUX) OR DEFINED(MACOS)}
  556. write(AEC,2,'J');
  557. {$ELSE}
  558. ClrScr;
  559. {$ENDIF}
  560. end;
  561. {$ENDIF}
  562. procedure ClearLine;
  563. begin
  564. {$IF NOT DEFINED(DELPHILINUX) AND NOT DEFINED(MACOS)}
  565. ClearLine(GetCursorY);
  566. {$ELSE}
  567. write(AEC,'K');
  568. {$ENDIF}
  569. end;
  570. procedure ClearLine(Y : Integer);
  571. {$IFDEF MSWINDOWS}
  572. var
  573. dwWriteCoord: TCoord;
  574. dwCount, dwSize: DWord;
  575. begin
  576. if hStdOut = 0 then Exit;
  577. dwWriteCoord.X := 0;
  578. dwWriteCoord.Y := Y;
  579. dwSize := ConsoleRect.Right + 1;
  580. FillConsoleOutputAttribute(hStdOut, TextAttr, dwSize, dwWriteCoord, dwCount);
  581. FillConsoleOutputCharacter(hStdOut, ' ', dwSize, dwWriteCoord, dwCount);
  582. end;
  583. {$ELSE}
  584. begin
  585. GotoXY(1,Y);
  586. {$IF DEFINED(DELPHILINUX) OR DEFINED(MACOS)}
  587. write(AEC,'K');
  588. {$ELSE}
  589. DelLine;
  590. {$ENDIF}
  591. GotoXY(1,Y);
  592. end;
  593. {$ENDIF}
  594. procedure ShowCursor;
  595. begin
  596. {$IFDEF MSWINDOWS}
  597. GetConsoleCursorInfo(hStdOut,CursorInfo);
  598. CursorInfo.bVisible := True;
  599. SetConsoleCursorInfo(hStdOut,CursorInfo);
  600. {$ELSE}
  601. CursorOn;
  602. {$ENDIF}
  603. end;
  604. procedure HideCursor;
  605. begin
  606. {$IFDEF MSWINDOWS}
  607. GetConsoleCursorInfo(hStdOut,CursorInfo);
  608. CursorInfo.bVisible := False;
  609. SetConsoleCursorInfo(hStdOut,CursorInfo);
  610. {$ELSE}
  611. CursorOff;
  612. {$ENDIF}
  613. end;
  614. function ConsoleKeyPressed(ExpectedKey: Word): Boolean;
  615. {$IFDEF MSWINDOWS}
  616. var
  617. lpNumberOfEvents: DWORD;
  618. lpBuffer: TInputRecord;
  619. lpNumberOfEventsRead : DWORD;
  620. nStdHandle: THandle;
  621. begin
  622. Result := False;
  623. nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
  624. lpNumberOfEvents := 0;
  625. GetNumberOfConsoleInputEvents(nStdHandle, lpNumberOfEvents);
  626. if lpNumberOfEvents <> 0 then
  627. begin
  628. PeekConsoleInput(nStdHandle, lpBuffer, 1, lpNumberOfEventsRead);
  629. if lpNumberOfEventsRead <> 0 then
  630. begin
  631. if lpBuffer.EventType = KEY_EVENT then
  632. begin
  633. if lpBuffer.Event.KeyEvent.bKeyDown and ((ExpectedKey = 0) or (lpBuffer.Event.KeyEvent.wVirtualKeyCode = ExpectedKey)) then Result := true
  634. else FlushConsoleInputBuffer(nStdHandle);
  635. end
  636. else FlushConsoleInputBuffer(nStdHandle);
  637. end;
  638. end;
  639. end;
  640. {$ELSE}
  641. var
  642. kp : Char;
  643. begin
  644. repeat
  645. kp := Readkey;
  646. until kp = Char(ExpectedKey);
  647. Result := True;
  648. end;
  649. {$ENDIF}
  650. function GetConsoleKeyPressed : Word;
  651. {$IFDEF MSWINDOWS}
  652. var
  653. lpNumberOfEvents: DWORD;
  654. lpBuffer: TInputRecord;
  655. lpNumberOfEventsRead : DWORD;
  656. nStdHandle: THandle;
  657. begin
  658. Result := 0;
  659. nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
  660. lpNumberOfEvents := 0;
  661. GetNumberOfConsoleInputEvents(nStdHandle, lpNumberOfEvents);
  662. if lpNumberOfEvents <> 0 then
  663. begin
  664. PeekConsoleInput(nStdHandle, lpBuffer, 1, lpNumberOfEventsRead);
  665. if lpNumberOfEventsRead <> 0 then
  666. begin
  667. if lpBuffer.EventType = KEY_EVENT then
  668. begin
  669. Result := lpBuffer.Event.KeyEvent.wVirtualKeyCode;
  670. FlushConsoleInputBuffer(nStdHandle);
  671. end
  672. else FlushConsoleInputBuffer(nStdHandle);
  673. end;
  674. end;
  675. end;
  676. {$ELSE}
  677. begin
  678. Result := Ord(ReadKey);
  679. end;
  680. {$ENDIF}
  681. {$IFDEF MSWINDOWS}
  682. procedure ProcessMessages;
  683. var
  684. Msg: TMsg;
  685. begin
  686. while integer(PeekMessage(Msg, 0, 0, 0, PM_REMOVE)) <> 0 do begin
  687. TranslateMessage(Msg);
  688. DispatchMessage(Msg);
  689. end;
  690. end;
  691. {$ENDIF}
  692. {$IFDEF MSWINDOWS}
  693. procedure ConsoleWaitForEnterKey;
  694. var
  695. msg: TMsg;
  696. begin
  697. while not ConsoleKeyPressed(VK_RETURN) do
  698. begin
  699. {$ifndef LVCL}
  700. {$IFDEF FPC}
  701. if GetCurrentThreadID = MainThreadID then
  702. begin
  703. CheckSynchronize;
  704. Sleep(1);
  705. end
  706. else
  707. {$ELSE}
  708. if GetCurrentThreadID = MainThreadID then CheckSynchronize{$IFDEF DELPHI7_UP}(1000){$ENDIF} else
  709. {$ENDIF}
  710. {$endif}
  711. WaitMessage;
  712. while PeekMessage(msg,0,0,0,PM_REMOVE) do
  713. begin
  714. if Msg.Message = WM_QUIT then Exit
  715. else
  716. begin
  717. TranslateMessage(Msg);
  718. DispatchMessage(Msg);
  719. end;
  720. end;
  721. end;
  722. end;
  723. {$ELSE}
  724. procedure ConsoleWaitForEnterKey;
  725. begin
  726. ReadLn;
  727. end;
  728. {$ENDIF}
  729. {$IFDEF MSWINDOWS}
  730. procedure RunConsoleCommand(const aCommand, aParameters : String; CallBack : TOutputProc<PAnsiChar> = nil; OutputLines : TStrings = nil);
  731. const
  732. CReadBuffer = 2400;
  733. var
  734. saSecurity: Windows.TSecurityAttributes;
  735. hRead: THandle;
  736. hWrite: THandle;
  737. suiStartup: TStartupInfo;
  738. piProcess: TProcessInformation;
  739. pBuffer: array [0..CReadBuffer] of AnsiChar;
  740. dBuffer: array [0..CReadBuffer] of AnsiChar;
  741. dRead: DWORD;
  742. dRunning: DWORD;
  743. dAvailable: DWORD;
  744. begin
  745. saSecurity.nLength := SizeOf(Windows.TSecurityAttributes);
  746. saSecurity.bInheritHandle := true;
  747. saSecurity.lpSecurityDescriptor := nil;
  748. if CreatePipe(hRead,hWrite,@saSecurity, 0) then
  749. begin
  750. try
  751. FillChar(suiStartup, SizeOf(TStartupInfo), #0);
  752. suiStartup.cb := SizeOf(TStartupInfo);
  753. suiStartup.hStdInput := hRead;
  754. suiStartup.hStdOutput := hWrite;
  755. suiStartup.hStdError := hWrite;
  756. suiStartup.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  757. suiStartup.wShowWindow := SW_HIDE;
  758. if CreateProcess(nil,PChar(Format('%s %s',[aCommand,aParameters])),
  759. @saSecurity,
  760. @saSecurity,
  761. True,
  762. NORMAL_PRIORITY_CLASS,
  763. nil,
  764. nil,
  765. suiStartup,
  766. piProcess) then
  767. begin
  768. try
  769. repeat
  770. dRunning := WaitForSingleObject(piProcess.hProcess,100);
  771. PeekNamedPipe(hRead,nil,0,nil,@dAvailable,nil);
  772. if (dAvailable > 0) then
  773. begin
  774. repeat
  775. dRead := 0;
  776. ReadFile(hRead,pBuffer[0],CReadBuffer,dRead,nil);
  777. pBuffer[dRead] := #0;
  778. OemToCharA(pBuffer,dBuffer);
  779. if Assigned(CallBack) then CallBack(dBuffer);
  780. if Assigned(OutputLines) then OutputLines.Add(dBuffer);
  781. until (dRead < CReadBuffer);
  782. end;
  783. //Application.ProcessMessages;
  784. until (dRunning <> WAIT_TIMEOUT);
  785. finally
  786. CloseHandle(piProcess.hProcess);
  787. CloseHandle(piProcess.hThread);
  788. end;
  789. end;
  790. finally
  791. CloseHandle(hRead);
  792. CloseHandle(hWrite);
  793. end;
  794. end
  795. else raise Exception.Create('Can''t create pipe!');
  796. end;
  797. {$ENDIF}
  798. {$IFDEF MSWINDOWS}
  799. procedure InitConsole;
  800. var
  801. BufferInfo: TConsoleScreenBufferInfo;
  802. begin
  803. Rewrite(Output);
  804. hStdOut := TTextRec(Output).Handle;
  805. {$IFDEF HASERROUTPUT}
  806. Rewrite(ErrOutput);
  807. hStdErr := TTextRec(ErrOutput).Handle;
  808. {$ELSE}
  809. hStdErr := GetStdHandle(STD_ERROR_HANDLE);
  810. {$ENDIF}
  811. if not GetConsoleScreenBufferInfo(hStdOut, BufferInfo) then
  812. begin
  813. {$IFNDEF FPC}
  814. SetInOutRes(GetLastError);
  815. {$ENDIF}
  816. Exit;
  817. end;
  818. ConsoleRect.Left := 0;
  819. ConsoleRect.Top := 0;
  820. ConsoleRect.Right := BufferInfo.dwSize.X - 1;
  821. ConsoleRect.Bottom := BufferInfo.dwSize.Y - 1;
  822. TextAttr := BufferInfo.wAttributes and $FF;
  823. DefConsoleColor := TextAttr;
  824. LastMode := 3; //CO80;
  825. end;
  826. {$ELSE}
  827. //AssignCrt(stderr);
  828. //Rewrite(stderr);
  829. {$ENDIF}
  830. { TConsoleMenu }
  831. {$IFDEF MSWINDOWS}
  832. procedure TConsoleMenu.AddMenu(const cMenuCaption: string; const cMenuKey: Word; MenuAction: TExecuteProc);
  833. var
  834. conmenu : TConsoleMenuOption;
  835. begin
  836. conmenu.Caption := cMenuCaption;
  837. conmenu.Key := cMenuKey;
  838. conmenu.OnKeyPressed := MenuAction;
  839. {$IFDEF DELPHIXE7_UP}
  840. fConsoleMenu := fConsoleMenu + [conmenu];
  841. {$ELSE}
  842. SetLength(fConsoleMenu,High(fConsoleMenu)+1);
  843. fConsoleMenu[High(fConsoleMenu)] := conmenu;
  844. {$ENDIF}
  845. end;
  846. procedure TConsoleMenu.AddMenu(MenuOption: TConsoleMenuOption);
  847. begin
  848. {$IFDEF DELPHIXE7_UP}
  849. fConsoleMenu := fConsoleMenu + [MenuOption];
  850. {$ELSE}
  851. SetLength(fConsoleMenu,High(fConsoleMenu)+1);
  852. fConsoleMenu[High(fConsoleMenu)] := MenuOption;
  853. {$ENDIF}
  854. end;
  855. constructor TConsoleMenu.Create;
  856. begin
  857. fMenuColor := ccLightCyan;
  858. fIsActive := False;
  859. end;
  860. procedure TConsoleMenu.Refresh(aClearScreen: Boolean);
  861. begin
  862. if aClearScreen then ClearScreen;
  863. WriteMenu;
  864. end;
  865. procedure TConsoleMenu.WaitForKeys;
  866. var
  867. msg: TMsg;
  868. conmenu : TConsoleMenuOption;
  869. keypressed : Word;
  870. begin
  871. fIsActive := True;
  872. HideCursor;
  873. WriteMenu;
  874. while True do
  875. begin
  876. //check key pressed
  877. keypressed := GetConsoleKeyPressed;
  878. for conmenu in fConsoleMenu do
  879. begin
  880. if keypressed = conmenu.Key then
  881. begin
  882. ClearScreen;
  883. WriteMenu;
  884. conmenu.DoKeyPressed;
  885. end;
  886. end;
  887. if keypressed = VK_ESCAPE then
  888. begin
  889. coutXY(50,12,'Exiting...',etInfo);
  890. Exit;
  891. end;
  892. {$ifndef LVCL}
  893. if GetCurrentThreadID=MainThreadID then CheckSynchronize{$ifdef WITHUXTHEME}(1000){$endif} else
  894. {$endif}
  895. WaitMessage;
  896. while PeekMessage(msg,0,0,0,PM_REMOVE) do
  897. begin
  898. if Msg.Message = WM_QUIT then Exit
  899. else
  900. begin
  901. TranslateMessage(Msg);
  902. DispatchMessage(Msg);
  903. end;
  904. end;
  905. end;
  906. ShowCursor;
  907. fIsActive := False;
  908. end;
  909. function GetCharFromVirtualKey(Key: Word): string;
  910. var
  911. keyboardState: TKeyboardState;
  912. asciiResult: Integer;
  913. begin
  914. GetKeyboardState(keyboardState) ;
  915. SetLength(Result, 2) ;
  916. asciiResult := ToAscii(key, MapVirtualKey(key, 0), keyboardState, @Result[1], 0) ;
  917. case asciiResult of
  918. 0: Result := '';
  919. 1: SetLength(Result, 1) ;
  920. 2:;
  921. else
  922. Result := '';
  923. end;
  924. end;
  925. procedure TConsoleMenu.WriteMenu;
  926. var
  927. conmenu : TConsoleMenuOption;
  928. ckey : string;
  929. coord : TCoord;
  930. oldcoord : TCoord;
  931. begin
  932. oldcoord.X := GetCursorX;
  933. oldcoord.Y := GetCursorY;
  934. coord.X := 0;
  935. coord.Y := 0;
  936. SetCursorPos(coord);
  937. TextColor(fMenuColor);
  938. ClearLine(0);
  939. for conmenu in fConsoleMenu do
  940. begin
  941. case conmenu.Key of
  942. VK_F1 : ckey := 'F1';
  943. VK_F2 : ckey := 'F2';
  944. VK_F3 : ckey := 'F3';
  945. VK_F4 : ckey := 'F4';
  946. VK_F5 : ckey := 'F5';
  947. VK_F6 : ckey := 'F6';
  948. VK_F7 : ckey := 'F7';
  949. VK_F8 : ckey := 'F8';
  950. VK_F9 : ckey := 'F9';
  951. VK_F10 : ckey := 'F10';
  952. VK_F11 : ckey := 'F11';
  953. VK_F12 : ckey := 'F12';
  954. else ckey := GetCharFromVirtualKey(conmenu.Key);
  955. end;
  956. TextColor(ccWhite);
  957. Write(Format('[%s]',[ckey]));
  958. TextColor(Self.MenuColor);
  959. Write(Format(' %s ',[conmenu.Caption]));
  960. end;
  961. TextColor(ccWhite);
  962. Write('[ESC]');
  963. TextColor(Self.MenuColor);
  964. Write(' Exit');
  965. TextColor(LastMode);
  966. SetCursorPos(oldcoord);
  967. end;
  968. { TConsoleMenuOption }
  969. procedure TConsoleMenuOption.DoKeyPressed;
  970. begin
  971. if Assigned(fOnKeyPressed) then fOnKeyPressed;
  972. end;
  973. {$ENDIF}
  974. initialization
  975. Console.LogVerbose := LOG_ALL;
  976. {$IF DEFINED(FPC) AND DEFINED(LINUX)}
  977. InitCriticalSection(CSConsole);
  978. {$ELSE}
  979. {$IF NOT DEFINED(DELPHILINUX) AND NOT DEFINED(MACOS)}
  980. InitializeCriticalSection(CSConsole);
  981. //init stdout if not a service
  982. try
  983. if HasConsoleOutput then InitConsole;
  984. except
  985. //avoid raise exception
  986. end;
  987. {$ELSE}
  988. CSConsole := TRTLCriticalSection.Create;
  989. {$ENDIF}
  990. {$ENDIF}
  991. finalization
  992. {$IF DEFINED(FPC) AND DEFINED(LINUX)}
  993. DoneCriticalsection(CSConsole);
  994. {$ELSE}
  995. {$IFNDEF DELPHILINUX}
  996. DeleteCriticalSection(CSConsole);
  997. {$ELSE}
  998. CSConsole.Free;
  999. {$ENDIF}
  1000. {$ENDIF}
  1001. end.