video.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by Florian Klaempfl
  5. member of the Free Pascal development team
  6. Video unit for Win32
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. unit Video;
  14. interface
  15. {$i videoh.inc}
  16. implementation
  17. uses
  18. windows,dos;
  19. {$i video.inc}
  20. const
  21. LastCursorType: word = crUnderline;
  22. OrigScreen: PVideoBuf = nil;
  23. OrigScreenSize: cardinal = 0;
  24. var ConsoleInfo : TConsoleScreenBufferInfo;
  25. ConsoleCursorInfo : TConsoleCursorInfo;
  26. OrigCP: cardinal;
  27. OrigConsoleCursorInfo : TConsoleCursorInfo;
  28. OrigConsoleInfo : TConsoleScreenBufferInfo;
  29. procedure SysInitVideo;
  30. begin
  31. ScreenColor:=true;
  32. GetConsoleScreenBufferInfo(TextRec(Output).Handle, OrigConsoleInfo);
  33. GetConsoleCursorInfo(TextRec(Output).Handle, OrigConsoleCursorInfo);
  34. OrigCP := GetConsoleCP;
  35. ConsoleInfo:=OrigConsoleInfo;
  36. ConsoleCursorInfo:=OrigConsoleCursorInfo;
  37. {
  38. About the ConsoleCursorInfo record: There are 3 possible
  39. structures in it that can be regarded as the 'screen':
  40. - dwsize : contains the cols & row in current screen buffer.
  41. - srwindow : Coordinates (relative to buffer) of upper left
  42. & lower right corners of visible console.
  43. - dmMaximumWindowSize : Maximal size of Screen buffer.
  44. The first implementation of video used srWindow. After some
  45. bug-reports, this was switched to dwMaximumWindowSize.
  46. }
  47. with ConsoleInfo.dwMaximumWindowSize do
  48. begin
  49. ScreenWidth:=X;
  50. ScreenHeight:=Y;
  51. end;
  52. { TDrawBuffer only has FVMaxWidth elements
  53. larger values lead to crashes }
  54. if ScreenWidth> FVMaxWidth then
  55. ScreenWidth:=FVMaxWidth;
  56. CursorX:=ConsoleInfo.dwCursorPosition.x;
  57. CursorY:=ConsoleInfo.dwCursorPosition.y;
  58. if not ConsoleCursorInfo.bvisible then
  59. CursorLines:=0
  60. else
  61. CursorLines:=ConsoleCursorInfo.dwSize;
  62. end;
  63. procedure SysDoneVideo;
  64. begin
  65. SetConsoleScreenBufferSize (TextRec (Output).Handle, OrigConsoleInfo.dwSize);
  66. SetConsoleWindowInfo (cardinal (TextRec (Output).Handle), true, OrigConsoleInfo.srWindow);
  67. SetCursorType(crUnderLine);
  68. SetConsoleCP(OrigCP);
  69. end;
  70. function SysGetCapabilities: Word;
  71. begin
  72. SysGetCapabilities:=cpColor or cpChangeCursor;
  73. end;
  74. procedure SysSetCursorPos(NewCursorX, NewCursorY: Word);
  75. var
  76. pos : COORD;
  77. begin
  78. pos.x:=NewCursorX;
  79. pos.y:=NewCursorY;
  80. SetConsoleCursorPosition(TextRec(Output).Handle,pos);
  81. CursorX:=pos.x;
  82. CursorY:=pos.y;
  83. end;
  84. function SysGetCursorType: Word;
  85. begin
  86. GetConsoleCursorInfo(TextRec(Output).Handle,ConsoleCursorInfo);
  87. if not ConsoleCursorInfo.bvisible then
  88. SysGetCursorType:=crHidden
  89. else
  90. case ConsoleCursorInfo.dwSize of
  91. 1..30:
  92. SysGetCursorType:=crUnderline;
  93. 31..70:
  94. SysGetCursorType:=crHalfBlock;
  95. 71..100:
  96. SysGetCursorType:=crBlock;
  97. end;
  98. end;
  99. procedure SysSetCursorType(NewType: Word);
  100. begin
  101. GetConsoleCursorInfo(TextRec(Output).Handle,ConsoleCursorInfo);
  102. if newType=crHidden then
  103. ConsoleCursorInfo.bvisible:=false
  104. else
  105. begin
  106. ConsoleCursorInfo.bvisible:=true;
  107. case NewType of
  108. crUnderline:
  109. ConsoleCursorInfo.dwSize:=10;
  110. crHalfBlock:
  111. ConsoleCursorInfo.dwSize:=50;
  112. crBlock:
  113. ConsoleCursorInfo.dwSize:=99;
  114. end
  115. end;
  116. SetConsoleCursorInfo(TextRec(Output).Handle,ConsoleCursorInfo);
  117. end;
  118. function SysVideoModeSelector (const VideoMode: TVideoMode): boolean;
  119. var MI: Console_Screen_Buffer_Info;
  120. C: Coord;
  121. SR: Small_Rect;
  122. begin
  123. if not (GetConsoleScreenBufferInfo (TextRec (Output).Handle, MI)) then
  124. SysVideoModeSelector := false
  125. else
  126. begin
  127. with MI do
  128. begin
  129. C.X := VideoMode.Col;
  130. C.Y := VideoMode.Row;
  131. end;
  132. with SR do
  133. begin
  134. Top := 0;
  135. Left := 0;
  136. (* First, we need to make sure we reach the minimum window size *)
  137. (* to always fit in the new buffer after changing buffer size. *)
  138. Right := MI.srWindow.Right - MI.srWindow.Left;
  139. if VideoMode.Col <= Right then
  140. Right := Pred (VideoMode.Col);
  141. Bottom := MI.srWindow.Bottom - MI.srWindow.Top;
  142. if VideoMode.Row <= Bottom then
  143. Bottom := Pred (VideoMode.Row);
  144. end;
  145. if SetConsoleWindowInfo (cardinal (TextRec (Output).Handle), true, SR) then
  146. if SetConsoleScreenBufferSize (TextRec (Output).Handle, C) then
  147. begin
  148. with SR do
  149. begin
  150. (* Now, we can resize the window to the final size. *)
  151. Right := Pred (VideoMode.Col);
  152. Bottom := Pred (VideoMode.Row);
  153. end;
  154. if SetConsoleWindowInfo (cardinal (TextRec (Output).Handle), true, SR) then
  155. begin
  156. SysVideoModeSelector := true;
  157. SetCursorType (LastCursorType);
  158. ClearScreen;
  159. end
  160. else
  161. begin
  162. SysVideoModeSelector := false;
  163. SetConsoleScreenBufferSize (TextRec (Output).Handle, MI.dwSize);
  164. SetConsoleWindowInfo (cardinal (TextRec (Output).Handle), true, MI.srWindow);
  165. SetCursorType (LastCursorType);
  166. end
  167. end
  168. else
  169. begin
  170. SysVideoModeSelector := false;
  171. SetConsoleWindowInfo (cardinal (TextRec (Output).Handle), true, MI.srWindow);
  172. SetCursorType (LastCursorType);
  173. end
  174. else
  175. SysVideoModeSelector := false;
  176. end;
  177. end;
  178. Const
  179. SysVideoModeCount = 6;
  180. SysVMD : Array[0..SysVideoModeCount-1] of TVideoMode = (
  181. (Col: 40; Row: 25; Color: True),
  182. (Col: 80; Row: 25; Color: True),
  183. (Col: 80; Row: 30; Color: True),
  184. (Col: 80; Row: 43; Color: True),
  185. (Col: 80; Row: 50; Color: True),
  186. (Col: 80; Row: 25; Color: True) // Reserved for TargetEntry
  187. );
  188. Function SysSetVideoMode (Const Mode : TVideoMode) : Boolean;
  189. Var
  190. I : Integer;
  191. begin
  192. I:=SysVideoModeCount-1;
  193. SysSetVideoMode:=False;
  194. While (I>=0) and Not SysSetVideoMode do
  195. If (Mode.col=SysVMD[i].col) and
  196. (Mode.Row=SysVMD[i].Row) and
  197. (Mode.Color=SysVMD[i].Color) then
  198. SysSetVideoMode:=True
  199. else
  200. Dec(I);
  201. If SysSetVideoMode then
  202. begin
  203. if SysVideoModeSelector(Mode) then
  204. begin
  205. ScreenWidth:=SysVMD[I].Col;
  206. ScreenHeight:=SysVMD[I].Row;
  207. ScreenColor:=SysVMD[I].Color;
  208. end else SysSetVideoMode := false;
  209. end;
  210. end;
  211. Function SysGetVideoModeData (Index : Word; Var Data : TVideoMode) : boolean;
  212. begin
  213. SysGetVideoModeData:=(Index<=SysVideoModeCount);
  214. If SysGetVideoModeData then
  215. Data:=SysVMD[Index];
  216. end;
  217. Function SysGetVideoModeCount : Word;
  218. begin
  219. SysGetVideoModeCount:=SysVideoModeCount;
  220. end;
  221. procedure SysClearScreen;
  222. begin
  223. UpdateScreen(true);
  224. end;
  225. {$IFDEF FPC}
  226. function WriteConsoleOutput(hConsoleOutput:HANDLE; lpBuffer:pointer; dwBufferSize:COORD; dwBufferCoord:COORD;
  227. var lpWriteRegion:SMALL_RECT):WINBOOL; stdcall;external 'kernel32' name 'WriteConsoleOutputA';
  228. {$ENDIF}
  229. procedure SysUpdateScreen(Force: Boolean);
  230. type TmpRec = Array[0..(1024*32) - 1] of TCharInfo;
  231. type WordRec = record
  232. One, Two: Byte;
  233. end; { wordrec }
  234. var
  235. BufSize,
  236. BufCoord : COORD;
  237. WriteRegion : SMALL_RECT;
  238. LineBuf : ^TmpRec;
  239. BufCounter : Longint;
  240. LineCounter,
  241. ColCounter : Longint;
  242. smallforce : boolean;
  243. x1,y1,x2,y2 : longint;
  244. begin
  245. if force then
  246. smallforce:=true
  247. else
  248. begin
  249. asm
  250. pushl %esi
  251. pushl %edi
  252. movl VideoBuf,%esi
  253. movl OldVideoBuf,%edi
  254. movl VideoBufSize,%ecx
  255. shrl $2,%ecx
  256. repe
  257. cmpsl
  258. setne smallforce
  259. popl %edi
  260. popl %esi
  261. end;
  262. end;
  263. if SmallForce then
  264. begin
  265. BufSize.X := ScreenWidth;
  266. BufSize.Y := ScreenHeight;
  267. BufCoord.X := 0;
  268. BufCoord.Y := 0;
  269. with WriteRegion do
  270. begin
  271. Top :=0;
  272. Left :=0;
  273. Bottom := ScreenHeight-1;
  274. Right := ScreenWidth-1;
  275. end;
  276. New(LineBuf);
  277. BufCounter := 0;
  278. x1:=ScreenWidth+1;
  279. x2:=-1;
  280. y1:=ScreenHeight+1;
  281. y2:=-1;
  282. for LineCounter := 1 to ScreenHeight do
  283. begin
  284. for ColCounter := 1 to ScreenWidth do
  285. begin
  286. if (WordRec(VideoBuf^[BufCounter]).One<>WordRec(OldVideoBuf^[BufCounter]).One) or
  287. (WordRec(VideoBuf^[BufCounter]).Two<>WordRec(OldVideoBuf^[BufCounter]).Two) then
  288. begin
  289. if ColCounter<x1 then
  290. x1:=ColCounter;
  291. if ColCounter>x2 then
  292. x2:=ColCounter;
  293. if LineCounter<y1 then
  294. y1:=LineCounter;
  295. if LineCounter>y2 then
  296. y2:=LineCounter;
  297. end;
  298. {$ifdef VER1_0}
  299. Word(LineBuf^[BufCounter].UniCodeChar) := WordRec(VideoBuf^[BufCounter]).One;
  300. {$else}
  301. LineBuf^[BufCounter].UniCodeChar := Widechar(WordRec(VideoBuf^[BufCounter]).One);
  302. {$endif}
  303. { If (WordRec(VideoBuf^[BufCounter]).Two and $80)<>0 then
  304. LineBuf^[BufCounter].Attributes := $100+WordRec(VideoBuf^[BufCounter]).Two
  305. else }
  306. LineBuf^[BufCounter].Attributes := WordRec(VideoBuf^[BufCounter]).Two;
  307. Inc(BufCounter);
  308. end; { for }
  309. end; { for }
  310. BufSize.X := ScreenWidth;
  311. BufSize.Y := ScreenHeight;
  312. with WriteRegion do
  313. begin
  314. if force then
  315. begin
  316. Top := 0;
  317. Left :=0;
  318. Bottom := ScreenHeight-1;
  319. Right := ScreenWidth-1;
  320. BufCoord.X := 0;
  321. BufCoord.Y := 0;
  322. end
  323. else
  324. begin
  325. Top := y1-1;
  326. Left :=x1-1;
  327. Bottom := y2-1;
  328. Right := x2-1;
  329. BufCoord.X := x1-1;
  330. BufCoord.Y := y1-1;
  331. end;
  332. end;
  333. {
  334. writeln('X1: ',x1);
  335. writeln('Y1: ',y1);
  336. writeln('X2: ',x2);
  337. writeln('Y2: ',y2);
  338. }
  339. WriteConsoleOutput(TextRec(Output).Handle, LineBuf, BufSize, BufCoord, WriteRegion);
  340. Dispose(LineBuf);
  341. move(VideoBuf^,OldVideoBuf^,VideoBufSize);
  342. end;
  343. end;
  344. Const
  345. SysVideoDriver : TVideoDriver = (
  346. InitDriver : @SysInitVideo;
  347. DoneDriver : @SysDoneVideo;
  348. UpdateScreen : @SysUpdateScreen;
  349. ClearScreen : @SysClearScreen;
  350. SetVideoMode : @SysSetVideoMode;
  351. GetVideoModeCount : @SysGetVideoModeCount;
  352. GetVideoModeData : @SysGetVideoModeData;
  353. SetCursorPos : @SysSetCursorPos;
  354. GetCursorType : @SysGetCursorType;
  355. SetCursorType : @SysSetCursorType;
  356. GetCapabilities : @SysGetCapabilities
  357. );
  358. procedure TargetEntry;
  359. var
  360. C: Coord;
  361. SR: Small_Rect;
  362. VioMode: TConsoleScreenBufferInfo;
  363. begin
  364. GetConsoleScreenBufferInfo (TextRec (Output).Handle, VioMode);
  365. { Register the curent video mode in reserved slot in System Modes}
  366. with VioMode do
  367. begin
  368. {Assume we have at least 16 colours available in "colour" modes}
  369. SysVMD[SysVideoModeCount-1].Col:=dwMaximumWindowSize.X;
  370. SysVMD[SysVideoModeCount-1].Row:=dwMaximumWindowSize.Y;
  371. SysVMD[SysVideoModeCount-1].Color:=true;
  372. OrigScreenSize := dwMaximumWindowSize.X * dwMaximumWindowSize.Y * SizeOf (Char_Info);
  373. end;
  374. GetMem (OrigScreen, OrigScreenSize);
  375. with C do
  376. begin
  377. X := 0;
  378. Y := 0;
  379. end;
  380. with SR do
  381. begin
  382. Top := 0;
  383. Left := 0;
  384. Right := Pred (VioMode.dwSize.X);
  385. Bottom := Pred (VioMode.dwSize.Y);
  386. end;
  387. if not (ReadConsoleOutput (TextRec (Output).Handle, OrigScreen, VioMode.dwSize, C, SR)) then
  388. begin
  389. FreeMem (OrigScreen, OrigScreenSize);
  390. OrigScreen := nil;
  391. OrigScreenSize := 0;
  392. end;
  393. end;
  394. initialization
  395. SetVideoDriver(SysVideoDriver);
  396. TargetEntry;
  397. end.
  398. {
  399. $Log$
  400. Revision 1.16 2004-12-22 15:29:26 peter
  401. * always restore original window/buffersize for the console at exit
  402. Revision 1.15 2004/10/03 20:16:43 armin
  403. * SysUpdateScreen modified esi and edi
  404. Revision 1.14 2004/09/15 18:59:40 hajny
  405. + resolution switching fully works now
  406. Revision 1.13 2004/09/13 20:58:57 hajny
  407. * SysSetVideoMode corrected to reflect SysVideoModeSelector result
  408. Revision 1.12 2004/09/11 21:45:13 hajny
  409. + experimental patch to support more resolutions in text-mode IDE under Win32
  410. Revision 1.11 2003/09/17 15:06:36 peter
  411. * stdcall patch
  412. Revision 1.10 2002/12/15 20:22:56 peter
  413. * fix updatescreen compare that was wrong when the last char was
  414. different
  415. Revision 1.9 2002/10/06 20:00:22 peter
  416. * Use Widechar in the Windows unit
  417. Revision 1.8 2002/09/07 16:01:29 peter
  418. * old logs removed and tabs fixed
  419. }