/packages/ptc/src/x11/x11modesi.inc

https://github.com/slibre/freepascal · Pascal · 461 lines · 340 code · 68 blank · 53 comment · 42 complexity · c19e0bc2a331d9b7fa61494fe934e993 MD5 · raw file

  1. {
  2. This file is part of the PTCPas framebuffer library
  3. Copyright (C) 2001-2012 Nikolay Nikolov (nickysn@users.sourceforge.net)
  4. Original C++ version by Christian Nentwich (c.nentwich@cs.ucl.ac.uk)
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version
  9. with the following modification:
  10. As a special exception, the copyright holders of this library give you
  11. permission to link this library with independent modules to produce an
  12. executable, regardless of the license terms of these independent modules,and
  13. to copy and distribute the resulting executable under terms of your choice,
  14. provided that you also meet, for each linked independent module, the terms
  15. and conditions of the license of that module. An independent module is a
  16. module which is not derived from or based on this library. If you modify
  17. this library, you may extend this exception to your version of the library,
  18. but you are not obligated to do so. If you do not wish to do so, delete this
  19. exception statement from your version.
  20. This library is distributed in the hope that it will be useful,
  21. but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  23. Lesser General Public License for more details.
  24. You should have received a copy of the GNU Lesser General Public
  25. License along with this library; if not, write to the Free Software
  26. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  27. }
  28. constructor TX11Modes.Create(ADisplay: PDisplay; AScreen: cint);
  29. begin
  30. FDisplay := ADisplay;
  31. FScreen := AScreen;
  32. end;
  33. constructor TX11ModesNoModeSwitching.Create(ADisplay: PDisplay; AScreen: cint);
  34. begin
  35. inherited;
  36. FWidth := DisplayWidth(FDisplay, FScreen);
  37. FHeight := DisplayHeight(FDisplay, FScreen);
  38. end;
  39. procedure TX11ModesNoModeSwitching.GetModes(var AModes: TPTCModeList; ACurrentDesktopFormat: IPTCFormat);
  40. begin
  41. SetLength(AModes, 1);
  42. AModes[0] := TPTCMode.Create(FWidth,
  43. FHeight,
  44. ACurrentDesktopFormat);
  45. end;
  46. procedure TX11ModesNoModeSwitching.SetBestMode(AWidth, AHeight: Integer);
  47. begin
  48. FWidth := DisplayWidth(FDisplay, FScreen);
  49. FHeight := DisplayHeight(FDisplay, FScreen);
  50. end;
  51. procedure TX11ModesNoModeSwitching.RestorePreviousMode;
  52. begin
  53. { do nothing }
  54. end;
  55. function TX11ModesNoModeSwitching.GetWidth: Integer;
  56. begin
  57. Result := FWidth;
  58. end;
  59. function TX11ModesNoModeSwitching.GetHeight: Integer;
  60. begin
  61. Result := FHeight;
  62. end;
  63. {$IFDEF ENABLE_X11_EXTENSION_XRANDR}
  64. constructor TX11ModesXrandr.Create(ADisplay: PDisplay; AScreen: cint);
  65. var
  66. dummy1, dummy2: cint;
  67. Major, Minor: cint;
  68. begin
  69. inherited;
  70. FInMode := False;
  71. if not XRRQueryExtension(FDisplay, @dummy1, @dummy2) then
  72. raise TPTCError.Create('Xrandr extension not available');
  73. XRRQueryVersion(FDisplay, @Major, @Minor); // todo: check
  74. LOG('Xrandr version: ' + IntToStr(Major) + '.' + IntToStr(Minor));
  75. FRoot := RootWindow(FDisplay, FScreen);
  76. { is this necessary? }
  77. CreateScreenConfig;
  78. DestroyScreenConfig;
  79. end;
  80. destructor TX11ModesXrandr.Destroy;
  81. begin
  82. RestorePreviousMode;
  83. inherited;
  84. end;
  85. procedure TX11ModesXrandr.CreateScreenConfig;
  86. begin
  87. DestroyScreenConfig;
  88. FXRRConfig := XRRGetScreenInfo(FDisplay, FRoot);
  89. if FXRRConfig = nil then
  90. raise TPTCError.Create('XRRGetScreenInfo failed');
  91. end;
  92. procedure TX11ModesXrandr.DestroyScreenConfig;
  93. var
  94. tmp: PXRRScreenConfiguration;
  95. begin
  96. if FXRRConfig <> nil then
  97. begin
  98. tmp := FXRRConfig;
  99. FXRRConfig := nil;
  100. XRRFreeScreenConfigInfo(tmp);
  101. end;
  102. end;
  103. procedure TX11ModesXrandr.GetModes(var AModes: TPTCModeList; ACurrentDesktopFormat: IPTCFormat);
  104. var
  105. ScreenSizes: PXRRScreenSize;
  106. ScreenSizesNum: cint;
  107. I: Integer;
  108. begin
  109. ScreenSizes := XRRConfigSizes(FXRRConfig, @ScreenSizesNum);
  110. SetLength(AModes, ScreenSizesNum);
  111. for I := 0 to ScreenSizesNum - 1 do
  112. AModes[I] := TPTCMode.Create(ScreenSizes[I].width,
  113. ScreenSizes[I].height,
  114. ACurrentDesktopFormat);
  115. end;
  116. class function TX11ModesXrandr.FindBestMode(AScreenSizes: PXRRScreenSize;
  117. AScreenSizesNum: Integer;
  118. ARequestedWidth,
  119. ARequestedHeight: Integer): Integer;
  120. var
  121. I: Integer;
  122. min_diff: Integer;
  123. d_x, d_y: Integer;
  124. found_mode: Integer;
  125. begin
  126. { Find exact match }
  127. for I := 0 to AScreenSizesNum - 1 do
  128. if (AScreenSizes[I].width = ARequestedWidth) and
  129. (AScreenSizes[I].height = ARequestedHeight) then
  130. exit(I);
  131. { try to find a mode that matches the width first }
  132. for I := 0 to AScreenSizesNum - 1 do
  133. if (AScreenSizes[I].width = ARequestedWidth) and
  134. (AScreenSizes[I].height >= ARequestedHeight) then
  135. exit(I);
  136. { Next try to match the height }
  137. for I := 0 to AScreenSizesNum - 1 do
  138. if (AScreenSizes[I].width >= ARequestedWidth) and
  139. (AScreenSizes[I].height = ARequestedHeight) then
  140. exit(I);
  141. { Finally, find the mode that is bigger than the requested one and makes }
  142. { the least difference }
  143. found_mode := -1;
  144. min_diff := High(Integer);
  145. for I := 0 to AScreenSizesNum - 1 do
  146. if (AScreenSizes[I].width >= ARequestedWidth) and
  147. (AScreenSizes[I].height >= ARequestedHeight) then
  148. begin
  149. d_x := Sqr(AScreenSizes[I].width - ARequestedWidth);
  150. d_y := Sqr(AScreenSizes[I].height - ARequestedHeight);
  151. if (d_x + d_y) < min_diff then
  152. begin
  153. min_diff := d_x + d_y;
  154. found_mode := I;
  155. end;
  156. end;
  157. if found_mode <> -1 then
  158. exit(found_mode);
  159. raise TPTCError.Create('Cannot find matching video mode');
  160. end;
  161. procedure TX11ModesXrandr.SetBestMode(AWidth, AHeight: Integer);
  162. var
  163. ScreenSizes: PXRRScreenSize;
  164. ScreenSizesNum: cint;
  165. BestMode: Integer;
  166. CurrentRotation: TRotation;
  167. begin
  168. CreateScreenConfig;
  169. try
  170. if not FInMode then
  171. SaveCurrentMode;
  172. ScreenSizes := XRRConfigSizes(FXRRConfig, @ScreenSizesNum);
  173. BestMode := FindBestMode(ScreenSizes, ScreenSizesNum, AWidth, AHeight);
  174. FWidth := ScreenSizes[BestMode].width;
  175. FHeight := ScreenSizes[BestMode].height;
  176. XRRConfigCurrentConfiguration(FXRRConfig, @CurrentRotation);
  177. X11CheckSuccess(XRRSetScreenConfig(FDisplay, FXRRConfig, FRoot, BestMode,
  178. CurrentRotation, CurrentTime),
  179. 'XRRSetScreenConfig failed');
  180. FInMode := True;
  181. finally
  182. DestroyScreenConfig;
  183. end;
  184. end;
  185. procedure TX11ModesXrandr.SaveCurrentMode;
  186. var
  187. SizeIndex: TSizeID;
  188. ScreenSizes: PXRRScreenSize;
  189. ScreenSizesNum: cint;
  190. begin
  191. LOG('xrandr: saving previous mode');
  192. SizeIndex := XRRConfigCurrentConfiguration(FXRRConfig, @FSavedMode.Rotation);
  193. FSavedMode.Rate := XRRConfigCurrentRate(FXRRConfig);
  194. ScreenSizes := XRRConfigSizes(FXRRConfig, @ScreenSizesNum);
  195. if (SizeIndex < 0) or (SizeIndex >= ScreenSizesNum) then
  196. raise TPTCError.Create('XRRConfigCurrentConfiguration returned a size index outside the range of screen sizes');
  197. FSavedMode.ScreenSize := ScreenSizes[SizeIndex];
  198. end;
  199. procedure TX11ModesXrandr.RestorePreviousMode;
  200. var
  201. I: Integer;
  202. SizeIndex: cint;
  203. ScreenSizes: PXRRScreenSize;
  204. ScreenSizesNum: cint;
  205. begin
  206. if not FInMode then
  207. exit;
  208. LOG('xrandr: restoring previous mode');
  209. CreateScreenConfig;
  210. try
  211. SizeIndex := -1;
  212. ScreenSizes := XRRConfigSizes(FXRRConfig, @ScreenSizesNum);
  213. for I := 0 to ScreenSizesNum - 1 do
  214. if (ScreenSizes[I].width = FSavedMode.ScreenSize.width ) and
  215. (ScreenSizes[I].height = FSavedMode.ScreenSize.height ) and
  216. (ScreenSizes[I].mwidth = FSavedMode.ScreenSize.mwidth ) and
  217. (ScreenSizes[I].mheight = FSavedMode.ScreenSize.mheight) then
  218. begin
  219. SizeIndex := I;
  220. Break;
  221. end;
  222. if SizeIndex = -1 then
  223. for I := 0 to ScreenSizesNum - 1 do
  224. if (ScreenSizes[I].width = FSavedMode.ScreenSize.width ) and
  225. (ScreenSizes[I].height = FSavedMode.ScreenSize.height) then
  226. begin
  227. SizeIndex := I;
  228. Break;
  229. end;
  230. if SizeIndex = -1 then
  231. raise TPTCError.Create('xrandr: saved mode size not found in size list');
  232. X11CheckSuccess(XRRSetScreenConfigAndRate(FDisplay,
  233. FXRRConfig,
  234. FRoot,
  235. SizeIndex,
  236. FSavedMode.Rotation,
  237. FSavedMode.Rate,
  238. CurrentTime),
  239. 'XRRSetScreenConfigAndRate failed');
  240. FInMode := False;
  241. finally
  242. DestroyScreenConfig;
  243. end;
  244. end;
  245. function TX11ModesXrandr.GetWidth: Integer;
  246. begin
  247. Result := FWidth;
  248. end;
  249. function TX11ModesXrandr.GetHeight: Integer;
  250. begin
  251. Result := FHeight;
  252. end;
  253. {$ENDIF ENABLE_X11_EXTENSION_XRANDR}
  254. {$IFDEF ENABLE_X11_EXTENSION_XF86VIDMODE}
  255. constructor TX11ModesXF86VidMode.Create(ADisplay: PDisplay; AScreen: Integer);
  256. var
  257. dummy1, dummy2: cint;
  258. begin
  259. inherited;
  260. FSavedMode := nil;
  261. FSavedDotClock := 0;
  262. FModeList := nil;
  263. FModeListCount := 0;
  264. if not XF86VidModeQueryExtension(FDisplay, @dummy1, @dummy2) then
  265. raise TPTCError.Create('VidMode extension not available');
  266. end;
  267. destructor TX11ModesXF86VidMode.Destroy;
  268. begin
  269. if FSavedMode <> nil then
  270. begin
  271. RestorePreviousMode;
  272. if FSavedMode^.privsize <> 0 then
  273. XFree(FSavedMode^.c_private);
  274. Dispose(FSavedMode);
  275. end;
  276. if FModeList <> nil then
  277. XFree(FModeList);
  278. inherited Destroy;
  279. end;
  280. {todo: move the saving of the previous mode to a separate function...}
  281. procedure TX11ModesXF86VidMode.RetrieveModeList;
  282. begin
  283. { if we have been called before, do nothing }
  284. if FModeList <> nil then
  285. exit;
  286. { Save previous mode }
  287. New(FSavedMode);
  288. FillChar(FSavedMode^, SizeOf(FSavedMode^), 0);
  289. XF86VidModeGetModeLine(FDisplay, FScreen, @FSavedDotClock, FSavedMode);
  290. { Get all available video modes }
  291. XF86VidModeGetAllModeLines(FDisplay, FScreen, @FModeListCount, @FModeList);
  292. end;
  293. procedure TX11ModesXF86VidMode.GetModes(var AModes: TPTCModeList; ACurrentDesktopFormat: IPTCFormat);
  294. var
  295. I: Integer;
  296. begin
  297. RetrieveModeList;
  298. SetLength(AModes, FModeListCount);
  299. for I := 0 to FModeListCount - 1 do
  300. AModes[I] := TPTCMode.Create(FModeList[I]^.hdisplay, FModeList[I]^.vdisplay, ACurrentDesktopFormat);
  301. end;
  302. function TX11ModesXF86VidMode.FindNumberOfBestMode(AWidth, AHeight: Integer): Integer;
  303. var
  304. min_diff: Integer;
  305. d_x, d_y: Integer;
  306. found_mode: Integer;
  307. I: Integer;
  308. begin
  309. { try an exact match }
  310. for I := 0 to FModeListCount - 1 do
  311. if (FModeList[I]^.hdisplay = AWidth) and (FModeList[I]^.vdisplay = AHeight) then
  312. exit(I);
  313. { try to find a mode that matches the width first }
  314. for I := 0 to FModeListCount - 1 do
  315. if (FModeList[I]^.hdisplay = AWidth) and (FModeList[I]^.vdisplay >= AHeight) then
  316. exit(I);
  317. { Next try to match the height }
  318. for I := 0 to FModeListCount - 1 do
  319. if (FModeList[I]^.hdisplay >= AWidth) and (FModeList[I]^.vdisplay = AHeight) then
  320. exit(I);
  321. { Finally, find the mode that is bigger than the requested one and makes }
  322. { the least difference }
  323. found_mode := -1;
  324. min_diff := High(Integer);
  325. for I := 0 to FModeListCount - 1 do
  326. if (FModeList[I]^.hdisplay >= AWidth) and (FModeList[I]^.vdisplay >= AHeight) then
  327. begin
  328. d_x := Sqr(FModeList[I]^.hdisplay - AWidth);
  329. d_y := Sqr(FModeList[I]^.vdisplay - AHeight);
  330. if (d_x + d_y) < min_diff then
  331. begin
  332. min_diff := d_x + d_y;
  333. found_mode := I;
  334. end;
  335. end;
  336. if found_mode <> -1 then
  337. Result := found_mode
  338. else
  339. raise TPTCError.Create('Cannot find matching video mode');
  340. end;
  341. procedure TX11ModesXF86VidMode.SetBestMode(AWidth, AHeight: Integer);
  342. var
  343. BestMode: Integer;
  344. begin
  345. RetrieveModeList;
  346. BestMode := FindNumberOfBestMode(AWidth, AHeight);
  347. if not XF86VidModeSwitchToMode(FDisplay, FScreen, FModeList[BestMode]) then
  348. raise TPTCError.Create('Error switching to the requested video mode');
  349. FWidth := FModeList[BestMode]^.hdisplay;
  350. FHeight := FModeList[BestMode]^.vdisplay;
  351. XWarpPointer(FDisplay, None, RootWindow(FDisplay, FScreen), 0, 0, 0, 0,
  352. FWidth div 2,
  353. FHeight div 2);
  354. if not XF86VidModeSetViewPort(FDisplay, FScreen, 0, 0) then
  355. raise TPTCError.Create('Error moving the viewport to the upper-left corner');
  356. end;
  357. procedure TX11ModesXF86VidMode.RestorePreviousMode;
  358. var
  359. ModeInfo: TXF86VidModeModeInfo;
  360. begin
  361. if FSavedMode <> nil then
  362. begin
  363. {FSavedMode is a TXF86VidModeModeLine, but XF86VidModeSwitchToMode wants a
  364. TXF86VidModeModeInfo :}
  365. FillChar(ModeInfo, SizeOf(ModeInfo), 0);
  366. ModeInfo.dotclock := FSavedDotClock;
  367. ModeInfo.hdisplay := FSavedMode^.hdisplay;
  368. ModeInfo.hsyncstart := FSavedMode^.hsyncstart;
  369. ModeInfo.hsyncend := FSavedMode^.hsyncend;
  370. ModeInfo.htotal := FSavedMode^.htotal;
  371. ModeInfo.vdisplay := FSavedMode^.vdisplay;
  372. ModeInfo.vsyncstart := FSavedMode^.vsyncstart;
  373. ModeInfo.vsyncend := FSavedMode^.vsyncend;
  374. ModeInfo.vtotal := FSavedMode^.vtotal;
  375. ModeInfo.flags := FSavedMode^.flags;
  376. ModeInfo.privsize := FSavedMode^.privsize;
  377. ModeInfo.c_private := FSavedMode^.c_private;
  378. XF86VidModeSwitchToMode(FDisplay, FScreen, @ModeInfo);
  379. end;
  380. end;
  381. function TX11ModesXF86VidMode.GetWidth: Integer;
  382. begin
  383. Result := FWidth;
  384. end;
  385. function TX11ModesXF86VidMode.GetHeight: Integer;
  386. begin
  387. Result := FHeight;
  388. end;
  389. {$ENDIF ENABLE_X11_EXTENSION_XF86VIDMODE}