GXS.Screen.pas 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.Screen;
  5. (* Routines to interact with the screen/desktop *)
  6. interface
  7. uses
  8. Winapi.Windows,
  9. System.Classes,
  10. System.SysUtils,
  11. FMX.Forms,
  12. Stage.VectorGeometry;
  13. const
  14. MaxVideoModes = 200;
  15. lcl_release = 0;
  16. type
  17. TResolution = 0 .. MaxVideoModes;
  18. // window attributes
  19. TgxWindowAttribute = (woDesktop, woStayOnTop, woTransparent);
  20. TgxWindowAttributes = set of TgxWindowAttribute;
  21. // window-to-screen fitting
  22. TgxWindowFitting = (wfDefault, wfFitWindowToScreen, wfFitScreenToWindow);
  23. TgxDisplayOptions = class(TPersistent)
  24. private
  25. FFullScreen: Boolean;
  26. FScreenResolution: TResolution;
  27. FWindowAttributes: TgxWindowAttributes;
  28. FWindowFitting: TgxWindowFitting;
  29. public
  30. procedure Assign(Source: TPersistent); override;
  31. published
  32. property FullScreen: Boolean read FFullScreen write FFullScreen
  33. default False;
  34. property ScreenResolution: TResolution read FScreenResolution
  35. write FScreenResolution default 0;
  36. property WindowAttributes: TgxWindowAttributes read FWindowAttributes
  37. write FWindowAttributes default [];
  38. property WindowFitting: TgxWindowFitting read FWindowFitting
  39. write FWindowFitting default wfDefault;
  40. end;
  41. TgxVideoMode = packed record
  42. Width: Word;
  43. Height: Word;
  44. ColorDepth: Byte;
  45. MaxFrequency: Byte;
  46. Description: String;
  47. end;
  48. PgxVideoMode = ^TgxVideoMode;
  49. function GetIndexFromResolution(XRes, YRes, BPP: Integer): TResolution;
  50. procedure ReadVideoModes;
  51. // Changes to the video mode given by 'Index'
  52. function SetFullscreenMode(modeIndex: TResolution;
  53. displayFrequency: Integer = 0): Boolean;
  54. procedure ReadScreenImage(Dest: HDC; DestLeft, DestTop: Integer;
  55. const SrcRect: TRectangle);
  56. procedure RestoreDefaultMode;
  57. procedure GLShowCursor(AShow: Boolean);
  58. procedure GLSetCursorPos(AScreenX, AScreenY: Integer);
  59. procedure GLGetCursorPos(var point: TPoint);
  60. function GLGetScreenWidth: Integer;
  61. function GLGetScreenHeight: Integer;
  62. var
  63. vNumberVideoModes: Integer = 0;
  64. vCurrentVideoMode: Integer = 0;
  65. vVideoModes: array of TgxVideoMode;
  66. // ------------------------------------------------------------------------------
  67. implementation
  68. // ------------------------------------------------------------------------------
  69. type
  70. TLowResMode = packed record
  71. Width: Word;
  72. Height: Word;
  73. ColorDepth: Byte;
  74. end;
  75. const
  76. NumberLowResModes = 15;
  77. LowResModes: array [0 .. NumberLowResModes - 1] of TLowResMode = ((Width: 320;
  78. Height: 200; ColorDepth: 8), (Width: 320; Height: 200; ColorDepth: 15),
  79. (Width: 320; Height: 200; ColorDepth: 16), (Width: 320; Height: 200;
  80. ColorDepth: 24), (Width: 320; Height: 200; ColorDepth: 32), (Width: 400;
  81. Height: 300; ColorDepth: 8), (Width: 400; Height: 300; ColorDepth: 15),
  82. (Width: 400; Height: 300; ColorDepth: 16), (Width: 400; Height: 300;
  83. ColorDepth: 24), (Width: 400; Height: 300; ColorDepth: 32), (Width: 512;
  84. Height: 384; ColorDepth: 8), (Width: 512; Height: 384; ColorDepth: 15),
  85. (Width: 512; Height: 384; ColorDepth: 16), (Width: 512; Height: 384;
  86. ColorDepth: 24), (Width: 512; Height: 384; ColorDepth: 32));
  87. procedure TgxDisplayOptions.Assign(Source: TPersistent);
  88. begin
  89. if Source is TgxDisplayOptions then
  90. begin
  91. FFullScreen := TgxDisplayOptions(Source).FFullScreen;
  92. FScreenResolution := TgxDisplayOptions(Source).FScreenResolution;
  93. FWindowAttributes := TgxDisplayOptions(Source).FWindowAttributes;
  94. FWindowFitting := TgxDisplayOptions(Source).FWindowFitting;
  95. end
  96. else
  97. inherited Assign(Source);
  98. end;
  99. function GetIndexFromResolution(XRes, YRes, BPP: Integer): TResolution;
  100. // Determines the index of a screen resolution nearest to the
  101. // given values. The returned screen resolution is always greater
  102. // or equal than XRes and YRes or, in case the resolution isn't
  103. // supported, the value 0, which indicates the default mode.
  104. var
  105. I: Integer;
  106. XDiff, YDiff: Integer;
  107. CDiff: Integer;
  108. begin
  109. ReadVideoModes;
  110. // prepare result in case we don't find a valid mode
  111. Result := 0;
  112. // set differences to maximum
  113. XDiff := 9999;
  114. YDiff := 9999;
  115. CDiff := 99;
  116. for I := 1 to vNumberVideoModes - 1 do
  117. with vVideoModes[I] do
  118. begin
  119. if (Width >= XRes) and ((Width - XRes) <= XDiff) and (Height >= YRes) and
  120. ((Height - YRes) <= YDiff) and (ColorDepth >= BPP) and
  121. ((ColorDepth - BPP) <= CDiff) then
  122. begin
  123. XDiff := Width - XRes;
  124. YDiff := Height - YRes;
  125. CDiff := ColorDepth - BPP;
  126. Result := I;
  127. end;
  128. end;
  129. end;
  130. procedure TryToAddToList(deviceMode: TDevMode);
  131. // Adds a video mode to the list if it's not a duplicate and can actually be set.
  132. var
  133. I: Integer;
  134. vm: PgxVideoMode;
  135. begin
  136. // See if this is a duplicate mode (can happen because of refresh
  137. // rates, or because we explicitly try all the low-res modes)
  138. for I := 1 to vNumberVideoModes - 1 do
  139. with deviceMode do
  140. begin
  141. vm := @vVideoModes[I];
  142. if ((dmBitsPerPel = vm^.ColorDepth) and (dmPelsWidth = vm^.Width) and
  143. (dmPelsHeight = vm^.Height)) then
  144. begin
  145. // it's a duplicate mode, higher frequency?
  146. if dmDisplayFrequency > vm^.MaxFrequency then
  147. vm^.MaxFrequency := dmDisplayFrequency;
  148. Exit;
  149. end;
  150. end;
  151. // do a mode set test (doesn't actually do the mode set, but reports whether it would have succeeded).
  152. if ChangeDisplaySettings(deviceMode, CDS_TEST or CDS_FULLSCREEN) <> DISP_CHANGE_SUCCESSFUL
  153. then
  154. Exit;
  155. // it's a new, valid mode, so add this to the list
  156. vm := @vVideoModes[vNumberVideoModes];
  157. with deviceMode do
  158. begin
  159. vm^.ColorDepth := dmBitsPerPel;
  160. vm^.Width := dmPelsWidth;
  161. vm^.Height := dmPelsHeight;
  162. vm^.MaxFrequency := dmDisplayFrequency;
  163. vm^.Description := Format('%d x %d, %d bpp', [dmPelsWidth, dmPelsHeight,
  164. dmBitsPerPel]);
  165. end;
  166. Inc(vNumberVideoModes);
  167. end;
  168. procedure ReadVideoModes;
  169. var
  170. I, ModeNumber: Integer;
  171. done: Boolean;
  172. deviceMode: TDevMode;
  173. DeskDC: HDC;
  174. begin
  175. if vNumberVideoModes > 0 then
  176. Exit;
  177. SetLength(vVideoModes, MaxVideoModes);
  178. vNumberVideoModes := 1;
  179. // prepare 'default' entry
  180. DeskDC := GetDC(0);
  181. with vVideoModes[0] do
  182. try
  183. ColorDepth := GetDeviceCaps(DeskDC, BITSPIXEL) *
  184. GetDeviceCaps(DeskDC, PLANES);
  185. Width := Trunc(Screen.Width);
  186. Height := Trunc(Screen.Height);
  187. Description := 'default';
  188. finally
  189. ReleaseDC(0, DeskDC);
  190. end;
  191. // enumerate all available video modes
  192. ModeNumber := 0;
  193. repeat
  194. done := not EnumDisplaySettings(nil, ModeNumber, deviceMode);
  195. TryToAddToList(deviceMode);
  196. Inc(ModeNumber);
  197. until (done or (vNumberVideoModes >= MaxVideoModes));
  198. // low-res modes don't always enumerate, ask about them explicitly
  199. with deviceMode do
  200. begin
  201. dmBitsPerPel := 8;
  202. dmPelsWidth := 42;
  203. dmPelsHeight := 37;
  204. dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
  205. // make sure the driver doesn't just answer yes to all tests
  206. if ChangeDisplaySettings(deviceMode, CDS_TEST or CDS_FULLSCREEN) <> DISP_CHANGE_SUCCESSFUL
  207. then
  208. begin
  209. I := 0;
  210. while (I < NumberLowResModes - 1) and
  211. (vNumberVideoModes < MaxVideoModes) do
  212. begin
  213. dmSize := Sizeof(deviceMode);
  214. with LowResModes[I] do
  215. begin
  216. dmBitsPerPel := ColorDepth;
  217. dmPelsWidth := Width;
  218. dmPelsHeight := Height;
  219. end;
  220. dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
  221. TryToAddToList(deviceMode);
  222. Inc(I);
  223. end;
  224. end;
  225. end;
  226. end;
  227. function SetFullscreenMode(modeIndex: TResolution;
  228. displayFrequency: Integer = 0): Boolean;
  229. var
  230. deviceMode: TDevMode;
  231. begin
  232. ReadVideoModes;
  233. FillChar(deviceMode, Sizeof(deviceMode), 0);
  234. with deviceMode do
  235. begin
  236. dmSize := Sizeof(deviceMode);
  237. dmBitsPerPel := vVideoModes[modeIndex].ColorDepth;
  238. dmPelsWidth := vVideoModes[modeIndex].Width;
  239. dmPelsHeight := vVideoModes[modeIndex].Height;
  240. dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
  241. if displayFrequency > 0 then
  242. begin
  243. dmFields := dmFields or DM_DISPLAYFREQUENCY;
  244. if displayFrequency > vVideoModes[modeIndex].MaxFrequency then
  245. displayFrequency := vVideoModes[modeIndex].MaxFrequency;
  246. dmDisplayFrequency := displayFrequency;
  247. end;
  248. end;
  249. Result := ChangeDisplaySettings(deviceMode, CDS_FULLSCREEN)
  250. = DISP_CHANGE_SUCCESSFUL;
  251. if Result then
  252. vCurrentVideoMode := modeIndex;
  253. end;
  254. procedure ReadScreenImage(Dest: HDC; DestLeft, DestTop: Integer;
  255. const SrcRect: TRectangle);
  256. var
  257. screenDC: HDC;
  258. begin
  259. screenDC := GetDC(0);
  260. try
  261. GDIFlush;
  262. BitBlt(Dest, DestLeft, DestTop, SrcRect.Width, SrcRect.Height, screenDC,
  263. SrcRect.Left, SrcRect.Top, SRCCOPY);
  264. finally
  265. ReleaseDC(0, screenDC);
  266. end;
  267. end;
  268. procedure RestoreDefaultMode;
  269. var
  270. t: PDevMode;
  271. begin
  272. t := nil;
  273. ChangeDisplaySettings(t^, CDS_FULLSCREEN);
  274. end;
  275. procedure GLShowCursor(AShow: Boolean);
  276. begin
  277. ShowCursor(AShow);
  278. end;
  279. procedure GLSetCursorPos(AScreenX, AScreenY: Integer);
  280. begin
  281. SetCursorPos(AScreenX, AScreenY);
  282. end;
  283. procedure GLGetCursorPos(var point: TPoint);
  284. begin
  285. GetCursorPos(point);
  286. end;
  287. function GLGetScreenWidth: Integer;
  288. begin
  289. Result := Trunc(Screen.Width);
  290. end;
  291. function GLGetScreenHeight: Integer;
  292. begin
  293. Result := Trunc(Screen.Height);
  294. end;
  295. initialization // -----------------------------------------------------------
  296. finalization
  297. if vCurrentVideoMode <> 0 then
  298. RestoreDefaultMode; // set default video mode
  299. end.