xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

quartzRandR.c (16270B)


      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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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 }