video.pp 13 KB

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