1/* 2 * 3 * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc. 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and its 6 * documentation for any purpose is hereby granted without fee, provided that 7 * the above copyright notice appear in all copies and that both that 8 * copyright notice and this permission notice appear in supporting 9 * documentation, and that the name of Keith Packard not be used in 10 * advertising or publicity pertaining to distribution of the software without 11 * specific, written prior permission. Keith Packard makes no 12 * representations about the suitability of this software for any purpose. It 13 * is provided "as is" without express or implied warranty. 14 * 15 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 17 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 21 * PERFORMANCE OF THIS SOFTWARE. 22 */ 23 24#ifdef HAVE_XORG_CONFIG_H 25#include <xorg-config.h> 26#endif 27 28#include <X11/X.h> 29#include "os.h" 30#include "globals.h" 31#include "xf86.h" 32#include "xf86str.h" 33#include "xf86Priv.h" 34#include "xf86DDC.h" 35#include "mipointer.h" 36#include <randrstr.h> 37#include "inputstr.h" 38 39typedef struct _xf86RandRInfo { 40 CloseScreenProcPtr CloseScreen; 41 int virtualX; 42 int virtualY; 43 int mmWidth; 44 int mmHeight; 45 Rotation rotation; 46} XF86RandRInfoRec, *XF86RandRInfoPtr; 47 48static DevPrivateKeyRec xf86RandRKeyRec; 49static DevPrivateKey xf86RandRKey; 50 51#define XF86RANDRINFO(p) ((XF86RandRInfoPtr)dixLookupPrivate(&(p)->devPrivates, xf86RandRKey)) 52 53static int 54xf86RandRModeRefresh(DisplayModePtr mode) 55{ 56 if (mode->VRefresh) 57 return (int) (mode->VRefresh + 0.5); 58 else if (mode->Clock == 0) 59 return 0; 60 else 61 return (int) (mode->Clock * 1000.0 / mode->HTotal / mode->VTotal + 0.5); 62} 63 64static Bool 65xf86RandRGetInfo(ScreenPtr pScreen, Rotation * rotations) 66{ 67 RRScreenSizePtr pSize; 68 ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen); 69 XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen); 70 DisplayModePtr mode; 71 int refresh0 = 60; 72 xorgRRModeMM RRModeMM; 73 74 *rotations = RR_Rotate_0; 75 76 for (mode = scrp->modes; mode != NULL; mode = mode->next) { 77 int refresh = xf86RandRModeRefresh(mode); 78 79 if (mode == scrp->modes) 80 refresh0 = refresh; 81 82 RRModeMM.mode = mode; 83 RRModeMM.virtX = randrp->virtualX; 84 RRModeMM.virtY = randrp->virtualY; 85 RRModeMM.mmWidth = randrp->mmWidth; 86 RRModeMM.mmHeight = randrp->mmHeight; 87 88 if (scrp->DriverFunc) { 89 (*scrp->DriverFunc) (scrp, RR_GET_MODE_MM, &RRModeMM); 90 } 91 92 pSize = RRRegisterSize(pScreen, 93 mode->HDisplay, mode->VDisplay, 94 RRModeMM.mmWidth, RRModeMM.mmHeight); 95 if (!pSize) 96 return FALSE; 97 RRRegisterRate(pScreen, pSize, refresh); 98 if (mode == scrp->currentMode && 99 mode->HDisplay == scrp->virtualX && 100 mode->VDisplay == scrp->virtualY) 101 RRSetCurrentConfig(pScreen, randrp->rotation, refresh, pSize); 102 if (mode->next == scrp->modes) 103 break; 104 } 105 if (scrp->currentMode->HDisplay != randrp->virtualX || 106 scrp->currentMode->VDisplay != randrp->virtualY) { 107 mode = scrp->modes; 108 109 RRModeMM.mode = NULL; 110 RRModeMM.virtX = randrp->virtualX; 111 RRModeMM.virtY = randrp->virtualY; 112 RRModeMM.mmWidth = randrp->mmWidth; 113 RRModeMM.mmHeight = randrp->mmHeight; 114 115 if (scrp->DriverFunc) { 116 (*scrp->DriverFunc) (scrp, RR_GET_MODE_MM, &RRModeMM); 117 } 118 119 pSize = RRRegisterSize(pScreen, 120 randrp->virtualX, randrp->virtualY, 121 RRModeMM.mmWidth, RRModeMM.mmHeight); 122 if (!pSize) 123 return FALSE; 124 RRRegisterRate(pScreen, pSize, refresh0); 125 if (scrp->virtualX == randrp->virtualX && 126 scrp->virtualY == randrp->virtualY) { 127 RRSetCurrentConfig(pScreen, randrp->rotation, refresh0, pSize); 128 } 129 } 130 131 /* If there is driver support for randr, let it set our supported rotations */ 132 if (scrp->DriverFunc) { 133 xorgRRRotation RRRotation; 134 135 RRRotation.RRRotations = *rotations; 136 if (!(*scrp->DriverFunc) (scrp, RR_GET_INFO, &RRRotation)) 137 return TRUE; 138 *rotations = RRRotation.RRRotations; 139 } 140 141 return TRUE; 142} 143 144static Bool 145xf86RandRSetMode(ScreenPtr pScreen, 146 DisplayModePtr mode, 147 Bool useVirtual, int mmWidth, int mmHeight) 148{ 149 ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen); 150 XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen); 151 int oldWidth = pScreen->width; 152 int oldHeight = pScreen->height; 153 int oldmmWidth = pScreen->mmWidth; 154 int oldmmHeight = pScreen->mmHeight; 155 int oldVirtualX = scrp->virtualX; 156 int oldVirtualY = scrp->virtualY; 157 WindowPtr pRoot = pScreen->root; 158 Bool ret = TRUE; 159 160 if (pRoot && scrp->vtSema) 161 (*scrp->EnableDisableFBAccess) (scrp, FALSE); 162 if (useVirtual) { 163 scrp->virtualX = randrp->virtualX; 164 scrp->virtualY = randrp->virtualY; 165 } 166 else { 167 scrp->virtualX = mode->HDisplay; 168 scrp->virtualY = mode->VDisplay; 169 } 170 171 /* 172 * The DIX forgets the physical dimensions we passed into RRRegisterSize, so 173 * reconstruct them if possible. 174 */ 175 if (scrp->DriverFunc) { 176 xorgRRModeMM RRModeMM; 177 178 RRModeMM.mode = mode; 179 RRModeMM.virtX = scrp->virtualX; 180 RRModeMM.virtY = scrp->virtualY; 181 RRModeMM.mmWidth = mmWidth; 182 RRModeMM.mmHeight = mmHeight; 183 184 (*scrp->DriverFunc) (scrp, RR_GET_MODE_MM, &RRModeMM); 185 186 mmWidth = RRModeMM.mmWidth; 187 mmHeight = RRModeMM.mmHeight; 188 } 189 if (randrp->rotation & (RR_Rotate_90 | RR_Rotate_270)) { 190 /* If the screen is rotated 90 or 270 degrees, swap the sizes. */ 191 pScreen->width = scrp->virtualY; 192 pScreen->height = scrp->virtualX; 193 pScreen->mmWidth = mmHeight; 194 pScreen->mmHeight = mmWidth; 195 } 196 else { 197 pScreen->width = scrp->virtualX; 198 pScreen->height = scrp->virtualY; 199 pScreen->mmWidth = mmWidth; 200 pScreen->mmHeight = mmHeight; 201 } 202 if (!xf86SwitchMode(pScreen, mode)) { 203 pScreen->width = oldWidth; 204 pScreen->height = oldHeight; 205 pScreen->mmWidth = oldmmWidth; 206 pScreen->mmHeight = oldmmHeight; 207 scrp->virtualX = oldVirtualX; 208 scrp->virtualY = oldVirtualY; 209 ret = FALSE; 210 } 211 /* 212 * Make sure the layout is correct 213 */ 214 xf86ReconfigureLayout(); 215 216 if (scrp->vtSema) { 217 /* 218 * Make sure the whole screen is visible 219 */ 220 xf86SetViewport (pScreen, pScreen->width, pScreen->height); 221 xf86SetViewport (pScreen, 0, 0); 222 if (pRoot) 223 (*scrp->EnableDisableFBAccess) (scrp, TRUE); 224 } 225 return ret; 226} 227 228static Bool 229xf86RandRSetConfig(ScreenPtr pScreen, 230 Rotation rotation, int rate, RRScreenSizePtr pSize) 231{ 232 ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen); 233 XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen); 234 DisplayModePtr mode; 235 int pos[MAXDEVICES][2]; 236 Bool useVirtual = FALSE; 237 Rotation oldRotation = randrp->rotation; 238 DeviceIntPtr dev; 239 Bool view_adjusted = FALSE; 240 241 for (dev = inputInfo.devices; dev; dev = dev->next) { 242 if (!IsMaster(dev) && !IsFloating(dev)) 243 continue; 244 245 miPointerGetPosition(dev, &pos[dev->id][0], &pos[dev->id][1]); 246 } 247 248 for (mode = scrp->modes;; mode = mode->next) { 249 if (mode->HDisplay == pSize->width && 250 mode->VDisplay == pSize->height && 251 (rate == 0 || xf86RandRModeRefresh(mode) == rate)) 252 break; 253 if (mode->next == scrp->modes) { 254 if (pSize->width == randrp->virtualX && 255 pSize->height == randrp->virtualY) { 256 mode = scrp->modes; 257 useVirtual = TRUE; 258 break; 259 } 260 return FALSE; 261 } 262 } 263 264 if (randrp->rotation != rotation) { 265 266 /* Have the driver do its thing. */ 267 if (scrp->DriverFunc) { 268 xorgRRRotation RRRotation; 269 270 RRRotation.RRConfig.rotation = rotation; 271 RRRotation.RRConfig.rate = rate; 272 RRRotation.RRConfig.width = pSize->width; 273 RRRotation.RRConfig.height = pSize->height; 274 275 /* 276 * Currently we need to rely on HW support for rotation. 277 */ 278 if (!(*scrp->DriverFunc) (scrp, RR_SET_CONFIG, &RRRotation)) 279 return FALSE; 280 } 281 else 282 return FALSE; 283 284 randrp->rotation = rotation; 285 } 286 287 if (!xf86RandRSetMode 288 (pScreen, mode, useVirtual, pSize->mmWidth, pSize->mmHeight)) { 289 if (randrp->rotation != oldRotation) { 290 /* Have the driver undo its thing. */ 291 if (scrp->DriverFunc) { 292 xorgRRRotation RRRotation; 293 294 RRRotation.RRConfig.rotation = oldRotation; 295 RRRotation.RRConfig.rate = 296 xf86RandRModeRefresh(scrp->currentMode); 297 RRRotation.RRConfig.width = scrp->virtualX; 298 RRRotation.RRConfig.height = scrp->virtualY; 299 (*scrp->DriverFunc) (scrp, RR_SET_CONFIG, &RRRotation); 300 } 301 302 randrp->rotation = oldRotation; 303 } 304 return FALSE; 305 } 306 307 update_desktop_dimensions(); 308 309 /* 310 * Move the cursor back where it belongs; SwitchMode repositions it 311 * FIXME: duplicated code, see modes/xf86RandR12.c 312 */ 313 for (dev = inputInfo.devices; dev; dev = dev->next) { 314 if (!IsMaster(dev) && !IsFloating(dev)) 315 continue; 316 317 if (pScreen == miPointerGetScreen(dev)) { 318 int px = pos[dev->id][0]; 319 int py = pos[dev->id][1]; 320 321 px = (px >= pScreen->width ? (pScreen->width - 1) : px); 322 py = (py >= pScreen->height ? (pScreen->height - 1) : py); 323 324 /* Setting the viewpoint makes only sense on one device */ 325 if (!view_adjusted && IsMaster(dev)) { 326 xf86SetViewport(pScreen, px, py); 327 view_adjusted = TRUE; 328 } 329 330 (*pScreen->SetCursorPosition) (dev, pScreen, px, py, FALSE); 331 } 332 } 333 334 return TRUE; 335} 336 337/* 338 * Reset size back to original 339 */ 340static Bool 341xf86RandRCloseScreen(ScreenPtr pScreen) 342{ 343 ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen); 344 XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen); 345 346 scrp->virtualX = pScreen->width = randrp->virtualX; 347 scrp->virtualY = pScreen->height = randrp->virtualY; 348 scrp->currentMode = scrp->modes; 349 pScreen->CloseScreen = randrp->CloseScreen; 350 free(randrp); 351 dixSetPrivate(&pScreen->devPrivates, xf86RandRKey, NULL); 352 return (*pScreen->CloseScreen) (pScreen); 353} 354 355Rotation 356xf86GetRotation(ScreenPtr pScreen) 357{ 358 if (xf86RandRKey == NULL) 359 return RR_Rotate_0; 360 361 return XF86RANDRINFO(pScreen)->rotation; 362} 363 364/* Function to change RandR's idea of the virtual screen size */ 365Bool 366xf86RandRSetNewVirtualAndDimensions(ScreenPtr pScreen, 367 int newvirtX, int newvirtY, int newmmWidth, 368 int newmmHeight, Bool resetMode) 369{ 370 XF86RandRInfoPtr randrp; 371 372 if (xf86RandRKey == NULL) 373 return FALSE; 374 375 randrp = XF86RANDRINFO(pScreen); 376 if (randrp == NULL) 377 return FALSE; 378 379 if (newvirtX > 0) 380 randrp->virtualX = newvirtX; 381 382 if (newvirtY > 0) 383 randrp->virtualY = newvirtY; 384 385 if (newmmWidth > 0) 386 randrp->mmWidth = newmmWidth; 387 388 if (newmmHeight > 0) 389 randrp->mmHeight = newmmHeight; 390 391 /* This is only for during server start */ 392 if (resetMode) { 393 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 394 return (xf86RandRSetMode(pScreen, 395 pScrn->currentMode, 396 TRUE, pScreen->mmWidth, pScreen->mmHeight)); 397 } 398 399 return TRUE; 400} 401 402Bool 403xf86RandRInit(ScreenPtr pScreen) 404{ 405 rrScrPrivPtr rp; 406 XF86RandRInfoPtr randrp; 407 ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen); 408 409#ifdef PANORAMIX 410 /* XXX disable RandR when using Xinerama */ 411 if (!noPanoramiXExtension) 412 return TRUE; 413#endif 414 415 xf86RandRKey = &xf86RandRKeyRec; 416 417 if (!dixRegisterPrivateKey(&xf86RandRKeyRec, PRIVATE_SCREEN, 0)) 418 return FALSE; 419 420 randrp = malloc(sizeof(XF86RandRInfoRec)); 421 if (!randrp) 422 return FALSE; 423 424 if (!RRScreenInit(pScreen)) { 425 free(randrp); 426 return FALSE; 427 } 428 rp = rrGetScrPriv(pScreen); 429 rp->rrGetInfo = xf86RandRGetInfo; 430 rp->rrSetConfig = xf86RandRSetConfig; 431 432 randrp->virtualX = scrp->virtualX; 433 randrp->virtualY = scrp->virtualY; 434 randrp->mmWidth = pScreen->mmWidth; 435 randrp->mmHeight = pScreen->mmHeight; 436 437 randrp->CloseScreen = pScreen->CloseScreen; 438 pScreen->CloseScreen = xf86RandRCloseScreen; 439 440 randrp->rotation = RR_Rotate_0; 441 442 dixSetPrivate(&pScreen->devPrivates, xf86RandRKey, randrp); 443 return TRUE; 444} 445