quartzRandR.c (16270B)
1 /* 2 * Quartz-specific support for the XRandR extension 3 * 4 * Copyright (c) 2001-2004 Greg Parker and Torrey T. Lyons, 5 * 2010 Jan Hauffa. 6 * 2010-2012 Apple Inc. 7 * All Rights Reserved. 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a 10 * copy of this software and associated documentation files (the "Software"), 11 * to deal in the Software without restriction, including without limitation 12 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 * and/or sell copies of the Software, and to permit persons to whom the 14 * Software is furnished to do so, subject to the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included in 17 * all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 * DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the sale, 29 * use or other dealings in this Software without prior written authorization. 30 */ 31 32 #include "sanitizedCarbon.h" 33 34 #ifdef HAVE_DIX_CONFIG_H 35 #include <dix-config.h> 36 #endif 37 38 #include "quartzRandR.h" 39 #include "quartz.h" 40 #include "darwin.h" 41 42 #include "X11Application.h" 43 44 #include <X11/extensions/randr.h> 45 #include <randrstr.h> 46 #include <IOKit/graphics/IOGraphicsTypes.h> 47 48 /* TODO: UGLY, find a better way! 49 * We want to ignore kXquartzDisplayChanged which are generated by us 50 */ 51 static Bool ignore_next_fake_mode_update = FALSE; 52 53 #define FAKE_REFRESH_ROOTLESS 1 54 #define FAKE_REFRESH_FULLSCREEN 2 55 56 #define DEFAULT_REFRESH 60 57 #define kDisplayModeUsableFlags (kDisplayModeValidFlag | kDisplayModeSafeFlag) 58 59 #define CALLBACK_SUCCESS 0 60 #define CALLBACK_CONTINUE 1 61 #define CALLBACK_ERROR -1 62 63 typedef int (*QuartzModeCallback) 64 (ScreenPtr, QuartzModeInfoPtr, void *); 65 66 static void 67 QuartzRandRGetModeInfo(CGDisplayModeRef modeRef, 68 QuartzModeInfoPtr pMode) 69 { 70 pMode->width = CGDisplayModeGetWidth(modeRef); 71 pMode->height = CGDisplayModeGetHeight(modeRef); 72 pMode->refresh = (int)(CGDisplayModeGetRefreshRate(modeRef) + 0.5); 73 if (pMode->refresh == 0) 74 pMode->refresh = DEFAULT_REFRESH; 75 pMode->ref = NULL; 76 pMode->pSize = NULL; 77 } 78 79 static Bool 80 QuartzRandRCopyCurrentModeInfo(CGDirectDisplayID screenId, 81 QuartzModeInfoPtr pMode) 82 { 83 CGDisplayModeRef curModeRef = CGDisplayCopyDisplayMode(screenId); 84 if (!curModeRef) 85 return FALSE; 86 87 QuartzRandRGetModeInfo(curModeRef, pMode); 88 pMode->ref = curModeRef; 89 return TRUE; 90 } 91 92 static Bool 93 QuartzRandRSetCGMode(CGDirectDisplayID screenId, 94 QuartzModeInfoPtr pMode) 95 { 96 CGDisplayModeRef modeRef = (CGDisplayModeRef)pMode->ref; 97 if (!modeRef) 98 return FALSE; 99 100 return (CGDisplaySetDisplayMode(screenId, modeRef, 101 NULL) == kCGErrorSuccess); 102 } 103 104 static Bool 105 QuartzRandREnumerateModes(ScreenPtr pScreen, 106 QuartzModeCallback callback, 107 void *data) 108 { 109 Bool retval = FALSE; 110 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 111 112 /* Just an 800x600 fallback if we have no attached heads */ 113 if (pQuartzScreen->displayIDs) { 114 CGDisplayModeRef curModeRef, modeRef; 115 CFStringRef curPixelEnc, pixelEnc; 116 CFComparisonResult pixelEncEqual; 117 CFArrayRef modes; 118 QuartzModeInfo modeInfo; 119 int i; 120 CGDirectDisplayID screenId = pQuartzScreen->displayIDs[0]; 121 122 curModeRef = CGDisplayCopyDisplayMode(screenId); 123 if (!curModeRef) 124 return FALSE; 125 curPixelEnc = CGDisplayModeCopyPixelEncoding(curModeRef); 126 CGDisplayModeRelease(curModeRef); 127 128 modes = CGDisplayCopyAllDisplayModes(screenId, NULL); 129 if (!modes) { 130 CFRelease(curPixelEnc); 131 return FALSE; 132 } 133 for (i = 0; i < CFArrayGetCount(modes); i++) { 134 int cb; 135 modeRef = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i); 136 137 /* Skip modes that are not usable on the current display or have a 138 different pixel encoding than the current mode. */ 139 if ((CGDisplayModeGetIOFlags(modeRef) & 140 kDisplayModeUsableFlags) != 141 kDisplayModeUsableFlags) 142 continue; 143 pixelEnc = CGDisplayModeCopyPixelEncoding(modeRef); 144 pixelEncEqual = CFStringCompare(pixelEnc, curPixelEnc, 0); 145 CFRelease(pixelEnc); 146 if (pixelEncEqual != kCFCompareEqualTo) 147 continue; 148 149 QuartzRandRGetModeInfo(modeRef, &modeInfo); 150 modeInfo.ref = modeRef; 151 cb = callback(pScreen, &modeInfo, data); 152 if (cb == CALLBACK_CONTINUE) { 153 retval = TRUE; 154 } 155 else if (cb == CALLBACK_SUCCESS) { 156 CFRelease(modes); 157 CFRelease(curPixelEnc); 158 return TRUE; 159 } 160 else if (cb == CALLBACK_ERROR) { 161 CFRelease(modes); 162 CFRelease(curPixelEnc); 163 return FALSE; 164 } 165 } 166 167 CFRelease(modes); 168 CFRelease(curPixelEnc); 169 } 170 171 switch (callback(pScreen, &pQuartzScreen->rootlessMode, data)) { 172 case CALLBACK_SUCCESS: 173 return TRUE; 174 175 case CALLBACK_ERROR: 176 return FALSE; 177 178 case CALLBACK_CONTINUE: 179 retval = TRUE; 180 181 default: 182 break; 183 } 184 185 switch (callback(pScreen, &pQuartzScreen->fullscreenMode, data)) { 186 case CALLBACK_SUCCESS: 187 return TRUE; 188 189 case CALLBACK_ERROR: 190 return FALSE; 191 192 case CALLBACK_CONTINUE: 193 retval = TRUE; 194 195 default: 196 break; 197 } 198 199 return retval; 200 } 201 202 static Bool 203 QuartzRandRModesEqual(QuartzModeInfoPtr pMode1, 204 QuartzModeInfoPtr pMode2) 205 { 206 return (pMode1->width == pMode2->width) && 207 (pMode1->height == pMode2->height) && 208 (pMode1->refresh == pMode2->refresh); 209 } 210 211 static Bool 212 QuartzRandRRegisterMode(ScreenPtr pScreen, 213 QuartzModeInfoPtr pMode) 214 { 215 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 216 Bool isCurrentMode = QuartzRandRModesEqual(&pQuartzScreen->currentMode, 217 pMode); 218 219 /* TODO: DPI */ 220 pMode->pSize = 221 RRRegisterSize(pScreen, pMode->width, pMode->height, pScreen->mmWidth, 222 pScreen->mmHeight); 223 if (pMode->pSize) { 224 //DEBUG_LOG("registering: %d x %d @ %d %s\n", (int)pMode->width, (int)pMode->height, (int)pMode->refresh, isCurrentMode ? "*" : ""); 225 RRRegisterRate(pScreen, pMode->pSize, pMode->refresh); 226 227 if (isCurrentMode) 228 RRSetCurrentConfig(pScreen, RR_Rotate_0, pMode->refresh, 229 pMode->pSize); 230 231 return TRUE; 232 } 233 return FALSE; 234 } 235 236 static int 237 QuartzRandRRegisterModeCallback(ScreenPtr pScreen, 238 QuartzModeInfoPtr pMode, 239 void *data __unused) 240 { 241 if (QuartzRandRRegisterMode(pScreen, pMode)) { 242 return CALLBACK_CONTINUE; 243 } 244 else { 245 return CALLBACK_ERROR; 246 } 247 } 248 249 static Bool 250 QuartzRandRSetMode(ScreenPtr pScreen, QuartzModeInfoPtr pMode, 251 BOOL doRegister) 252 { 253 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 254 Bool captureDisplay = 255 (pMode->refresh != FAKE_REFRESH_FULLSCREEN && pMode->refresh != 256 FAKE_REFRESH_ROOTLESS); 257 CGDirectDisplayID screenId; 258 259 if (pQuartzScreen->displayIDs == NULL) 260 return FALSE; 261 262 screenId = pQuartzScreen->displayIDs[0]; 263 if (XQuartzShieldingWindowLevel == 0 && captureDisplay) { 264 if (!X11ApplicationCanEnterRandR()) 265 return FALSE; 266 CGCaptureAllDisplays(); 267 XQuartzShieldingWindowLevel = CGShieldingWindowLevel(); // 2147483630 268 DEBUG_LOG("Display captured. ShieldWindowID: %u, Shield level: %d\n", 269 CGShieldingWindowID(screenId), XQuartzShieldingWindowLevel); 270 } 271 272 if (pQuartzScreen->currentMode.ref && 273 CFEqual(pMode->ref, pQuartzScreen->currentMode.ref)) { 274 DEBUG_LOG("Requested RandR resolution matches current CG mode\n"); 275 } 276 if (QuartzRandRSetCGMode(screenId, pMode)) { 277 ignore_next_fake_mode_update = TRUE; 278 } 279 else { 280 DEBUG_LOG("Error while requesting CG resolution change.\n"); 281 return FALSE; 282 } 283 284 /* If the client requested the fake rootless mode, switch to rootless. 285 * Otherwise, force fullscreen mode. 286 */ 287 QuartzSetRootless(pMode->refresh == FAKE_REFRESH_ROOTLESS); 288 if (pMode->refresh != FAKE_REFRESH_ROOTLESS) { 289 QuartzShowFullscreen(TRUE); 290 } 291 292 if (pQuartzScreen->currentMode.ref) 293 CFRelease(pQuartzScreen->currentMode.ref); 294 pQuartzScreen->currentMode = *pMode; 295 if (pQuartzScreen->currentMode.ref) 296 CFRetain(pQuartzScreen->currentMode.ref); 297 298 if (XQuartzShieldingWindowLevel != 0 && !captureDisplay) { 299 CGReleaseAllDisplays(); 300 XQuartzShieldingWindowLevel = 0; 301 } 302 303 return TRUE; 304 } 305 306 static int 307 QuartzRandRSetModeCallback(ScreenPtr pScreen, 308 QuartzModeInfoPtr pMode, 309 void *data) 310 { 311 QuartzModeInfoPtr pReqMode = (QuartzModeInfoPtr)data; 312 313 if (!QuartzRandRModesEqual(pMode, pReqMode)) 314 return CALLBACK_CONTINUE; /* continue enumeration */ 315 316 DEBUG_LOG("Found a match for requested RandR resolution (%dx%d@%d).\n", 317 (int)pMode->width, (int)pMode->height, (int)pMode->refresh); 318 319 if (QuartzRandRSetMode(pScreen, pMode, FALSE)) 320 return CALLBACK_SUCCESS; 321 else 322 return CALLBACK_ERROR; 323 } 324 325 static Bool 326 QuartzRandRGetInfo(ScreenPtr pScreen, Rotation *rotations) 327 { 328 *rotations = RR_Rotate_0; /* TODO: support rotation */ 329 330 return QuartzRandREnumerateModes(pScreen, QuartzRandRRegisterModeCallback, 331 NULL); 332 } 333 334 static Bool 335 QuartzRandRSetConfig(ScreenPtr pScreen, 336 Rotation randr, 337 int rate, 338 RRScreenSizePtr pSize) 339 { 340 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 341 QuartzModeInfo reqMode; 342 343 reqMode.width = pSize->width; 344 reqMode.height = pSize->height; 345 reqMode.refresh = rate; 346 347 /* Do not switch modes if requested mode is equal to current mode. */ 348 if (QuartzRandRModesEqual(&reqMode, &pQuartzScreen->currentMode)) 349 return TRUE; 350 351 if (QuartzRandREnumerateModes(pScreen, QuartzRandRSetModeCallback, 352 &reqMode)) { 353 return TRUE; 354 } 355 356 DEBUG_LOG("Unable to find a matching config: %d x %d @ %d\n", 357 (int)reqMode.width, (int)reqMode.height, 358 (int)reqMode.refresh); 359 return FALSE; 360 } 361 362 static Bool 363 _QuartzRandRUpdateFakeModes(ScreenPtr pScreen) 364 { 365 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 366 QuartzModeInfo activeMode; 367 368 if (pQuartzScreen->displayCount > 0) { 369 if (!QuartzRandRCopyCurrentModeInfo(pQuartzScreen->displayIDs[0], 370 &activeMode)) { 371 ErrorF("Unable to determine current display mode.\n"); 372 return FALSE; 373 } 374 } 375 else { 376 memset(&activeMode, 0, sizeof(activeMode)); 377 activeMode.width = 800; 378 activeMode.height = 600; 379 activeMode.refresh = 60; 380 } 381 382 if (pQuartzScreen->fullscreenMode.ref) 383 CFRelease(pQuartzScreen->fullscreenMode.ref); 384 if (pQuartzScreen->currentMode.ref) 385 CFRelease(pQuartzScreen->currentMode.ref); 386 387 if (pQuartzScreen->displayCount > 1) { 388 activeMode.width = pScreen->width; 389 activeMode.height = pScreen->height; 390 if (XQuartzIsRootless) 391 activeMode.height += aquaMenuBarHeight; 392 } 393 394 pQuartzScreen->fullscreenMode = activeMode; 395 pQuartzScreen->fullscreenMode.refresh = FAKE_REFRESH_FULLSCREEN; 396 397 pQuartzScreen->rootlessMode = activeMode; 398 pQuartzScreen->rootlessMode.refresh = FAKE_REFRESH_ROOTLESS; 399 pQuartzScreen->rootlessMode.height -= aquaMenuBarHeight; 400 401 if (XQuartzIsRootless) { 402 pQuartzScreen->currentMode = pQuartzScreen->rootlessMode; 403 } 404 else { 405 pQuartzScreen->currentMode = pQuartzScreen->fullscreenMode; 406 } 407 408 /* This extra retain is for currentMode's copy. 409 * fullscreen and rootless share a retain. 410 */ 411 if (pQuartzScreen->currentMode.ref) 412 CFRetain(pQuartzScreen->currentMode.ref); 413 414 DEBUG_LOG("rootlessMode: %d x %d\n", 415 (int)pQuartzScreen->rootlessMode.width, 416 (int)pQuartzScreen->rootlessMode.height); 417 DEBUG_LOG("fullscreenMode: %d x %d\n", 418 (int)pQuartzScreen->fullscreenMode.width, 419 (int)pQuartzScreen->fullscreenMode.height); 420 DEBUG_LOG("currentMode: %d x %d\n", (int)pQuartzScreen->currentMode.width, 421 (int)pQuartzScreen->currentMode.height); 422 423 return TRUE; 424 } 425 426 Bool 427 QuartzRandRUpdateFakeModes(BOOL force_update) 428 { 429 ScreenPtr pScreen = screenInfo.screens[0]; 430 431 if (ignore_next_fake_mode_update) { 432 DEBUG_LOG( 433 "Ignoring update request caused by RandR resolution change.\n"); 434 ignore_next_fake_mode_update = FALSE; 435 return TRUE; 436 } 437 438 if (!_QuartzRandRUpdateFakeModes(pScreen)) 439 return FALSE; 440 441 if (force_update) 442 RRGetInfo(pScreen, TRUE); 443 444 return TRUE; 445 } 446 447 Bool 448 QuartzRandRInit(ScreenPtr pScreen) 449 { 450 rrScrPrivPtr pScrPriv; 451 452 if (!RRScreenInit(pScreen)) return FALSE; 453 if (!_QuartzRandRUpdateFakeModes(pScreen)) return FALSE; 454 455 pScrPriv = rrGetScrPriv(pScreen); 456 pScrPriv->rrGetInfo = QuartzRandRGetInfo; 457 pScrPriv->rrSetConfig = QuartzRandRSetConfig; 458 return TRUE; 459 } 460 461 void 462 QuartzRandRSetFakeRootless(void) 463 { 464 int i; 465 466 DEBUG_LOG("QuartzRandRSetFakeRootless called.\n"); 467 468 for (i = 0; i < screenInfo.numScreens; i++) { 469 ScreenPtr pScreen = screenInfo.screens[i]; 470 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 471 472 QuartzRandRSetMode(pScreen, &pQuartzScreen->rootlessMode, TRUE); 473 } 474 } 475 476 void 477 QuartzRandRSetFakeFullscreen(BOOL state) 478 { 479 int i; 480 481 DEBUG_LOG("QuartzRandRSetFakeFullscreen called.\n"); 482 483 for (i = 0; i < screenInfo.numScreens; i++) { 484 ScreenPtr pScreen = screenInfo.screens[i]; 485 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 486 487 QuartzRandRSetMode(pScreen, &pQuartzScreen->fullscreenMode, TRUE); 488 } 489 490 QuartzShowFullscreen(state); 491 } 492 493 /* Toggle fullscreen mode. If "fake" fullscreen is the current mode, 494 * this will just show/hide the X11 windows. If we are in a RandR fullscreen 495 * mode, this will toggles us to the default fake mode and hide windows if 496 * it is fullscreen 497 */ 498 void 499 QuartzRandRToggleFullscreen(void) 500 { 501 ScreenPtr pScreen = screenInfo.screens[0]; 502 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 503 504 if (pQuartzScreen->currentMode.ref == NULL) { 505 ErrorF( 506 "Ignoring QuartzRandRToggleFullscreen because don't have a current mode set.\n"); 507 } 508 else if (pQuartzScreen->currentMode.refresh == FAKE_REFRESH_ROOTLESS) { 509 ErrorF( 510 "Ignoring QuartzRandRToggleFullscreen because we are in rootless mode.\n"); 511 } 512 else if (pQuartzScreen->currentMode.refresh == FAKE_REFRESH_FULLSCREEN) { 513 /* Legacy fullscreen mode. Hide/Show */ 514 QuartzShowFullscreen(!XQuartzFullscreenVisible); 515 } 516 else { 517 /* RandR fullscreen mode. Return to default mode and hide if it is fullscreen. */ 518 if (XQuartzRootlessDefault) { 519 QuartzRandRSetFakeRootless(); 520 } 521 else { 522 QuartzRandRSetFakeFullscreen(FALSE); 523 } 524 } 525 }