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
QuartzAddScreen(int index,ScreenPtr pScreen)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
QuartzSetupScreen(int index,ScreenPtr pScreen)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
QuartzBlockHandler(void * blockData,void * pTimeout)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
QuartzWakeupHandler(void * blockData,int result)173 QuartzWakeupHandler(void *blockData, int result)
174 {
175 /* nothing here */
176 }
177
178 /*
179 * QuartzInitOutput
180 * Quartz display initialization.
181 */
182 void
QuartzInitOutput(int argc,char ** argv)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
QuartzInitInput(int argc,char ** argv)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
QuartzUpdateScreens(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
pokeActivityCallback(CFRunLoopTimerRef timer,void * info)317 pokeActivityCallback(CFRunLoopTimerRef timer, void *info)
318 {
319 UpdateSystemActivity(OverallAct);
320 }
321
322 static void
QuartzScreenSaver(int state)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
QuartzShowFullscreen(int state)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
QuartzSetRootless(Bool state)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
QuartzShow(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
QuartzHide(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
QuartzSetRootClip(int mode)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
QuartzSpaceChanged(uint32_t space_id)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
QuartzCopyDisplayIDs(ScreenPtr pScreen,int displayCount,CGDirectDisplayID * displayIDs)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
DDXRingBell(int volume,int pitch,int duration)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 }
553