1/* 2 * 3 * Quartz-specific support for the Darwin X Server 4 * 5 * Copyright (c) 2001-2004 Greg Parker and Torrey T. Lyons. 6 * All Rights Reserved. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the "Software"), 10 * to deal in the Software without restriction, including without limitation 11 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 * and/or sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included in 16 * all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 * DEALINGS IN THE SOFTWARE. 25 * 26 * Except as contained in this notice, the name(s) of the above copyright 27 * holders shall not be used in advertising or otherwise to promote the sale, 28 * use or other dealings in this Software without prior written authorization. 29 */ 30 31#include "sanitizedCarbon.h" 32 33#ifdef HAVE_DIX_CONFIG_H 34#include <dix-config.h> 35#endif 36 37#include "quartzCommon.h" 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#define _APPLEWM_SERVER_ 45#include "applewmExt.h" 46 47#include "X11Application.h" 48 49#include <X11/extensions/applewmconst.h> 50 51// X headers 52#include "scrnintstr.h" 53#include "windowstr.h" 54#include "colormapst.h" 55#include "globals.h" 56#include "mi.h" 57 58// System headers 59#include <stdlib.h> 60#include <string.h> 61#include <sys/types.h> 62#include <sys/stat.h> 63#include <fcntl.h> 64#include <IOKit/pwr_mgt/IOPMLib.h> 65#include <pthread.h> 66#include <signal.h> 67 68#include <rootlessCommon.h> 69#include <Xplugin.h> 70 71DevPrivateKeyRec quartzScreenKeyRec; 72int aquaMenuBarHeight = 0; 73QuartzModeProcsPtr quartzProcs = NULL; 74const char *quartzOpenGLBundle = NULL; 75 76Bool XQuartzFullscreenDisableHotkeys = TRUE; 77Bool XQuartzOptionSendsAlt = FALSE; 78Bool XQuartzEnableKeyEquivalents = TRUE; 79Bool XQuartzFullscreenVisible = FALSE; 80Bool XQuartzRootlessDefault = TRUE; 81Bool XQuartzIsRootless = TRUE; 82Bool XQuartzServerVisible = FALSE; 83Bool XQuartzFullscreenMenu = FALSE; 84 85int32_t XQuartzShieldingWindowLevel = 0; 86 87/* 88=========================================================================== 89 90 Screen functions 91 92=========================================================================== 93*/ 94 95/* 96 * QuartzAddScreen 97 * Do mode dependent initialization of each screen for Quartz. 98 */ 99Bool QuartzAddScreen( 100 int index, 101 ScreenPtr pScreen) 102{ 103 // allocate space for private per screen Quartz specific storage 104 QuartzScreenPtr displayInfo = calloc(sizeof(QuartzScreenRec), 1); 105 106 // QUARTZ_PRIV(pScreen) = displayInfo; 107 dixSetPrivate(&pScreen->devPrivates, quartzScreenKey, displayInfo); 108 109 // do Quartz mode specific initialization 110 return quartzProcs->AddScreen(index, pScreen); 111} 112 113 114/* 115 * QuartzSetupScreen 116 * Finalize mode specific setup of each screen. 117 */ 118Bool QuartzSetupScreen( 119 int index, 120 ScreenPtr pScreen) 121{ 122 // do Quartz mode specific setup 123 if (! quartzProcs->SetupScreen(index, pScreen)) 124 return FALSE; 125 126 // setup cursor support 127 if (! quartzProcs->InitCursor(pScreen)) 128 return FALSE; 129 130#if defined(RANDR) 131 if(!QuartzRandRInit(pScreen)) { 132 DEBUG_LOG("Failed to init RandR extension.\n"); 133 return FALSE; 134 } 135#endif 136 137 return TRUE; 138} 139 140 141/* 142 * QuartzInitOutput 143 * Quartz display initialization. 144 */ 145void QuartzInitOutput( 146 int argc, 147 char **argv ) 148{ 149 /* For XQuartz, we want to just use the default signal handler to work better with CrashTracer */ 150 signal(SIGSEGV, SIG_DFL); 151 signal(SIGILL, SIG_DFL); 152#ifdef SIGEMT 153 signal(SIGEMT, SIG_DFL); 154#endif 155 signal(SIGFPE, SIG_DFL); 156#ifdef SIGBUS 157 signal(SIGBUS, SIG_DFL); 158#endif 159#ifdef SIGSYS 160 signal(SIGSYS, SIG_DFL); 161#endif 162#ifdef SIGXCPU 163 signal(SIGXCPU, SIG_DFL); 164#endif 165#ifdef SIGXFSZ 166 signal(SIGXFSZ, SIG_DFL); 167#endif 168 169 if (!RegisterBlockAndWakeupHandlers(QuartzBlockHandler, 170 QuartzWakeupHandler, 171 NULL)) 172 { 173 FatalError("Could not register block and wakeup handlers."); 174 } 175 176 if (!dixRegisterPrivateKey(&quartzScreenKeyRec, PRIVATE_SCREEN, 0)) 177 FatalError("Failed to alloc quartz screen private.\n"); 178 179 // Do display mode specific initialization 180 quartzProcs->DisplayInit(); 181} 182 183 184/* 185 * QuartzInitInput 186 * Inform the main thread the X server is ready to handle events. 187 */ 188void QuartzInitInput( 189 int argc, 190 char **argv ) 191{ 192 X11ApplicationSetCanQuit(0); 193 X11ApplicationServerReady(); 194 // Do final display mode specific initialization before handling events 195 if (quartzProcs->InitInput) 196 quartzProcs->InitInput(argc, argv); 197} 198 199 200void QuartzUpdateScreens(void) { 201 ScreenPtr pScreen; 202 WindowPtr pRoot; 203 int x, y, width, height, sx, sy; 204 xEvent e; 205 BoxRec bounds; 206 207 if (noPseudoramiXExtension || screenInfo.numScreens != 1) 208 { 209 /* FIXME: if not using Xinerama, we have multiple screens, and 210 to do this properly may need to add or remove screens. Which 211 isn't possible. So don't do anything. Another reason why 212 we default to running with Xinerama. */ 213 214 return; 215 } 216 217 pScreen = screenInfo.screens[0]; 218 219 PseudoramiXResetScreens(); 220 quartzProcs->AddPseudoramiXScreens(&x, &y, &width, &height, pScreen); 221 222 pScreen->x = x; 223 pScreen->y = y; 224 pScreen->mmWidth = pScreen->mmWidth * ((double) width / pScreen->width); 225 pScreen->mmHeight = pScreen->mmHeight * ((double) height / pScreen->height); 226 pScreen->width = width; 227 pScreen->height = height; 228 229 DarwinAdjustScreenOrigins(&screenInfo); 230 231 /* DarwinAdjustScreenOrigins or UpdateScreen may change pScreen->x/y, 232 * so use it rather than x/y 233 */ 234 sx = pScreen->x + darwinMainScreenX; 235 sy = pScreen->y + darwinMainScreenY; 236 237 /* Adjust the root window. */ 238 pRoot = pScreen->root; 239 AppleWMSetScreenOrigin(pRoot); 240 pScreen->ResizeWindow(pRoot, x - sx, y - sy, width, height, NULL); 241 242 /* <rdar://problem/7770779> pointer events are clipped to old display region after display reconfiguration 243 * http://xquartz.macosforge.org/trac/ticket/346 244 */ 245 bounds.x1 = 0; 246 bounds.x2 = width; 247 bounds.y1 = 0; 248 bounds.y2 = height; 249 pScreen->ConstrainCursor(inputInfo.pointer, pScreen, &bounds); 250 inputInfo.pointer->spriteInfo->sprite->physLimits = bounds; 251 inputInfo.pointer->spriteInfo->sprite->hotLimits = bounds; 252 253 DEBUG_LOG("Root Window: %dx%d @ (%d, %d) darwinMainScreen (%d, %d) xy (%d, %d) dixScreenOrigins (%d, %d)\n", width, height, x - sx, y - sy, darwinMainScreenX, darwinMainScreenY, x, y, pScreen->x, pScreen->y); 254 255 /* Send an event for the root reconfigure */ 256 e.u.u.type = ConfigureNotify; 257 e.u.configureNotify.window = pRoot->drawable.id; 258 e.u.configureNotify.aboveSibling = None; 259 e.u.configureNotify.x = x - sx; 260 e.u.configureNotify.y = y - sy; 261 e.u.configureNotify.width = width; 262 e.u.configureNotify.height = height; 263 e.u.configureNotify.borderWidth = wBorderWidth(pRoot); 264 e.u.configureNotify.override = pRoot->overrideRedirect; 265 DeliverEvents(pRoot, &e, 1, NullWindow); 266 267 quartzProcs->UpdateScreen(pScreen); 268 269 /* miPaintWindow needs to be called after RootlessUpdateScreenPixmap (from xprUpdateScreen) */ 270 miPaintWindow(pRoot, &pRoot->borderClip, PW_BACKGROUND); 271 272 /* Tell RandR about the new size, so new connections get the correct info */ 273 RRScreenSizeNotify(pScreen); 274} 275 276static void pokeActivityCallback(CFRunLoopTimerRef timer, void *info) { 277 UpdateSystemActivity(OverallAct); 278} 279 280static void QuartzScreenSaver(int state) { 281 static CFRunLoopTimerRef pokeActivityTimer = NULL; 282 static CFRunLoopTimerContext pokeActivityContext = { 0, NULL, NULL, NULL, NULL }; 283 static pthread_mutex_t pokeActivityMutex = PTHREAD_MUTEX_INITIALIZER; 284 285 pthread_mutex_lock(&pokeActivityMutex); 286 287 if(state) { 288 if(pokeActivityTimer == NULL) 289 goto QuartzScreenSaverEnd; 290 291 CFRunLoopTimerInvalidate(pokeActivityTimer); 292 CFRelease(pokeActivityTimer); 293 pokeActivityTimer = NULL; 294 } else { 295 if(pokeActivityTimer != NULL) 296 goto QuartzScreenSaverEnd; 297 298 pokeActivityTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 30, 0, 0, pokeActivityCallback, &pokeActivityContext); 299 if(pokeActivityTimer == NULL) { 300 ErrorF("Unable to create pokeActivityTimer.\n"); 301 goto QuartzScreenSaverEnd; 302 } 303 304 CFRunLoopAddTimer(CFRunLoopGetMain(), pokeActivityTimer, kCFRunLoopCommonModes); 305 } 306QuartzScreenSaverEnd: 307 pthread_mutex_unlock(&pokeActivityMutex); 308} 309 310void QuartzShowFullscreen(int state) { 311 int i; 312 313 DEBUG_LOG("QuartzShowFullscreen: state=%d\n", state); 314 315 if(XQuartzIsRootless) { 316 ErrorF("QuartzShowFullscreen called while in rootless mode.\n"); 317 return; 318 } 319 320 QuartzScreenSaver(!state); 321 322 if(XQuartzFullscreenVisible == state) 323 return; 324 325 XQuartzFullscreenVisible = state; 326 327 xp_disable_update (); 328 329 if (!XQuartzFullscreenVisible) 330 RootlessHideAllWindows(); 331 332 RootlessUpdateRooted(XQuartzFullscreenVisible); 333 334 if (XQuartzFullscreenVisible) { 335 RootlessShowAllWindows (); 336 for (i=0; i < screenInfo.numScreens; i++) { 337 ScreenPtr pScreen = screenInfo.screens[i]; 338 RootlessRepositionWindows(pScreen); 339 // JH: I don't think this is necessary, but keeping it here as a reminder 340 //RootlessUpdateScreenPixmap(pScreen); 341 } 342 } 343 344 /* Somehow the menubar manages to interfere with our event stream 345 * in fullscreen mode, even though it's not visible. 346 */ 347 X11ApplicationShowHideMenubar(!XQuartzFullscreenVisible); 348 349 xp_reenable_update (); 350 351 if (XQuartzFullscreenDisableHotkeys) 352 xp_disable_hot_keys(XQuartzFullscreenVisible); 353} 354 355void QuartzSetRootless(Bool state) { 356 DEBUG_LOG("QuartzSetRootless state=%d\n", state); 357 358 if(XQuartzIsRootless == state) 359 return; 360 361 if(state) 362 QuartzShowFullscreen(FALSE); 363 364 XQuartzIsRootless = state; 365 366 xp_disable_update(); 367 368 /* When in rootless, the menubar is not part of the screen, so we need to update our screens on toggle */ 369 QuartzUpdateScreens(); 370 371 if(XQuartzIsRootless) { 372 RootlessShowAllWindows(); 373 } else { 374 RootlessHideAllWindows(); 375 } 376 377 X11ApplicationShowHideMenubar(TRUE); 378 379 xp_reenable_update(); 380 381 xp_disable_hot_keys(FALSE); 382} 383 384/* 385 * QuartzShow 386 * Show the X server on screen. Does nothing if already shown. 387 * Calls mode specific screen resume to restore the X clip regions 388 * (if needed) and the X server cursor state. 389 */ 390void QuartzShow(void) { 391 int i; 392 393 if (XQuartzServerVisible) 394 return; 395 396 XQuartzServerVisible = TRUE; 397 for (i = 0; i < screenInfo.numScreens; i++) { 398 if (screenInfo.screens[i]) { 399 quartzProcs->ResumeScreen(screenInfo.screens[i]); 400 } 401 } 402 403 if (!XQuartzIsRootless) 404 QuartzShowFullscreen(TRUE); 405} 406 407 408/* 409 * QuartzHide 410 * Remove the X server display from the screen. Does nothing if already 411 * hidden. Calls mode specific screen suspend to set X clip regions to 412 * prevent drawing (if needed) and restore the Aqua cursor. 413 */ 414void QuartzHide(void) 415{ 416 int i; 417 418 if (XQuartzServerVisible) { 419 for (i = 0; i < screenInfo.numScreens; i++) { 420 if (screenInfo.screens[i]) { 421 quartzProcs->SuspendScreen(screenInfo.screens[i]); 422 } 423 } 424 } 425 426 if(!XQuartzIsRootless) 427 QuartzShowFullscreen(FALSE); 428 XQuartzServerVisible = FALSE; 429} 430 431 432/* 433 * QuartzSetRootClip 434 * Enable or disable rendering to the X screen. 435 */ 436void QuartzSetRootClip( 437 BOOL enable) 438{ 439 int i; 440 441 if (!XQuartzServerVisible) 442 return; 443 444 for (i = 0; i < screenInfo.numScreens; i++) { 445 if (screenInfo.screens[i]) { 446 xf86SetRootClip(screenInfo.screens[i], enable); 447 } 448 } 449} 450 451/* 452 * QuartzSpaceChanged 453 * Unmap offscreen windows, map onscreen windows 454 */ 455void QuartzSpaceChanged(uint32_t space_id) { 456 /* Do something special here, so we don't depend on quartz-wm for spaces to work... */ 457 DEBUG_LOG("Space Changed (%u) ... do something interesting...\n", space_id); 458} 459 460/* 461 * QuartzCopyDisplayIDs 462 * Associate an X11 screen with one or more CoreGraphics display IDs by copying 463 * the list into a private array. Free the previously copied array, if present. 464 */ 465void QuartzCopyDisplayIDs(ScreenPtr pScreen, 466 int displayCount, CGDirectDisplayID *displayIDs) { 467 QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen); 468 469 free(pQuartzScreen->displayIDs); 470 if(displayCount) { 471 size_t size = displayCount * sizeof(CGDirectDisplayID); 472 pQuartzScreen->displayIDs = malloc(size); 473 memcpy(pQuartzScreen->displayIDs, displayIDs, size); 474 } else { 475 pQuartzScreen->displayIDs = NULL; 476 } 477 pQuartzScreen->displayCount = displayCount; 478} 479 480void NSBeep(void); 481void DDXRingBell( 482 int volume, // volume is % of max 483 int pitch, // pitch is Hz 484 int duration) // duration is milliseconds 485{ 486 if (volume) 487 NSBeep(); 488} 489