1706f2543Smrg/*
2706f2543Smrg * Quartz-specific support for the XRandR extension
3706f2543Smrg *
4706f2543Smrg * Copyright (c) 2001-2004 Greg Parker and Torrey T. Lyons,
5706f2543Smrg *               2010      Jan Hauffa.
6706f2543Smrg *               2010-2011 Apple Inc.
7706f2543Smrg *                 All Rights Reserved.
8706f2543Smrg *
9706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a
10706f2543Smrg * copy of this software and associated documentation files (the "Software"),
11706f2543Smrg * to deal in the Software without restriction, including without limitation
12706f2543Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13706f2543Smrg * and/or sell copies of the Software, and to permit persons to whom the
14706f2543Smrg * Software is furnished to do so, subject to the following conditions:
15706f2543Smrg *
16706f2543Smrg * The above copyright notice and this permission notice shall be included in
17706f2543Smrg * all copies or substantial portions of the Software.
18706f2543Smrg *
19706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22706f2543Smrg * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
23706f2543Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24706f2543Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25706f2543Smrg * DEALINGS IN THE SOFTWARE.
26706f2543Smrg *
27706f2543Smrg * Except as contained in this notice, the name(s) of the above copyright
28706f2543Smrg * holders shall not be used in advertising or otherwise to promote the sale,
29706f2543Smrg * use or other dealings in this Software without prior written authorization.
30706f2543Smrg */
31706f2543Smrg
32706f2543Smrg#include "sanitizedCarbon.h"
33706f2543Smrg
34706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
35706f2543Smrg#include <dix-config.h>
36706f2543Smrg#endif
37706f2543Smrg
38706f2543Smrg#include "quartzCommon.h"
39706f2543Smrg#include "quartzRandR.h"
40706f2543Smrg#include "quartz.h"
41706f2543Smrg#include "darwin.h"
42706f2543Smrg
43706f2543Smrg#include "X11Application.h"
44706f2543Smrg
45706f2543Smrg#include <AvailabilityMacros.h>
46706f2543Smrg
47706f2543Smrg#include <X11/extensions/randr.h>
48706f2543Smrg#include <randrstr.h>
49706f2543Smrg#include <IOKit/graphics/IOGraphicsTypes.h>
50706f2543Smrg
51706f2543Smrg/* TODO: UGLY, find a better way!
52706f2543Smrg * We want to ignore kXquartzDisplayChanged which are generated by us
53706f2543Smrg */
54706f2543Smrgstatic Bool ignore_next_fake_mode_update = FALSE;
55706f2543Smrg
56706f2543Smrg#define FAKE_REFRESH_ROOTLESS 1
57706f2543Smrg#define FAKE_REFRESH_FULLSCREEN 2
58706f2543Smrg
59706f2543Smrg#define DEFAULT_REFRESH  60
60706f2543Smrg#define kDisplayModeUsableFlags  (kDisplayModeValidFlag | kDisplayModeSafeFlag)
61706f2543Smrg
62706f2543Smrg#define CALLBACK_SUCCESS 0
63706f2543Smrg#define CALLBACK_CONTINUE 1
64706f2543Smrg#define CALLBACK_ERROR -1
65706f2543Smrg
66706f2543Smrgtypedef int (*QuartzModeCallback)
67706f2543Smrg    (ScreenPtr, QuartzModeInfoPtr, void *);
68706f2543Smrg
69706f2543Smrg#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
70706f2543Smrg
71706f2543Smrgstatic long getDictLong (CFDictionaryRef dictRef, CFStringRef key) {
72706f2543Smrg    long value;
73706f2543Smrg
74706f2543Smrg    CFNumberRef numRef = (CFNumberRef) CFDictionaryGetValue(dictRef, key);
75706f2543Smrg    if (!numRef)
76706f2543Smrg        return 0;
77706f2543Smrg
78706f2543Smrg    if (!CFNumberGetValue(numRef, kCFNumberLongType, &value))
79706f2543Smrg        return 0;
80706f2543Smrg    return value;
81706f2543Smrg}
82706f2543Smrg
83706f2543Smrgstatic double getDictDouble (CFDictionaryRef dictRef, CFStringRef key) {
84706f2543Smrg    double value;
85706f2543Smrg
86706f2543Smrg    CFNumberRef numRef = (CFNumberRef) CFDictionaryGetValue(dictRef, key);
87706f2543Smrg    if (!numRef)
88706f2543Smrg        return 0.0;
89706f2543Smrg
90706f2543Smrg    if (!CFNumberGetValue(numRef, kCFNumberDoubleType, &value))
91706f2543Smrg        return 0.0;
92706f2543Smrg    return value;
93706f2543Smrg}
94706f2543Smrg
95706f2543Smrgstatic void QuartzRandRGetModeInfo (CFDictionaryRef modeRef,
96706f2543Smrg                                    QuartzModeInfoPtr pMode) {
97706f2543Smrg    pMode->width = (size_t) getDictLong(modeRef, kCGDisplayWidth);
98706f2543Smrg    pMode->height = (size_t) getDictLong(modeRef, kCGDisplayHeight);
99706f2543Smrg    pMode->refresh = (int)(getDictDouble(modeRef, kCGDisplayRefreshRate) + 0.5);
100706f2543Smrg    if (pMode->refresh == 0)
101706f2543Smrg        pMode->refresh = DEFAULT_REFRESH;
102706f2543Smrg    pMode->ref = NULL;
103706f2543Smrg    pMode->pSize = NULL;
104706f2543Smrg}
105706f2543Smrg
106706f2543Smrgstatic Bool QuartzRandRCopyCurrentModeInfo (CGDirectDisplayID screenId,
107706f2543Smrg                                           QuartzModeInfoPtr pMode) {
108706f2543Smrg    CFDictionaryRef curModeRef = CGDisplayCurrentMode(screenId);
109706f2543Smrg    if (!curModeRef)
110706f2543Smrg        return FALSE;
111706f2543Smrg
112706f2543Smrg    QuartzRandRGetModeInfo(curModeRef, pMode);
113706f2543Smrg    pMode->ref = (void *)curModeRef;
114706f2543Smrg    CFRetain(pMode->ref);
115706f2543Smrg    return TRUE;
116706f2543Smrg}
117706f2543Smrg
118706f2543Smrgstatic Bool QuartzRandRSetCGMode (CGDirectDisplayID screenId,
119706f2543Smrg                                QuartzModeInfoPtr pMode) {
120706f2543Smrg    CFDictionaryRef modeRef = (CFDictionaryRef) pMode->ref;
121706f2543Smrg    return (CGDisplaySwitchToMode(screenId, modeRef) == kCGErrorSuccess);
122706f2543Smrg}
123706f2543Smrg
124706f2543Smrgstatic Bool QuartzRandREnumerateModes (ScreenPtr pScreen,
125706f2543Smrg                                       QuartzModeCallback callback,
126706f2543Smrg                                       void *data) {
127706f2543Smrg    Bool retval = FALSE;
128706f2543Smrg    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
129706f2543Smrg
130706f2543Smrg    /* Just an 800x600 fallback if we have no attached heads */
131706f2543Smrg    if(pQuartzScreen->displayIDs) {
132706f2543Smrg        CFDictionaryRef curModeRef, modeRef;
133706f2543Smrg        long curBpp;
134706f2543Smrg        CFArrayRef modes;
135706f2543Smrg        QuartzModeInfo modeInfo;
136706f2543Smrg        int i;
137706f2543Smrg        CGDirectDisplayID screenId = pQuartzScreen->displayIDs[0];
138706f2543Smrg
139706f2543Smrg        curModeRef = CGDisplayCurrentMode(screenId);
140706f2543Smrg        if (!curModeRef)
141706f2543Smrg            return FALSE;
142706f2543Smrg        curBpp = getDictLong(curModeRef, kCGDisplayBitsPerPixel);
143706f2543Smrg
144706f2543Smrg        modes = CGDisplayAvailableModes(screenId);
145706f2543Smrg        if (!modes)
146706f2543Smrg            return FALSE;
147706f2543Smrg        for (i = 0; i < CFArrayGetCount(modes); i++) {
148706f2543Smrg            int cb;
149706f2543Smrg            modeRef = (CFDictionaryRef) CFArrayGetValueAtIndex(modes, i);
150706f2543Smrg
151706f2543Smrg            /* Skip modes that are not usable on the current display or have a
152706f2543Smrg               different pixel encoding than the current mode. */
153706f2543Smrg            if (((unsigned long) getDictLong(modeRef, kCGDisplayIOFlags) &
154706f2543Smrg                 kDisplayModeUsableFlags) != kDisplayModeUsableFlags)
155706f2543Smrg                continue;
156706f2543Smrg            if (getDictLong(modeRef, kCGDisplayBitsPerPixel) != curBpp)
157706f2543Smrg                continue;
158706f2543Smrg
159706f2543Smrg            QuartzRandRGetModeInfo(modeRef, &modeInfo);
160706f2543Smrg            modeInfo.ref = (void *)modeRef;
161706f2543Smrg            cb = callback(pScreen, &modeInfo, data);
162706f2543Smrg            if (cb == CALLBACK_CONTINUE)
163706f2543Smrg                retval = TRUE;
164706f2543Smrg            else if (cb == CALLBACK_SUCCESS)
165706f2543Smrg                return TRUE;
166706f2543Smrg            else if (cb == CALLBACK_ERROR)
167706f2543Smrg                return FALSE;
168706f2543Smrg        }
169706f2543Smrg    }
170706f2543Smrg
171706f2543Smrg    switch(callback(pScreen, &pQuartzScreen->rootlessMode, data)) {
172706f2543Smrg        case CALLBACK_SUCCESS:
173706f2543Smrg            return TRUE;
174706f2543Smrg        case CALLBACK_ERROR:
175706f2543Smrg            return FALSE;
176706f2543Smrg        case CALLBACK_CONTINUE:
177706f2543Smrg            retval = TRUE;
178706f2543Smrg        default:
179706f2543Smrg            break;
180706f2543Smrg    }
181706f2543Smrg
182706f2543Smrg    switch(callback(pScreen, &pQuartzScreen->fullscreenMode, data)) {
183706f2543Smrg        case CALLBACK_SUCCESS:
184706f2543Smrg            return TRUE;
185706f2543Smrg        case CALLBACK_ERROR:
186706f2543Smrg            return FALSE;
187706f2543Smrg        case CALLBACK_CONTINUE:
188706f2543Smrg            retval = TRUE;
189706f2543Smrg        default:
190706f2543Smrg            break;
191706f2543Smrg    }
192706f2543Smrg
193706f2543Smrg    return retval;
194706f2543Smrg}
195706f2543Smrg
196706f2543Smrg#else /* we have the new CG APIs from Snow Leopard */
197706f2543Smrg
198706f2543Smrgstatic void QuartzRandRGetModeInfo (CGDisplayModeRef modeRef,
199706f2543Smrg                                    QuartzModeInfoPtr pMode) {
200706f2543Smrg    pMode->width = CGDisplayModeGetWidth(modeRef);
201706f2543Smrg    pMode->height = CGDisplayModeGetHeight(modeRef);
202706f2543Smrg    pMode->refresh = (int) (CGDisplayModeGetRefreshRate(modeRef) + 0.5);
203706f2543Smrg    if (pMode->refresh == 0)
204706f2543Smrg        pMode->refresh = DEFAULT_REFRESH;
205706f2543Smrg    pMode->ref = NULL;
206706f2543Smrg    pMode->pSize = NULL;
207706f2543Smrg}
208706f2543Smrg
209706f2543Smrgstatic Bool QuartzRandRCopyCurrentModeInfo (CGDirectDisplayID screenId,
210706f2543Smrg                                            QuartzModeInfoPtr pMode) {
211706f2543Smrg    CGDisplayModeRef curModeRef = CGDisplayCopyDisplayMode(screenId);
212706f2543Smrg    if (!curModeRef)
213706f2543Smrg        return FALSE;
214706f2543Smrg
215706f2543Smrg    QuartzRandRGetModeInfo(curModeRef, pMode);
216706f2543Smrg    pMode->ref = curModeRef;
217706f2543Smrg    return TRUE;
218706f2543Smrg}
219706f2543Smrg
220706f2543Smrgstatic Bool QuartzRandRSetCGMode (CGDirectDisplayID screenId,
221706f2543Smrg                                QuartzModeInfoPtr pMode) {
222706f2543Smrg    CGDisplayModeRef modeRef = (CGDisplayModeRef) pMode->ref;
223706f2543Smrg    if (!modeRef)
224706f2543Smrg        return FALSE;
225706f2543Smrg
226706f2543Smrg    return (CGDisplaySetDisplayMode(screenId, modeRef, NULL) == kCGErrorSuccess);
227706f2543Smrg}
228706f2543Smrg
229706f2543Smrgstatic Bool QuartzRandREnumerateModes (ScreenPtr pScreen,
230706f2543Smrg                                       QuartzModeCallback callback,
231706f2543Smrg                                       void *data) {
232706f2543Smrg    Bool retval = FALSE;
233706f2543Smrg    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
234706f2543Smrg
235706f2543Smrg    /* Just an 800x600 fallback if we have no attached heads */
236706f2543Smrg    if(pQuartzScreen->displayIDs) {
237706f2543Smrg        CGDisplayModeRef curModeRef, modeRef;
238706f2543Smrg        CFStringRef curPixelEnc, pixelEnc;
239706f2543Smrg        CFComparisonResult pixelEncEqual;
240706f2543Smrg        CFArrayRef modes;
241706f2543Smrg        QuartzModeInfo modeInfo;
242706f2543Smrg        int i;
243706f2543Smrg        CGDirectDisplayID screenId = pQuartzScreen->displayIDs[0];
244706f2543Smrg
245706f2543Smrg        curModeRef = CGDisplayCopyDisplayMode(screenId);
246706f2543Smrg        if (!curModeRef)
247706f2543Smrg            return FALSE;
248706f2543Smrg        curPixelEnc = CGDisplayModeCopyPixelEncoding(curModeRef);
249706f2543Smrg        CGDisplayModeRelease(curModeRef);
250706f2543Smrg
251706f2543Smrg        modes = CGDisplayCopyAllDisplayModes(screenId, NULL);
252706f2543Smrg        if (!modes) {
253706f2543Smrg            CFRelease(curPixelEnc);
254706f2543Smrg            return FALSE;
255706f2543Smrg        }
256706f2543Smrg        for (i = 0; i < CFArrayGetCount(modes); i++) {
257706f2543Smrg            int cb;
258706f2543Smrg            modeRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
259706f2543Smrg
260706f2543Smrg            /* Skip modes that are not usable on the current display or have a
261706f2543Smrg               different pixel encoding than the current mode. */
262706f2543Smrg            if ((CGDisplayModeGetIOFlags(modeRef) & kDisplayModeUsableFlags) !=
263706f2543Smrg                kDisplayModeUsableFlags)
264706f2543Smrg                continue;
265706f2543Smrg            pixelEnc = CGDisplayModeCopyPixelEncoding(modeRef);
266706f2543Smrg            pixelEncEqual = CFStringCompare(pixelEnc, curPixelEnc, 0);
267706f2543Smrg            CFRelease(pixelEnc);
268706f2543Smrg            if (pixelEncEqual != kCFCompareEqualTo)
269706f2543Smrg                continue;
270706f2543Smrg
271706f2543Smrg            QuartzRandRGetModeInfo(modeRef, &modeInfo);
272706f2543Smrg            modeInfo.ref = modeRef;
273706f2543Smrg            cb = callback(pScreen, &modeInfo, data);
274706f2543Smrg            if (cb == CALLBACK_CONTINUE) {
275706f2543Smrg                retval = TRUE;
276706f2543Smrg            } else if (cb == CALLBACK_SUCCESS) {
277706f2543Smrg                CFRelease(modes);
278706f2543Smrg                CFRelease(curPixelEnc);
279706f2543Smrg                return TRUE;
280706f2543Smrg            } else if (cb == CALLBACK_ERROR) {
281706f2543Smrg                CFRelease(modes);
282706f2543Smrg                CFRelease(curPixelEnc);
283706f2543Smrg                return FALSE;
284706f2543Smrg            }
285706f2543Smrg        }
286706f2543Smrg
287706f2543Smrg        CFRelease(modes);
288706f2543Smrg        CFRelease(curPixelEnc);
289706f2543Smrg    }
290706f2543Smrg
291706f2543Smrg    switch(callback(pScreen, &pQuartzScreen->rootlessMode, data)) {
292706f2543Smrg        case CALLBACK_SUCCESS:
293706f2543Smrg            return TRUE;
294706f2543Smrg        case CALLBACK_ERROR:
295706f2543Smrg            return FALSE;
296706f2543Smrg        case CALLBACK_CONTINUE:
297706f2543Smrg            retval = TRUE;
298706f2543Smrg        default:
299706f2543Smrg            break;
300706f2543Smrg    }
301706f2543Smrg
302706f2543Smrg    switch(callback(pScreen, &pQuartzScreen->fullscreenMode, data)) {
303706f2543Smrg        case CALLBACK_SUCCESS:
304706f2543Smrg            return TRUE;
305706f2543Smrg        case CALLBACK_ERROR:
306706f2543Smrg            return FALSE;
307706f2543Smrg        case CALLBACK_CONTINUE:
308706f2543Smrg            retval = TRUE;
309706f2543Smrg        default:
310706f2543Smrg            break;
311706f2543Smrg    }
312706f2543Smrg
313706f2543Smrg    return retval;
314706f2543Smrg}
315706f2543Smrg
316706f2543Smrg#endif  /* Snow Leopard CoreGraphics APIs */
317706f2543Smrg
318706f2543Smrg
319706f2543Smrgstatic Bool QuartzRandRModesEqual (QuartzModeInfoPtr pMode1,
320706f2543Smrg                                   QuartzModeInfoPtr pMode2) {
321706f2543Smrg    return (pMode1->width == pMode2->width) &&
322706f2543Smrg           (pMode1->height == pMode2->height) &&
323706f2543Smrg           (pMode1->refresh == pMode2->refresh);
324706f2543Smrg}
325706f2543Smrg
326706f2543Smrgstatic Bool QuartzRandRRegisterMode (ScreenPtr pScreen,
327706f2543Smrg                                     QuartzModeInfoPtr pMode) {
328706f2543Smrg    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
329706f2543Smrg    Bool isCurrentMode = QuartzRandRModesEqual(&pQuartzScreen->currentMode, pMode);
330706f2543Smrg
331706f2543Smrg    /* TODO: DPI */
332706f2543Smrg    pMode->pSize = RRRegisterSize(pScreen, pMode->width, pMode->height, pScreen->mmWidth, pScreen->mmHeight);
333706f2543Smrg    if (pMode->pSize) {
334706f2543Smrg        //DEBUG_LOG("registering: %d x %d @ %d %s\n", (int)pMode->width, (int)pMode->height, (int)pMode->refresh, isCurrentMode ? "*" : "");
335706f2543Smrg        RRRegisterRate(pScreen, pMode->pSize, pMode->refresh);
336706f2543Smrg
337706f2543Smrg        if (isCurrentMode)
338706f2543Smrg            RRSetCurrentConfig(pScreen, RR_Rotate_0, pMode->refresh, pMode->pSize);
339706f2543Smrg
340706f2543Smrg        return TRUE;
341706f2543Smrg    }
342706f2543Smrg    return FALSE;
343706f2543Smrg}
344706f2543Smrg
345706f2543Smrgstatic int QuartzRandRRegisterModeCallback (ScreenPtr pScreen,
346706f2543Smrg                                        QuartzModeInfoPtr pMode,
347706f2543Smrg                                        void *data __unused) {
348706f2543Smrg    if(QuartzRandRRegisterMode(pScreen, pMode)) {
349706f2543Smrg        return CALLBACK_CONTINUE;
350706f2543Smrg    } else {
351706f2543Smrg        return CALLBACK_ERROR;
352706f2543Smrg    }
353706f2543Smrg}
354706f2543Smrg
355706f2543Smrgstatic Bool QuartzRandRSetMode(ScreenPtr pScreen, QuartzModeInfoPtr pMode, BOOL doRegister) {
356706f2543Smrg    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
357706f2543Smrg    Bool captureDisplay = (pMode->refresh != FAKE_REFRESH_FULLSCREEN && pMode->refresh != FAKE_REFRESH_ROOTLESS);
358706f2543Smrg    CGDirectDisplayID screenId;
359706f2543Smrg
360706f2543Smrg    if(pQuartzScreen->displayIDs == NULL)
361706f2543Smrg        return FALSE;
362706f2543Smrg
363706f2543Smrg    screenId = pQuartzScreen->displayIDs[0];
364706f2543Smrg    if(XQuartzShieldingWindowLevel == 0 && captureDisplay) {
365706f2543Smrg        if(!X11ApplicationCanEnterRandR())
366706f2543Smrg            return FALSE;
367706f2543Smrg        CGCaptureAllDisplays();
368706f2543Smrg        XQuartzShieldingWindowLevel = CGShieldingWindowLevel(); // 2147483630
369706f2543Smrg        DEBUG_LOG("Display captured.  ShieldWindowID: %u, Shield level: %d\n",
370706f2543Smrg                  CGShieldingWindowID(screenId), XQuartzShieldingWindowLevel);
371706f2543Smrg    }
372706f2543Smrg
373706f2543Smrg    if (pQuartzScreen->currentMode.ref && CFEqual(pMode->ref, pQuartzScreen->currentMode.ref)) {
374706f2543Smrg        DEBUG_LOG("Requested RandR resolution matches current CG mode\n");
375706f2543Smrg    } if (QuartzRandRSetCGMode(screenId, pMode)) {
376706f2543Smrg        ignore_next_fake_mode_update = TRUE;
377706f2543Smrg    } else {
378706f2543Smrg        DEBUG_LOG("Error while requesting CG resolution change.\n");
379706f2543Smrg        return FALSE;
380706f2543Smrg    }
381706f2543Smrg
382706f2543Smrg    /* If the client requested the fake rootless mode, switch to rootless.
383706f2543Smrg     * Otherwise, force fullscreen mode.
384706f2543Smrg     */
385706f2543Smrg    QuartzSetRootless(pMode->refresh == FAKE_REFRESH_ROOTLESS);
386706f2543Smrg    if (pMode->refresh != FAKE_REFRESH_ROOTLESS) {
387706f2543Smrg        QuartzShowFullscreen(TRUE);
388706f2543Smrg    }
389706f2543Smrg
390706f2543Smrg    if(pQuartzScreen->currentMode.ref)
391706f2543Smrg        CFRelease(pQuartzScreen->currentMode.ref);
392706f2543Smrg    pQuartzScreen->currentMode = *pMode;
393706f2543Smrg    if(pQuartzScreen->currentMode.ref)
394706f2543Smrg        CFRetain(pQuartzScreen->currentMode.ref);
395706f2543Smrg
396706f2543Smrg    if(XQuartzShieldingWindowLevel != 0 && !captureDisplay) {
397706f2543Smrg        CGReleaseAllDisplays();
398706f2543Smrg        XQuartzShieldingWindowLevel = 0;
399706f2543Smrg    }
400706f2543Smrg
401706f2543Smrg    return TRUE;
402706f2543Smrg}
403706f2543Smrg
404706f2543Smrgstatic int QuartzRandRSetModeCallback (ScreenPtr pScreen,
405706f2543Smrg                                       QuartzModeInfoPtr pMode,
406706f2543Smrg                                       void *data) {
407706f2543Smrg    QuartzModeInfoPtr pReqMode = (QuartzModeInfoPtr) data;
408706f2543Smrg
409706f2543Smrg    if (!QuartzRandRModesEqual(pMode, pReqMode))
410706f2543Smrg        return CALLBACK_CONTINUE;  /* continue enumeration */
411706f2543Smrg
412706f2543Smrg    DEBUG_LOG("Found a match for requested RandR resolution (%dx%d@%d).\n", (int)pMode->width, (int)pMode->height, (int)pMode->refresh);
413706f2543Smrg
414706f2543Smrg    if(QuartzRandRSetMode(pScreen, pMode, FALSE))
415706f2543Smrg        return CALLBACK_SUCCESS;
416706f2543Smrg    else
417706f2543Smrg        return CALLBACK_ERROR;
418706f2543Smrg}
419706f2543Smrg
420706f2543Smrgstatic Bool QuartzRandRGetInfo (ScreenPtr pScreen, Rotation *rotations) {
421706f2543Smrg    *rotations = RR_Rotate_0;  /* TODO: support rotation */
422706f2543Smrg
423706f2543Smrg    return QuartzRandREnumerateModes(pScreen, QuartzRandRRegisterModeCallback, NULL);
424706f2543Smrg}
425706f2543Smrg
426706f2543Smrgstatic Bool QuartzRandRSetConfig (ScreenPtr           pScreen,
427706f2543Smrg                                  Rotation            randr,
428706f2543Smrg                                  int                 rate,
429706f2543Smrg                                  RRScreenSizePtr     pSize) {
430706f2543Smrg    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
431706f2543Smrg    QuartzModeInfo reqMode;
432706f2543Smrg
433706f2543Smrg    reqMode.width = pSize->width;
434706f2543Smrg    reqMode.height = pSize->height;
435706f2543Smrg    reqMode.refresh = rate;
436706f2543Smrg
437706f2543Smrg    /* Do not switch modes if requested mode is equal to current mode. */
438706f2543Smrg    if (QuartzRandRModesEqual(&reqMode, &pQuartzScreen->currentMode))
439706f2543Smrg        return TRUE;
440706f2543Smrg
441706f2543Smrg    if (QuartzRandREnumerateModes(pScreen, QuartzRandRSetModeCallback, &reqMode)) {
442706f2543Smrg        return TRUE;
443706f2543Smrg    }
444706f2543Smrg
445706f2543Smrg    DEBUG_LOG("Unable to find a matching config: %d x %d @ %d\n", (int)reqMode.width, (int)reqMode.height, (int)reqMode.refresh);
446706f2543Smrg    return FALSE;
447706f2543Smrg}
448706f2543Smrg
449706f2543Smrgstatic Bool _QuartzRandRUpdateFakeModes (ScreenPtr pScreen) {
450706f2543Smrg    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
451706f2543Smrg    QuartzModeInfo activeMode;
452706f2543Smrg
453706f2543Smrg    if(pQuartzScreen->displayCount > 0) {
454706f2543Smrg        if(!QuartzRandRCopyCurrentModeInfo(pQuartzScreen->displayIDs[0], &activeMode)) {
455706f2543Smrg            ErrorF("Unable to determine current display mode.\n");
456706f2543Smrg            return FALSE;
457706f2543Smrg        }
458706f2543Smrg    } else {
459706f2543Smrg        memset(&activeMode, 0, sizeof(activeMode));
460706f2543Smrg        activeMode.width = 800;
461706f2543Smrg        activeMode.height = 600;
462706f2543Smrg        activeMode.refresh = 60;
463706f2543Smrg    }
464706f2543Smrg
465706f2543Smrg    if(pQuartzScreen->fullscreenMode.ref)
466706f2543Smrg        CFRelease(pQuartzScreen->fullscreenMode.ref);
467706f2543Smrg    if(pQuartzScreen->currentMode.ref)
468706f2543Smrg        CFRelease(pQuartzScreen->currentMode.ref);
469706f2543Smrg
470706f2543Smrg    if(pQuartzScreen->displayCount > 1) {
471706f2543Smrg        activeMode.width = pScreen->width;
472706f2543Smrg        activeMode.height = pScreen->height;
473706f2543Smrg        if(XQuartzIsRootless)
474706f2543Smrg            activeMode.height += aquaMenuBarHeight;
475706f2543Smrg    }
476706f2543Smrg
477706f2543Smrg    pQuartzScreen->fullscreenMode = activeMode;
478706f2543Smrg    pQuartzScreen->fullscreenMode.refresh = FAKE_REFRESH_FULLSCREEN;
479706f2543Smrg
480706f2543Smrg    pQuartzScreen->rootlessMode = activeMode;
481706f2543Smrg    pQuartzScreen->rootlessMode.refresh = FAKE_REFRESH_ROOTLESS;
482706f2543Smrg    pQuartzScreen->rootlessMode.height -= aquaMenuBarHeight;
483706f2543Smrg
484706f2543Smrg    if(XQuartzIsRootless) {
485706f2543Smrg        pQuartzScreen->currentMode = pQuartzScreen->rootlessMode;
486706f2543Smrg    } else {
487706f2543Smrg        pQuartzScreen->currentMode = pQuartzScreen->fullscreenMode;
488706f2543Smrg    }
489706f2543Smrg
490706f2543Smrg    /* This extra retain is for currentMode's copy.
491706f2543Smrg     * fullscreen and rootless share a retain.
492706f2543Smrg     */
493706f2543Smrg    if(pQuartzScreen->currentMode.ref)
494706f2543Smrg        CFRetain(pQuartzScreen->currentMode.ref);
495706f2543Smrg
496706f2543Smrg    DEBUG_LOG("rootlessMode: %d x %d\n", (int)pQuartzScreen->rootlessMode.width, (int)pQuartzScreen->rootlessMode.height);
497706f2543Smrg    DEBUG_LOG("fullscreenMode: %d x %d\n", (int)pQuartzScreen->fullscreenMode.width, (int)pQuartzScreen->fullscreenMode.height);
498706f2543Smrg    DEBUG_LOG("currentMode: %d x %d\n", (int)pQuartzScreen->currentMode.width, (int)pQuartzScreen->currentMode.height);
499706f2543Smrg
500706f2543Smrg    return TRUE;
501706f2543Smrg}
502706f2543Smrg
503706f2543SmrgBool QuartzRandRUpdateFakeModes (BOOL force_update) {
504706f2543Smrg    ScreenPtr pScreen = screenInfo.screens[0];
505706f2543Smrg
506706f2543Smrg    if(ignore_next_fake_mode_update) {
507706f2543Smrg        DEBUG_LOG("Ignoring update request caused by RandR resolution change.\n");
508706f2543Smrg        ignore_next_fake_mode_update = FALSE;
509706f2543Smrg        return TRUE;
510706f2543Smrg    }
511706f2543Smrg
512706f2543Smrg    if(!_QuartzRandRUpdateFakeModes(pScreen))
513706f2543Smrg        return FALSE;
514706f2543Smrg
515706f2543Smrg    if(force_update)
516706f2543Smrg        RRGetInfo(pScreen, TRUE);
517706f2543Smrg
518706f2543Smrg    return TRUE;
519706f2543Smrg}
520706f2543Smrg
521706f2543SmrgBool QuartzRandRInit (ScreenPtr pScreen) {
522706f2543Smrg    rrScrPrivPtr    pScrPriv;
523706f2543Smrg
524706f2543Smrg    if (!RRScreenInit (pScreen)) return FALSE;
525706f2543Smrg    if (!_QuartzRandRUpdateFakeModes (pScreen)) return FALSE;
526706f2543Smrg
527706f2543Smrg    pScrPriv = rrGetScrPriv(pScreen);
528706f2543Smrg    pScrPriv->rrGetInfo = QuartzRandRGetInfo;
529706f2543Smrg    pScrPriv->rrSetConfig = QuartzRandRSetConfig;
530706f2543Smrg    return TRUE;
531706f2543Smrg}
532706f2543Smrg
533706f2543Smrgvoid QuartzRandRSetFakeRootless (void) {
534706f2543Smrg    int i;
535706f2543Smrg
536706f2543Smrg    DEBUG_LOG("QuartzRandRSetFakeRootless called.\n");
537706f2543Smrg
538706f2543Smrg    for (i=0; i < screenInfo.numScreens; i++) {
539706f2543Smrg        ScreenPtr pScreen = screenInfo.screens[i];
540706f2543Smrg        QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
541706f2543Smrg
542706f2543Smrg        QuartzRandRSetMode(pScreen, &pQuartzScreen->rootlessMode, TRUE);
543706f2543Smrg    }
544706f2543Smrg}
545706f2543Smrg
546706f2543Smrgvoid QuartzRandRSetFakeFullscreen (BOOL state) {
547706f2543Smrg    int i;
548706f2543Smrg
549706f2543Smrg    DEBUG_LOG("QuartzRandRSetFakeFullscreen called.\n");
550706f2543Smrg
551706f2543Smrg    for (i=0; i < screenInfo.numScreens; i++) {
552706f2543Smrg        ScreenPtr pScreen = screenInfo.screens[i];
553706f2543Smrg        QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
554706f2543Smrg
555706f2543Smrg        QuartzRandRSetMode(pScreen, &pQuartzScreen->fullscreenMode, TRUE);
556706f2543Smrg    }
557706f2543Smrg
558706f2543Smrg    QuartzShowFullscreen(state);
559706f2543Smrg}
560706f2543Smrg
561706f2543Smrg/* Toggle fullscreen mode.  If "fake" fullscreen is the current mode,
562706f2543Smrg * this will just show/hide the X11 windows.  If we are in a RandR fullscreen
563706f2543Smrg * mode, this will toggles us to the default fake mode and hide windows if
564706f2543Smrg * it is fullscreen
565706f2543Smrg */
566706f2543Smrgvoid QuartzRandRToggleFullscreen (void) {
567706f2543Smrg    ScreenPtr pScreen = screenInfo.screens[0];
568706f2543Smrg    QuartzScreenPtr pQuartzScreen = QUARTZ_PRIV(pScreen);
569706f2543Smrg
570706f2543Smrg    if (pQuartzScreen->currentMode.ref == NULL) {
571706f2543Smrg        ErrorF("Ignoring QuartzRandRToggleFullscreen because don't have a current mode set.\n");
572706f2543Smrg    } else if (pQuartzScreen->currentMode.refresh == FAKE_REFRESH_ROOTLESS) {
573706f2543Smrg        ErrorF("Ignoring QuartzRandRToggleFullscreen because we are in rootless mode.\n");
574706f2543Smrg    } else if (pQuartzScreen->currentMode.refresh == FAKE_REFRESH_FULLSCREEN) {
575706f2543Smrg        /* Legacy fullscreen mode.  Hide/Show */
576706f2543Smrg        QuartzShowFullscreen(!XQuartzFullscreenVisible);
577706f2543Smrg    } else {
578706f2543Smrg        /* RandR fullscreen mode.  Return to default mode and hide if it is fullscreen. */
579706f2543Smrg        if(XQuartzRootlessDefault) {
580706f2543Smrg            QuartzRandRSetFakeRootless();
581706f2543Smrg        } else {
582706f2543Smrg            QuartzRandRSetFakeFullscreen(FALSE);
583706f2543Smrg        }
584706f2543Smrg    }
585706f2543Smrg}
586