2
0

Quick.Console.pas 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. { ***************************************************************************
  2. Copyright (c) 2016-2017 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.5
  7. Created : 10/05/2017
  8. Modified : 17/09/2017
  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. {$IFDEF CONDITIONALEXPRESSIONS}
  23. {$ifndef VER140}
  24. {$ifndef LINUX}
  25. {$define WITHUXTHEME}
  26. {$endif}
  27. {$endif}
  28. {$IF CompilerVersion >= 17.0}
  29. {$DEFINE INLINES}
  30. {$ENDIF}
  31. {$IF RTLVersion >= 14.0}
  32. {$DEFINE HASERROUTPUT}
  33. {$ENDIF}
  34. {$ENDIF}
  35. interface
  36. uses
  37. Classes,
  38. Windows,
  39. Winapi.Messages,
  40. System.SysUtils,
  41. Quick.Commons,
  42. Quick.Log;
  43. type
  44. //text colors
  45. TConsoleColor = (
  46. ccBlack = 0,
  47. ccBlue = 1,
  48. ccGreen = 2,
  49. ccCyan = 3,
  50. ccRed = 4,
  51. ccMagenta = 5,
  52. ccBrown = 6,
  53. ccLightGray = 7,
  54. ccDarkGray = 8,
  55. ccLightBlue = 9,
  56. ccLightGreen = 10,
  57. ccLightCyan = 11,
  58. ccLightRed = 12,
  59. ccLightMagenta = 13,
  60. ccYellow = 14,
  61. ccWhite = 15);
  62. TConsoleProperties = record
  63. LogVerbose : TLogVerbose;
  64. Log : TQuickLog;
  65. end;
  66. procedure cout(const cMsg : Integer; cEventType : TEventType); overload;
  67. procedure cout(const cMsg : Double; cEventType : TEventType); overload;
  68. procedure cout(const cMsg : string; cEventType : TEventType); overload;
  69. procedure coutXY(x,y : Integer; const s : string);
  70. procedure coutFmt(const cMsg : string; params : array of const; cEventType : TEventType);
  71. procedure TextColor(Color: TConsoleColor); overload;
  72. procedure TextColor(Color: Byte); overload;
  73. procedure TextBackground(Color: TConsoleColor); overload;
  74. procedure TextBackground(Color: Byte); overload;
  75. function ClearScreen : Boolean;
  76. procedure ClearLine;
  77. procedure ConsoleWaitForEnterKey;
  78. procedure InitConsole;
  79. var
  80. Console : TConsoleProperties;
  81. CSConsole : TRTLCriticalSection;
  82. LastMode : Word;
  83. TextAttr : Byte;
  84. hStdOut: THandle;
  85. hStdErr: THandle;
  86. ConsoleRect: TSmallRect;
  87. ScreenBufInfo : TConsoleScreenBufferInfo;
  88. implementation
  89. procedure cout(const cMsg : Integer; cEventType : TEventType);
  90. var
  91. FmtSets : TFormatSettings;
  92. begin
  93. try
  94. FmtSets := TFormatSettings.Create;
  95. FmtSets.ThousandSeparator := '.';
  96. FmtSets.DecimalSeparator := ',';
  97. cout(FormatFloat('0,',cMsg,FmtSets),cEventType);
  98. except
  99. cout(cMsg.ToString,cEventType);
  100. end;
  101. end;
  102. procedure cout(const cMsg : Double; cEventType : TEventType);
  103. var
  104. FmtSets : TFormatSettings;
  105. begin
  106. try
  107. FmtSets := TFormatSettings.Create;
  108. FmtSets.ThousandSeparator := '.';
  109. FmtSets.DecimalSeparator := ',';
  110. cout(FormatFloat('.0###,',cMsg,FmtSets),cEventType);
  111. except
  112. cout(cMsg.ToString,cEventType);
  113. end;
  114. end;
  115. procedure cout(const cMsg : string; cEventType : TEventType);
  116. begin
  117. if cEventType in Console.LogVerbose then
  118. begin
  119. EnterCriticalSection(CSConsole);
  120. try
  121. if hStdOut <> 0 then
  122. begin
  123. case cEventType of
  124. etError : TextColor(ccLightRed);
  125. etInfo : TextColor(ccWhite);
  126. etSuccess : TextColor(ccLightGreen);
  127. etWarning : TextColor(ccYellow);
  128. etDebug : TextColor(ccLightCyan);
  129. etTrace : TextColor(ccLightMagenta);
  130. else TextColor(ccWhite);
  131. end;
  132. Writeln(cMsg);
  133. TextColor(LastMode);
  134. end;
  135. finally
  136. LeaveCriticalSection(CSConsole);
  137. end;
  138. if Assigned(Log) then Log.Add(cMsg,cEventType);
  139. end;
  140. end;
  141. function GetCursorX: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  142. var
  143. BufferInfo: TConsoleScreenBufferInfo;
  144. begin
  145. GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
  146. Result := BufferInfo.dwCursorPosition.X;
  147. end;
  148. function GetCursorY: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  149. var
  150. BufferInfo: TConsoleScreenBufferInfo;
  151. begin
  152. GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
  153. Result := BufferInfo.dwCursorPosition.Y;
  154. end;
  155. procedure SetCursorPos(NewCoord : TCoord);
  156. begin
  157. SetConsoleCursorPosition(hStdOut, NewCoord);
  158. end;
  159. procedure coutXY(x,y : Integer; const s : string);
  160. var
  161. NewCoord : TCoord;
  162. LastCoord : TCoord;
  163. dwCount : DWORD;
  164. begin
  165. if hStdOut = 0 then Exit;
  166. EnterCriticalSection(CSConsole);
  167. try
  168. LastCoord.X := GetCursorX;
  169. LastCoord.Y := GetCursorY;
  170. NewCoord.X := x;
  171. NewCoord.Y := GetCursorY;
  172. ClearLine;
  173. SetCursorPos(NewCoord);
  174. if s <> '' then Write(s)
  175. else ClearLine;
  176. SetCursorPos(LastCoord);
  177. finally
  178. LeaveCriticalSection(CSConsole);
  179. end;
  180. end;
  181. procedure coutFmt(const cMsg : string; params : array of const; cEventType : TEventType);
  182. begin
  183. cout(Format(cMsg,params),cEventType);
  184. end;
  185. procedure TextColor(Color: TConsoleColor);
  186. begin
  187. TextColor(Integer(Color));
  188. end;
  189. procedure TextColor(Color: Byte);
  190. begin
  191. if hStdOut = 0 then Exit;
  192. LastMode := TextAttr;
  193. TextAttr := (TextAttr and $F0) or (Color and $0F);
  194. if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
  195. end;
  196. procedure TextBackground(Color: TConsoleColor);
  197. begin
  198. TextBackground(Integer(Color));
  199. end;
  200. procedure TextBackground(Color: Byte);
  201. begin
  202. if hStdOut = 0 then Exit;
  203. LastMode := TextAttr;
  204. TextAttr := (TextAttr and $0F) or ((Color shl 4) and $F0);
  205. if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
  206. end;
  207. function ClearScreen : Boolean;
  208. const
  209. BUFSIZE = 80*25;
  210. var
  211. Han, Dummy: LongWord;
  212. buf: string;
  213. coord: TCoord;
  214. begin
  215. Result := false;
  216. Han := GetStdHandle(STD_OUTPUT_HANDLE);
  217. if Han <> INVALID_HANDLE_VALUE then
  218. begin
  219. if SetConsoleTextAttribute(han, FOREGROUND_RED or FOREGROUND_GREEN or FOREGROUND_BLUE) then
  220. begin
  221. SetLength(buf,BUFSIZE);
  222. FillChar(buf[1],Length(buf),' ');
  223. if WriteConsole(han,PChar(buf),BUFSIZE,Dummy,nil) then
  224. begin
  225. coord.X := 0;
  226. coord.Y := 0;
  227. if SetConsoleCursorPosition(han,coord) then Result := true;
  228. end;
  229. end;
  230. end;
  231. end;
  232. procedure ClearLine;
  233. var
  234. dwWriteCoord: TCoord;
  235. dwCount, dwSize: DWord;
  236. begin
  237. if hStdOut = 0 then Exit;
  238. dwWriteCoord.X := ConsoleRect.Left;
  239. dwWriteCoord.Y := GetCursorY;
  240. dwSize := ConsoleRect.Right - ConsoleRect.Left + 1;
  241. FillConsoleOutputAttribute(hStdOut, TextAttr, dwSize, dwWriteCoord, dwCount);
  242. FillConsoleOutputCharacter(hStdOut, ' ', dwSize, dwWriteCoord, dwCount);
  243. end;
  244. function ConsoleKeyPressed(ExpectedKey: Word): Boolean;
  245. var
  246. lpNumberOfEvents: DWORD;
  247. lpBuffer: TInputRecord;
  248. lpNumberOfEventsRead : DWORD;
  249. nStdHandle: THandle;
  250. begin
  251. Result := False;
  252. nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
  253. lpNumberOfEvents := 0;
  254. GetNumberOfConsoleInputEvents(nStdHandle, lpNumberOfEvents);
  255. if lpNumberOfEvents <> 0 then
  256. begin
  257. PeekConsoleInput(nStdHandle, lpBuffer, 1, lpNumberOfEventsRead);
  258. if lpNumberOfEventsRead <> 0 then
  259. begin
  260. if lpBuffer.EventType = KEY_EVENT then
  261. begin
  262. if lpBuffer.Event.KeyEvent.bKeyDown and ((ExpectedKey = 0) or (lpBuffer.Event.KeyEvent.wVirtualKeyCode = ExpectedKey)) then Result := true
  263. else FlushConsoleInputBuffer(nStdHandle);
  264. end
  265. else FlushConsoleInputBuffer(nStdHandle);
  266. end;
  267. end;
  268. end;
  269. procedure ConsoleWaitForEnterKey;
  270. var
  271. msg: TMsg;
  272. begin
  273. while not ConsoleKeyPressed(VK_RETURN) do
  274. begin
  275. {$ifndef LVCL}
  276. if GetCurrentThreadID=MainThreadID then CheckSynchronize{$ifdef WITHUXTHEME}(1000){$endif} else
  277. {$endif}
  278. WaitMessage;
  279. while PeekMessage(msg,0,0,0,PM_REMOVE) do
  280. begin
  281. if Msg.Message = WM_QUIT then Exit
  282. else
  283. begin
  284. TranslateMessage(Msg);
  285. DispatchMessage(Msg);
  286. end;
  287. end;
  288. end;
  289. end;
  290. procedure InitConsole;
  291. var
  292. BufferInfo: TConsoleScreenBufferInfo;
  293. begin
  294. Console.LogVerbose := LOG_ALL;
  295. Rewrite(Output);
  296. hStdOut := TTextRec(Output).Handle;
  297. {$IFDEF HASERROUTPUT}
  298. Rewrite(ErrOutput);
  299. hStdErr := TTextRec(ErrOutput).Handle;
  300. {$ELSE}
  301. hStdErr := GetStdHandle(STD_ERROR_HANDLE);
  302. {$ENDIF}
  303. if not GetConsoleScreenBufferInfo(hStdOut, BufferInfo) then
  304. begin
  305. SetInOutRes(GetLastError);
  306. Exit;
  307. end;
  308. ConsoleRect.Left := 0;
  309. ConsoleRect.Top := 0;
  310. ConsoleRect.Right := BufferInfo.dwSize.X - 1;
  311. ConsoleRect.Bottom := BufferInfo.dwSize.Y - 1;
  312. TextAttr := BufferInfo.wAttributes and $FF;
  313. LastMode := 3; //CO80;
  314. end;
  315. initialization
  316. InitializeCriticalSection(CSConsole);
  317. //init stdout if not a service
  318. if GetStdHandle(STD_OUTPUT_HANDLE) <> 0 then InitConsole;
  319. finalization
  320. DeleteCriticalSection(CSConsole);
  321. end.