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