quartz.c (15260B)
1 /* 2 * 3 * Quartz-specific support for the Darwin X Server 4 * 5 * Copyright (c) 2002-2012 Apple Inc. All rights reserved. 6 * Copyright (c) 2001-2004 Greg Parker and Torrey T. Lyons. 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 "inputstr.h" 40 #include "quartz.h" 41 #include "darwin.h" 42 #include "darwinEvents.h" 43 #include "pseudoramiX.h" 44 #include "extension.h" 45 #include "nonsdk_extinit.h" 46 #include "glx_extinit.h" 47 #define _APPLEWM_SERVER_ 48 #include "applewmExt.h" 49 50 #include "X11Application.h" 51 52 #include <X11/extensions/applewmconst.h> 53 54 // X headers 55 #include "scrnintstr.h" 56 #include "windowstr.h" 57 #include "colormapst.h" 58 #include "globals.h" 59 #include "mi.h" 60 61 // System headers 62 #include <stdlib.h> 63 #include <string.h> 64 #include <sys/types.h> 65 #include <sys/stat.h> 66 #include <fcntl.h> 67 #include <IOKit/pwr_mgt/IOPMLib.h> 68 #include <libkern/OSAtomic.h> 69 #include <signal.h> 70 71 #include <rootlessCommon.h> 72 #include <Xplugin.h> 73 74 // These are vended by the Objective-C runtime, but they are unfortunately 75 // not available as API in the macOS SDK. We are following suit with swift 76 // and clang in declaring them inline here. They canot be removed or changed 77 // in the OS without major bincompat ramifications. 78 // 79 // These were added in macOS 10.7. 80 void * _Nonnull objc_autoreleasePoolPush(void); 81 void objc_autoreleasePoolPop(void * _Nonnull context); 82 83 DevPrivateKeyRec quartzScreenKeyRec; 84 int aquaMenuBarHeight = 0; 85 QuartzModeProcsPtr quartzProcs = NULL; 86 const char *quartzOpenGLBundle = NULL; 87 88 Bool XQuartzFullscreenDisableHotkeys = TRUE; 89 Bool XQuartzOptionSendsAlt = FALSE; 90 Bool XQuartzEnableKeyEquivalents = TRUE; 91 Bool XQuartzFullscreenVisible = FALSE; 92 Bool XQuartzRootlessDefault = TRUE; 93 Bool XQuartzIsRootless = TRUE; 94 Bool XQuartzServerVisible = FALSE; 95 Bool XQuartzFullscreenMenu = FALSE; 96 97 int32_t XQuartzShieldingWindowLevel = 0; 98 99 /* 100 =========================================================================== 101 102 Screen functions 103 104 =========================================================================== 105 */ 106 107 /* 108 * QuartzAddScreen 109 * Do mode dependent initialization of each screen for Quartz. 110 */ 111 Bool 112 QuartzAddScreen(int index, 113 ScreenPtr pScreen) 114 { 115 // The clang static analyzer thinks we leak displayInfo here 116 #ifndef __clang_analyzer__ 117 // allocate space for private per screen Quartz specific storage 118 QuartzScreenPtr displayInfo = calloc(sizeof(QuartzScreenRec), 1); 119 120 // QUARTZ_PRIV(pScreen) = displayInfo; 121 dixSetPrivate(&pScreen->devPrivates, quartzScreenKey, displayInfo); 122 #endif /* __clang_analyzer__ */ 123 124 // do Quartz mode specific initialization 125 return quartzProcs->AddScreen(index, pScreen); 126 } 127 128 /* 129 * QuartzSetupScreen 130 * Finalize mode specific setup of each screen. 131 */ 132 Bool 133 QuartzSetupScreen(int index, 134 ScreenPtr pScreen) 135 { 136 // do Quartz mode specific setup 137 if (!quartzProcs->SetupScreen(index, pScreen)) 138 return FALSE; 139 140 // setup cursor support 141 if (!quartzProcs->InitCursor(pScreen)) 142 return FALSE; 143 144 #if defined(RANDR) 145 if (!QuartzRandRInit(pScreen)) { 146 DEBUG_LOG("Failed to init RandR extension.\n"); 147 return FALSE; 148 } 149 #endif 150 151 return TRUE; 152 } 153 154 /* 155 * QuartzBlockHandler 156 * Clean out any autoreleased objects. 157 */ 158 static void 159 QuartzBlockHandler(void *blockData, void *pTimeout) 160 { 161 static void *poolToken = NULL; 162 163 if (poolToken) { 164 objc_autoreleasePoolPop(poolToken); 165 } 166 poolToken = objc_autoreleasePoolPush(); 167 } 168 169 /* 170 * QuartzWakeupHandler 171 */ 172 static void 173 QuartzWakeupHandler(void *blockData, int result) 174 { 175 /* nothing here */ 176 } 177 178 /* 179 * QuartzInitOutput 180 * Quartz display initialization. 181 */ 182 void 183 QuartzInitOutput(int argc, 184 char **argv) 185 { 186 /* For XQuartz, we want to just use the default signal handler to work better with CrashTracer */ 187 signal(SIGSEGV, SIG_DFL); 188 signal(SIGABRT, SIG_DFL); 189 signal(SIGILL, SIG_DFL); 190 #ifdef SIGEMT 191 signal(SIGEMT, SIG_DFL); 192 #endif 193 signal(SIGFPE, SIG_DFL); 194 #ifdef SIGBUS 195 signal(SIGBUS, SIG_DFL); 196 #endif 197 #ifdef SIGSYS 198 signal(SIGSYS, SIG_DFL); 199 #endif 200 #ifdef SIGXCPU 201 signal(SIGXCPU, SIG_DFL); 202 #endif 203 #ifdef SIGXFSZ 204 signal(SIGXFSZ, SIG_DFL); 205 #endif 206 207 if (!RegisterBlockAndWakeupHandlers(QuartzBlockHandler, 208 QuartzWakeupHandler, 209 NULL)) { 210 FatalError("Could not register block and wakeup handlers."); 211 } 212 213 if (!dixRegisterPrivateKey(&quartzScreenKeyRec, PRIVATE_SCREEN, 0)) 214 FatalError("Failed to alloc quartz screen private.\n"); 215 216 // Do display mode specific initialization 217 quartzProcs->DisplayInit(); 218 } 219 220 /* 221 * QuartzInitInput 222 * Inform the main thread the X server is ready to handle events. 223 */ 224 void 225 QuartzInitInput(int argc, 226 char **argv) 227 { 228 X11ApplicationSetCanQuit(0); 229 X11ApplicationServerReady(); 230 // Do final display mode specific initialization before handling events 231 if (quartzProcs->InitInput) 232 quartzProcs->InitInput(argc, argv); 233 } 234 235 void 236 QuartzUpdateScreens(void) 237 { 238 ScreenPtr pScreen; 239 WindowPtr pRoot; 240 int x, y, width, height, sx, sy; 241 xEvent e; 242 BoxRec bounds; 243 244 if (noPseudoramiXExtension || screenInfo.numScreens != 1) { 245 /* FIXME: if not using Xinerama, we have multiple screens, and 246 to do this properly may need to add or remove screens. Which 247 isn't possible. So don't do anything. Another reason why 248 we default to running with Xinerama. */ 249 250 return; 251 } 252 253 pScreen = screenInfo.screens[0]; 254 255 PseudoramiXResetScreens(); 256 quartzProcs->AddPseudoramiXScreens(&x, &y, &width, &height, pScreen); 257 258 pScreen->x = x; 259 pScreen->y = y; 260 pScreen->mmWidth = pScreen->mmWidth * ((double)width / pScreen->width); 261 pScreen->mmHeight = pScreen->mmHeight * ((double)height / pScreen->height); 262 pScreen->width = width; 263 pScreen->height = height; 264 265 DarwinAdjustScreenOrigins(&screenInfo); 266 267 /* DarwinAdjustScreenOrigins or UpdateScreen may change pScreen->x/y, 268 * so use it rather than x/y 269 */ 270 sx = pScreen->x + darwinMainScreenX; 271 sy = pScreen->y + darwinMainScreenY; 272 273 /* Adjust the root window. */ 274 pRoot = pScreen->root; 275 AppleWMSetScreenOrigin(pRoot); 276 pScreen->ResizeWindow(pRoot, x - sx, y - sy, width, height, NULL); 277 278 /* <rdar://problem/7770779> pointer events are clipped to old display region after display reconfiguration 279 * http://xquartz.macosforge.org/trac/ticket/346 280 */ 281 bounds.x1 = 0; 282 bounds.x2 = width; 283 bounds.y1 = 0; 284 bounds.y2 = height; 285 pScreen->ConstrainCursor(inputInfo.pointer, pScreen, &bounds); 286 inputInfo.pointer->spriteInfo->sprite->physLimits = bounds; 287 inputInfo.pointer->spriteInfo->sprite->hotLimits = bounds; 288 289 DEBUG_LOG( 290 "Root Window: %dx%d @ (%d, %d) darwinMainScreen (%d, %d) xy (%d, %d) dixScreenOrigins (%d, %d)\n", 291 width, height, x - sx, y - sy, darwinMainScreenX, darwinMainScreenY, 292 x, y, 293 pScreen->x, pScreen->y); 294 295 /* Send an event for the root reconfigure */ 296 e.u.u.type = ConfigureNotify; 297 e.u.configureNotify.window = pRoot->drawable.id; 298 e.u.configureNotify.aboveSibling = None; 299 e.u.configureNotify.x = x - sx; 300 e.u.configureNotify.y = y - sy; 301 e.u.configureNotify.width = width; 302 e.u.configureNotify.height = height; 303 e.u.configureNotify.borderWidth = wBorderWidth(pRoot); 304 e.u.configureNotify.override = pRoot->overrideRedirect; 305 DeliverEvents(pRoot, &e, 1, NullWindow); 306 307 quartzProcs->UpdateScreen(pScreen); 308 309 /* PaintWindow needs to be called after RootlessUpdateScreenPixmap (from xprUpdateScreen) */ 310 pScreen->PaintWindow(pRoot, &pRoot->borderClip, PW_BACKGROUND); 311 312 /* Tell RandR about the new size, so new connections get the correct info */ 313 RRScreenSizeNotify(pScreen); 314 } 315 316 static void 317 pokeActivityCallback(CFRunLoopTimerRef timer, void *info) 318 { 319 UpdateSystemActivity(OverallAct); 320 } 321 322 static void 323 QuartzScreenSaver(int state) 324 { 325 static CFRunLoopTimerRef pokeActivityTimer = NULL; 326 static CFRunLoopTimerContext pokeActivityContext = 327 { 0, NULL, NULL, NULL, NULL }; 328 static OSSpinLock pokeActivitySpinLock = OS_SPINLOCK_INIT; 329 330 OSSpinLockLock(&pokeActivitySpinLock); 331 332 if (state) { 333 if (pokeActivityTimer == NULL) 334 goto QuartzScreenSaverEnd; 335 336 CFRunLoopTimerInvalidate(pokeActivityTimer); 337 CFRelease(pokeActivityTimer); 338 pokeActivityTimer = NULL; 339 } 340 else { 341 if (pokeActivityTimer != NULL) 342 goto QuartzScreenSaverEnd; 343 344 pokeActivityTimer = CFRunLoopTimerCreate(NULL, 345 CFAbsoluteTimeGetCurrent(), 346 30, 0, 0, 347 pokeActivityCallback, 348 &pokeActivityContext); 349 if (pokeActivityTimer == NULL) { 350 ErrorF("Unable to create pokeActivityTimer.\n"); 351 goto QuartzScreenSaverEnd; 352 } 353 354 CFRunLoopAddTimer( 355 CFRunLoopGetMain(), pokeActivityTimer, kCFRunLoopCommonModes); 356 } 357 QuartzScreenSaverEnd: 358 OSSpinLockUnlock(&pokeActivitySpinLock); 359 } 360 361 void 362 QuartzShowFullscreen(int state) 363 { 364 int i; 365 366 DEBUG_LOG("QuartzShowFullscreen: state=%d\n", state); 367 368 if (XQuartzIsRootless) { 369 ErrorF("QuartzShowFullscreen called while in rootless mode.\n"); 370 return; 371 } 372 373 QuartzScreenSaver(!state); 374 375 if (XQuartzFullscreenVisible == state) 376 return; 377 378 XQuartzFullscreenVisible = state; 379 380 xp_disable_update(); 381 382 if (!XQuartzFullscreenVisible) 383 RootlessHideAllWindows(); 384 385 RootlessUpdateRooted(XQuartzFullscreenVisible); 386 387 if (XQuartzFullscreenVisible) { 388 RootlessShowAllWindows(); 389 for (i = 0; i < screenInfo.numScreens; i++) { 390 ScreenPtr pScreen = screenInfo.screens[i]; 391 RootlessRepositionWindows(pScreen); 392 // JH: I don't think this is necessary, but keeping it here as a reminder 393 //RootlessUpdateScreenPixmap(pScreen); 394 } 395 } 396 397 /* Somehow the menubar manages to interfere with our event stream 398 * in fullscreen mode, even though it's not visible. 399 */ 400 X11ApplicationShowHideMenubar(!XQuartzFullscreenVisible); 401 402 xp_reenable_update(); 403 404 if (XQuartzFullscreenDisableHotkeys) 405 xp_disable_hot_keys(XQuartzFullscreenVisible); 406 } 407 408 void 409 QuartzSetRootless(Bool state) 410 { 411 DEBUG_LOG("QuartzSetRootless state=%d\n", state); 412 413 if (XQuartzIsRootless == state) 414 return; 415 416 if (state) 417 QuartzShowFullscreen(FALSE); 418 419 XQuartzIsRootless = state; 420 421 xp_disable_update(); 422 423 /* When in rootless, the menubar is not part of the screen, so we need to update our screens on toggle */ 424 QuartzUpdateScreens(); 425 426 if (XQuartzIsRootless) { 427 RootlessShowAllWindows(); 428 } 429 else { 430 RootlessHideAllWindows(); 431 } 432 433 X11ApplicationShowHideMenubar(TRUE); 434 435 xp_reenable_update(); 436 437 xp_disable_hot_keys(FALSE); 438 } 439 440 /* 441 * QuartzShow 442 * Show the X server on screen. Does nothing if already shown. 443 * Calls mode specific screen resume to restore the X clip regions 444 * (if needed) and the X server cursor state. 445 */ 446 void 447 QuartzShow(void) 448 { 449 int i; 450 451 if (XQuartzServerVisible) 452 return; 453 454 XQuartzServerVisible = TRUE; 455 for (i = 0; i < screenInfo.numScreens; i++) { 456 if (screenInfo.screens[i]) { 457 quartzProcs->ResumeScreen(screenInfo.screens[i]); 458 } 459 } 460 461 if (!XQuartzIsRootless) 462 QuartzShowFullscreen(TRUE); 463 } 464 465 /* 466 * QuartzHide 467 * Remove the X server display from the screen. Does nothing if already 468 * hidden. Calls mode specific screen suspend to set X clip regions to 469 * prevent drawing (if needed) and restore the Aqua cursor. 470 */ 471 void 472 QuartzHide(void) 473 { 474 int i; 475 476 if (XQuartzServerVisible) { 477 for (i = 0; i < screenInfo.numScreens; i++) { 478 if (screenInfo.screens[i]) { 479 quartzProcs->SuspendScreen(screenInfo.screens[i]); 480 } 481 } 482 } 483 484 if (!XQuartzIsRootless) 485 QuartzShowFullscreen(FALSE); 486 XQuartzServerVisible = FALSE; 487 } 488 489 /* 490 * QuartzSetRootClip 491 * Enable or disable rendering to the X screen. 492 */ 493 void 494 QuartzSetRootClip(int mode) 495 { 496 int i; 497 498 if (!XQuartzServerVisible) 499 return; 500 501 for (i = 0; i < screenInfo.numScreens; i++) { 502 if (screenInfo.screens[i]) { 503 SetRootClip(screenInfo.screens[i], mode); 504 } 505 } 506 } 507 508 /* 509 * QuartzSpaceChanged 510 * Unmap offscreen windows, map onscreen windows 511 */ 512 void 513 QuartzSpaceChanged(uint32_t space_id) 514 { 515 /* Do something special here, so we don't depend on quartz-wm for spaces to work... */ 516 DEBUG_LOG("Space Changed (%u) ... do something interesting...\n", 517 space_id); 518 } 519 520 /* 521 * QuartzCopyDisplayIDs 522 * Associate an X11 screen with one or more CoreGraphics display IDs by copying 523 * the list into a private array. Free the previously copied array, if present. 524 */ 525 void 526 QuartzCopyDisplayIDs(ScreenPtr pScreen, 527 int displayCount, CGDirectDisplayID *displayIDs) 528 { 529 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 530 531 free(pQuartzScreen->displayIDs); 532 if (displayCount) { 533 size_t size = displayCount * sizeof(CGDirectDisplayID); 534 pQuartzScreen->displayIDs = malloc(size); 535 memcpy(pQuartzScreen->displayIDs, displayIDs, size); 536 } 537 else { 538 pQuartzScreen->displayIDs = NULL; 539 } 540 pQuartzScreen->displayCount = displayCount; 541 } 542 543 void 544 NSBeep(void); 545 void 546 DDXRingBell(int volume, // volume is % of max 547 int pitch, // pitch is Hz 548 int duration) // duration is milliseconds 549 { 550 if (volume) 551 NSBeep(); 552 }