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