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