1/*
2 * Quartz-specific support for the XRandR extension
3 *
4 * Copyright (c) 2001-2004 Greg Parker and Torrey T. Lyons,
5 *               2010      Jan Hauffa.
6 *               2010-2011 Apple Inc.
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 "quartz.h"
41#include "darwin.h"
42
43#include "X11Application.h"
44
45#include <AvailabilityMacros.h>
46
47#include <X11/extensions/randr.h>
48#include <randrstr.h>
49#include <IOKit/graphics/IOGraphicsTypes.h>
50
51/* TODO: UGLY, find a better way!
52 * We want to ignore kXquartzDisplayChanged which are generated by us
53 */
54static Bool ignore_next_fake_mode_update = FALSE;
55
56#define FAKE_REFRESH_ROOTLESS 1
57#define FAKE_REFRESH_FULLSCREEN 2
58
59#define DEFAULT_REFRESH  60
60#define kDisplayModeUsableFlags  (kDisplayModeValidFlag | kDisplayModeSafeFlag)
61
62#define CALLBACK_SUCCESS 0
63#define CALLBACK_CONTINUE 1
64#define CALLBACK_ERROR -1
65
66typedef int (*QuartzModeCallback)
67    (ScreenPtr, QuartzModeInfoPtr, void *);
68
69#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
70
71static long getDictLong (CFDictionaryRef dictRef, CFStringRef key) {
72    long value;
73
74    CFNumberRef numRef = (CFNumberRef) CFDictionaryGetValue(dictRef, key);
75    if (!numRef)
76        return 0;
77
78    if (!CFNumberGetValue(numRef, kCFNumberLongType, &value))
79        return 0;
80    return value;
81}
82
83static double getDictDouble (CFDictionaryRef dictRef, CFStringRef key) {
84    double value;
85
86    CFNumberRef numRef = (CFNumberRef) CFDictionaryGetValue(dictRef, key);
87    if (!numRef)
88        return 0.0;
89
90    if (!CFNumberGetValue(numRef, kCFNumberDoubleType, &value))
91        return 0.0;
92    return value;
93}
94
95static void QuartzRandRGetModeInfo (CFDictionaryRef modeRef,
96                                    QuartzModeInfoPtr pMode) {
97    pMode->width = (size_t) getDictLong(modeRef, kCGDisplayWidth);
98    pMode->height = (size_t) getDictLong(modeRef, kCGDisplayHeight);
99    pMode->refresh = (int)(getDictDouble(modeRef, kCGDisplayRefreshRate) + 0.5);
100    if (pMode->refresh == 0)
101        pMode->refresh = DEFAULT_REFRESH;
102    pMode->ref = NULL;
103    pMode->pSize = NULL;
104}
105
106static Bool QuartzRandRCopyCurrentModeInfo (CGDirectDisplayID screenId,
107                                           QuartzModeInfoPtr pMode) {
108    CFDictionaryRef curModeRef = CGDisplayCurrentMode(screenId);
109    if (!curModeRef)
110        return FALSE;
111
112    QuartzRandRGetModeInfo(curModeRef, pMode);
113    pMode->ref = (void *)curModeRef;
114    CFRetain(pMode->ref);
115    return TRUE;
116}
117
118static Bool QuartzRandRSetCGMode (CGDirectDisplayID screenId,
119                                QuartzModeInfoPtr pMode) {
120    CFDictionaryRef modeRef = (CFDictionaryRef) pMode->ref;
121    return (CGDisplaySwitchToMode(screenId, modeRef) == kCGErrorSuccess);
122}
123
124static Bool QuartzRandREnumerateModes (ScreenPtr pScreen,
125                                       QuartzModeCallback callback,
126                                       void *data) {
127    Bool retval = FALSE;
128    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
129
130    /* Just an 800x600 fallback if we have no attached heads */
131    if(pQuartzScreen->displayIDs) {
132        CFDictionaryRef curModeRef, modeRef;
133        long curBpp;
134        CFArrayRef modes;
135        QuartzModeInfo modeInfo;
136        int i;
137        CGDirectDisplayID screenId = pQuartzScreen->displayIDs[0];
138
139        curModeRef = CGDisplayCurrentMode(screenId);
140        if (!curModeRef)
141            return FALSE;
142        curBpp = getDictLong(curModeRef, kCGDisplayBitsPerPixel);
143
144        modes = CGDisplayAvailableModes(screenId);
145        if (!modes)
146            return FALSE;
147        for (i = 0; i < CFArrayGetCount(modes); i++) {
148            int cb;
149            modeRef = (CFDictionaryRef) CFArrayGetValueAtIndex(modes, i);
150
151            /* Skip modes that are not usable on the current display or have a
152               different pixel encoding than the current mode. */
153            if (((unsigned long) getDictLong(modeRef, kCGDisplayIOFlags) &
154                 kDisplayModeUsableFlags) != kDisplayModeUsableFlags)
155                continue;
156            if (getDictLong(modeRef, kCGDisplayBitsPerPixel) != curBpp)
157                continue;
158
159            QuartzRandRGetModeInfo(modeRef, &modeInfo);
160            modeInfo.ref = (void *)modeRef;
161            cb = callback(pScreen, &modeInfo, data);
162            if (cb == CALLBACK_CONTINUE)
163                retval = TRUE;
164            else if (cb == CALLBACK_SUCCESS)
165                return TRUE;
166            else if (cb == CALLBACK_ERROR)
167                return FALSE;
168        }
169    }
170
171    switch(callback(pScreen, &pQuartzScreen->rootlessMode, data)) {
172        case CALLBACK_SUCCESS:
173            return TRUE;
174        case CALLBACK_ERROR:
175            return FALSE;
176        case CALLBACK_CONTINUE:
177            retval = TRUE;
178        default:
179            break;
180    }
181
182    switch(callback(pScreen, &pQuartzScreen->fullscreenMode, data)) {
183        case CALLBACK_SUCCESS:
184            return TRUE;
185        case CALLBACK_ERROR:
186            return FALSE;
187        case CALLBACK_CONTINUE:
188            retval = TRUE;
189        default:
190            break;
191    }
192
193    return retval;
194}
195
196#else /* we have the new CG APIs from Snow Leopard */
197
198static void QuartzRandRGetModeInfo (CGDisplayModeRef modeRef,
199                                    QuartzModeInfoPtr pMode) {
200    pMode->width = CGDisplayModeGetWidth(modeRef);
201    pMode->height = CGDisplayModeGetHeight(modeRef);
202    pMode->refresh = (int) (CGDisplayModeGetRefreshRate(modeRef) + 0.5);
203    if (pMode->refresh == 0)
204        pMode->refresh = DEFAULT_REFRESH;
205    pMode->ref = NULL;
206    pMode->pSize = NULL;
207}
208
209static Bool QuartzRandRCopyCurrentModeInfo (CGDirectDisplayID screenId,
210                                            QuartzModeInfoPtr pMode) {
211    CGDisplayModeRef curModeRef = CGDisplayCopyDisplayMode(screenId);
212    if (!curModeRef)
213        return FALSE;
214
215    QuartzRandRGetModeInfo(curModeRef, pMode);
216    pMode->ref = curModeRef;
217    return TRUE;
218}
219
220static Bool QuartzRandRSetCGMode (CGDirectDisplayID screenId,
221                                QuartzModeInfoPtr pMode) {
222    CGDisplayModeRef modeRef = (CGDisplayModeRef) pMode->ref;
223    if (!modeRef)
224        return FALSE;
225
226    return (CGDisplaySetDisplayMode(screenId, modeRef, NULL) == kCGErrorSuccess);
227}
228
229static Bool QuartzRandREnumerateModes (ScreenPtr pScreen,
230                                       QuartzModeCallback callback,
231                                       void *data) {
232    Bool retval = FALSE;
233    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
234
235    /* Just an 800x600 fallback if we have no attached heads */
236    if(pQuartzScreen->displayIDs) {
237        CGDisplayModeRef curModeRef, modeRef;
238        CFStringRef curPixelEnc, pixelEnc;
239        CFComparisonResult pixelEncEqual;
240        CFArrayRef modes;
241        QuartzModeInfo modeInfo;
242        int i;
243        CGDirectDisplayID screenId = pQuartzScreen->displayIDs[0];
244
245        curModeRef = CGDisplayCopyDisplayMode(screenId);
246        if (!curModeRef)
247            return FALSE;
248        curPixelEnc = CGDisplayModeCopyPixelEncoding(curModeRef);
249        CGDisplayModeRelease(curModeRef);
250
251        modes = CGDisplayCopyAllDisplayModes(screenId, NULL);
252        if (!modes) {
253            CFRelease(curPixelEnc);
254            return FALSE;
255        }
256        for (i = 0; i < CFArrayGetCount(modes); i++) {
257            int cb;
258            modeRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
259
260            /* Skip modes that are not usable on the current display or have a
261               different pixel encoding than the current mode. */
262            if ((CGDisplayModeGetIOFlags(modeRef) & kDisplayModeUsableFlags) !=
263                kDisplayModeUsableFlags)
264                continue;
265            pixelEnc = CGDisplayModeCopyPixelEncoding(modeRef);
266            pixelEncEqual = CFStringCompare(pixelEnc, curPixelEnc, 0);
267            CFRelease(pixelEnc);
268            if (pixelEncEqual != kCFCompareEqualTo)
269                continue;
270
271            QuartzRandRGetModeInfo(modeRef, &modeInfo);
272            modeInfo.ref = modeRef;
273            cb = callback(pScreen, &modeInfo, data);
274            if (cb == CALLBACK_CONTINUE) {
275                retval = TRUE;
276            } else if (cb == CALLBACK_SUCCESS) {
277                CFRelease(modes);
278                CFRelease(curPixelEnc);
279                return TRUE;
280            } else if (cb == CALLBACK_ERROR) {
281                CFRelease(modes);
282                CFRelease(curPixelEnc);
283                return FALSE;
284            }
285        }
286
287        CFRelease(modes);
288        CFRelease(curPixelEnc);
289    }
290
291    switch(callback(pScreen, &pQuartzScreen->rootlessMode, data)) {
292        case CALLBACK_SUCCESS:
293            return TRUE;
294        case CALLBACK_ERROR:
295            return FALSE;
296        case CALLBACK_CONTINUE:
297            retval = TRUE;
298        default:
299            break;
300    }
301
302    switch(callback(pScreen, &pQuartzScreen->fullscreenMode, data)) {
303        case CALLBACK_SUCCESS:
304            return TRUE;
305        case CALLBACK_ERROR:
306            return FALSE;
307        case CALLBACK_CONTINUE:
308            retval = TRUE;
309        default:
310            break;
311    }
312
313    return retval;
314}
315
316#endif  /* Snow Leopard CoreGraphics APIs */
317
318
319static Bool QuartzRandRModesEqual (QuartzModeInfoPtr pMode1,
320                                   QuartzModeInfoPtr pMode2) {
321    return (pMode1->width == pMode2->width) &&
322           (pMode1->height == pMode2->height) &&
323           (pMode1->refresh == pMode2->refresh);
324}
325
326static Bool QuartzRandRRegisterMode (ScreenPtr pScreen,
327                                     QuartzModeInfoPtr pMode) {
328    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
329    Bool isCurrentMode = QuartzRandRModesEqual(&pQuartzScreen->currentMode, pMode);
330
331    /* TODO: DPI */
332    pMode->pSize = RRRegisterSize(pScreen, pMode->width, pMode->height, pScreen->mmWidth, pScreen->mmHeight);
333    if (pMode->pSize) {
334        //DEBUG_LOG("registering: %d x %d @ %d %s\n", (int)pMode->width, (int)pMode->height, (int)pMode->refresh, isCurrentMode ? "*" : "");
335        RRRegisterRate(pScreen, pMode->pSize, pMode->refresh);
336
337        if (isCurrentMode)
338            RRSetCurrentConfig(pScreen, RR_Rotate_0, pMode->refresh, pMode->pSize);
339
340        return TRUE;
341    }
342    return FALSE;
343}
344
345static int QuartzRandRRegisterModeCallback (ScreenPtr pScreen,
346                                        QuartzModeInfoPtr pMode,
347                                        void *data __unused) {
348    if(QuartzRandRRegisterMode(pScreen, pMode)) {
349        return CALLBACK_CONTINUE;
350    } else {
351        return CALLBACK_ERROR;
352    }
353}
354
355static Bool QuartzRandRSetMode(ScreenPtr pScreen, QuartzModeInfoPtr pMode, BOOL doRegister) {
356    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
357    Bool captureDisplay = (pMode->refresh != FAKE_REFRESH_FULLSCREEN && pMode->refresh != FAKE_REFRESH_ROOTLESS);
358    CGDirectDisplayID screenId;
359
360    if(pQuartzScreen->displayIDs == NULL)
361        return FALSE;
362
363    screenId = pQuartzScreen->displayIDs[0];
364    if(XQuartzShieldingWindowLevel == 0 && captureDisplay) {
365        if(!X11ApplicationCanEnterRandR())
366            return FALSE;
367        CGCaptureAllDisplays();
368        XQuartzShieldingWindowLevel = CGShieldingWindowLevel(); // 2147483630
369        DEBUG_LOG("Display captured.  ShieldWindowID: %u, Shield level: %d\n",
370                  CGShieldingWindowID(screenId), XQuartzShieldingWindowLevel);
371    }
372
373    if (pQuartzScreen->currentMode.ref && CFEqual(pMode->ref, pQuartzScreen->currentMode.ref)) {
374        DEBUG_LOG("Requested RandR resolution matches current CG mode\n");
375    } if (QuartzRandRSetCGMode(screenId, pMode)) {
376        ignore_next_fake_mode_update = TRUE;
377    } else {
378        DEBUG_LOG("Error while requesting CG resolution change.\n");
379        return FALSE;
380    }
381
382    /* If the client requested the fake rootless mode, switch to rootless.
383     * Otherwise, force fullscreen mode.
384     */
385    QuartzSetRootless(pMode->refresh == FAKE_REFRESH_ROOTLESS);
386    if (pMode->refresh != FAKE_REFRESH_ROOTLESS) {
387        QuartzShowFullscreen(TRUE);
388    }
389
390    if(pQuartzScreen->currentMode.ref)
391        CFRelease(pQuartzScreen->currentMode.ref);
392    pQuartzScreen->currentMode = *pMode;
393    if(pQuartzScreen->currentMode.ref)
394        CFRetain(pQuartzScreen->currentMode.ref);
395
396    if(XQuartzShieldingWindowLevel != 0 && !captureDisplay) {
397        CGReleaseAllDisplays();
398        XQuartzShieldingWindowLevel = 0;
399    }
400
401    return TRUE;
402}
403
404static int QuartzRandRSetModeCallback (ScreenPtr pScreen,
405                                       QuartzModeInfoPtr pMode,
406                                       void *data) {
407    QuartzModeInfoPtr pReqMode = (QuartzModeInfoPtr) data;
408
409    if (!QuartzRandRModesEqual(pMode, pReqMode))
410        return CALLBACK_CONTINUE;  /* continue enumeration */
411
412    DEBUG_LOG("Found a match for requested RandR resolution (%dx%d@%d).\n", (int)pMode->width, (int)pMode->height, (int)pMode->refresh);
413
414    if(QuartzRandRSetMode(pScreen, pMode, FALSE))
415        return CALLBACK_SUCCESS;
416    else
417        return CALLBACK_ERROR;
418}
419
420static Bool QuartzRandRGetInfo (ScreenPtr pScreen, Rotation *rotations) {
421    *rotations = RR_Rotate_0;  /* TODO: support rotation */
422
423    return QuartzRandREnumerateModes(pScreen, QuartzRandRRegisterModeCallback, NULL);
424}
425
426static Bool QuartzRandRSetConfig (ScreenPtr           pScreen,
427                                  Rotation            randr,
428                                  int                 rate,
429                                  RRScreenSizePtr     pSize) {
430    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
431    QuartzModeInfo reqMode;
432
433    reqMode.width = pSize->width;
434    reqMode.height = pSize->height;
435    reqMode.refresh = rate;
436
437    /* Do not switch modes if requested mode is equal to current mode. */
438    if (QuartzRandRModesEqual(&reqMode, &pQuartzScreen->currentMode))
439        return TRUE;
440
441    if (QuartzRandREnumerateModes(pScreen, QuartzRandRSetModeCallback, &reqMode)) {
442        return TRUE;
443    }
444
445    DEBUG_LOG("Unable to find a matching config: %d x %d @ %d\n", (int)reqMode.width, (int)reqMode.height, (int)reqMode.refresh);
446    return FALSE;
447}
448
449static Bool _QuartzRandRUpdateFakeModes (ScreenPtr pScreen) {
450    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
451    QuartzModeInfo activeMode;
452
453    if(pQuartzScreen->displayCount > 0) {
454        if(!QuartzRandRCopyCurrentModeInfo(pQuartzScreen->displayIDs[0], &activeMode)) {
455            ErrorF("Unable to determine current display mode.\n");
456            return FALSE;
457        }
458    } else {
459        memset(&activeMode, 0, sizeof(activeMode));
460        activeMode.width = 800;
461        activeMode.height = 600;
462        activeMode.refresh = 60;
463    }
464
465    if(pQuartzScreen->fullscreenMode.ref)
466        CFRelease(pQuartzScreen->fullscreenMode.ref);
467    if(pQuartzScreen->currentMode.ref)
468        CFRelease(pQuartzScreen->currentMode.ref);
469
470    if(pQuartzScreen->displayCount > 1) {
471        activeMode.width = pScreen->width;
472        activeMode.height = pScreen->height;
473        if(XQuartzIsRootless)
474            activeMode.height += aquaMenuBarHeight;
475    }
476
477    pQuartzScreen->fullscreenMode = activeMode;
478    pQuartzScreen->fullscreenMode.refresh = FAKE_REFRESH_FULLSCREEN;
479
480    pQuartzScreen->rootlessMode = activeMode;
481    pQuartzScreen->rootlessMode.refresh = FAKE_REFRESH_ROOTLESS;
482    pQuartzScreen->rootlessMode.height -= aquaMenuBarHeight;
483
484    if(XQuartzIsRootless) {
485        pQuartzScreen->currentMode = pQuartzScreen->rootlessMode;
486    } else {
487        pQuartzScreen->currentMode = pQuartzScreen->fullscreenMode;
488    }
489
490    /* This extra retain is for currentMode's copy.
491     * fullscreen and rootless share a retain.
492     */
493    if(pQuartzScreen->currentMode.ref)
494        CFRetain(pQuartzScreen->currentMode.ref);
495
496    DEBUG_LOG("rootlessMode: %d x %d\n", (int)pQuartzScreen->rootlessMode.width, (int)pQuartzScreen->rootlessMode.height);
497    DEBUG_LOG("fullscreenMode: %d x %d\n", (int)pQuartzScreen->fullscreenMode.width, (int)pQuartzScreen->fullscreenMode.height);
498    DEBUG_LOG("currentMode: %d x %d\n", (int)pQuartzScreen->currentMode.width, (int)pQuartzScreen->currentMode.height);
499
500    return TRUE;
501}
502
503Bool QuartzRandRUpdateFakeModes (BOOL force_update) {
504    ScreenPtr pScreen = screenInfo.screens[0];
505
506    if(ignore_next_fake_mode_update) {
507        DEBUG_LOG("Ignoring update request caused by RandR resolution change.\n");
508        ignore_next_fake_mode_update = FALSE;
509        return TRUE;
510    }
511
512    if(!_QuartzRandRUpdateFakeModes(pScreen))
513        return FALSE;
514
515    if(force_update)
516        RRGetInfo(pScreen, TRUE);
517
518    return TRUE;
519}
520
521Bool QuartzRandRInit (ScreenPtr pScreen) {
522    rrScrPrivPtr    pScrPriv;
523
524    if (!RRScreenInit (pScreen)) return FALSE;
525    if (!_QuartzRandRUpdateFakeModes (pScreen)) return FALSE;
526
527    pScrPriv = rrGetScrPriv(pScreen);
528    pScrPriv->rrGetInfo = QuartzRandRGetInfo;
529    pScrPriv->rrSetConfig = QuartzRandRSetConfig;
530    return TRUE;
531}
532
533void QuartzRandRSetFakeRootless (void) {
534    int i;
535
536    DEBUG_LOG("QuartzRandRSetFakeRootless called.\n");
537
538    for (i=0; i < screenInfo.numScreens; i++) {
539        ScreenPtr pScreen = screenInfo.screens[i];
540        QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
541
542        QuartzRandRSetMode(pScreen, &pQuartzScreen->rootlessMode, TRUE);
543    }
544}
545
546void QuartzRandRSetFakeFullscreen (BOOL state) {
547    int i;
548
549    DEBUG_LOG("QuartzRandRSetFakeFullscreen called.\n");
550
551    for (i=0; i < screenInfo.numScreens; i++) {
552        ScreenPtr pScreen = screenInfo.screens[i];
553        QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
554
555        QuartzRandRSetMode(pScreen, &pQuartzScreen->fullscreenMode, TRUE);
556    }
557
558    QuartzShowFullscreen(state);
559}
560
561/* Toggle fullscreen mode.  If "fake" fullscreen is the current mode,
562 * this will just show/hide the X11 windows.  If we are in a RandR fullscreen
563 * mode, this will toggles us to the default fake mode and hide windows if
564 * it is fullscreen
565 */
566void QuartzRandRToggleFullscreen (void) {
567    ScreenPtr pScreen = screenInfo.screens[0];
568    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
569
570    if (pQuartzScreen->currentMode.ref == NULL) {
571        ErrorF("Ignoring QuartzRandRToggleFullscreen because don't have a current mode set.\n");
572    } else if (pQuartzScreen->currentMode.refresh == FAKE_REFRESH_ROOTLESS) {
573        ErrorF("Ignoring QuartzRandRToggleFullscreen because we are in rootless mode.\n");
574    } else if (pQuartzScreen->currentMode.refresh == FAKE_REFRESH_FULLSCREEN) {
575        /* Legacy fullscreen mode.  Hide/Show */
576        QuartzShowFullscreen(!XQuartzFullscreenVisible);
577    } else {
578        /* RandR fullscreen mode.  Return to default mode and hide if it is fullscreen. */
579        if(XQuartzRootlessDefault) {
580            QuartzRandRSetFakeRootless();
581        } else {
582            QuartzRandRSetFakeFullscreen(FALSE);
583        }
584    }
585}
586