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