xref: /OK3568_Linux_fs/external/xserver/hw/xquartz/quartz.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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