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