video.pp 12 KB

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