/* * Screen routines for full screen Quartz mode * * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * TORREY T. LYONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name(s) of the above copyright * holders shall not be used in advertising or otherwise to promote the sale, * use or other dealings in this Software without prior written authorization. */ #include "quartzCommon.h" #include "darwin.h" #include "quartz.h" #include "quartzCursor.h" #include "colormapst.h" #include "scrnintstr.h" #include "micmap.h" #include "shadow.h" // Full screen specific per screen storage structure typedef struct { CGDirectDisplayID displayID; CFDictionaryRef xDisplayMode; CFDictionaryRef aquaDisplayMode; CGDirectPaletteRef xPalette; CGDirectPaletteRef aquaPalette; unsigned char *framebuffer; unsigned char *shadowPtr; } FSScreenRec, *FSScreenPtr; #define FULLSCREEN_PRIV(pScreen) \ ((FSScreenPtr)pScreen->devPrivates[fsScreenIndex].ptr) static int fsScreenIndex; static CGDirectDisplayID *quartzDisplayList = NULL; static int quartzNumScreens = 0; static FSScreenPtr quartzScreens[MAXSCREENS]; static int darwinCmapPrivateIndex = -1; static unsigned long darwinCmapGeneration = 0; #define CMAP_PRIV(pCmap) \ ((CGDirectPaletteRef) (pCmap)->devPrivates[darwinCmapPrivateIndex].ptr) /* ============================================================================= Colormap handling ============================================================================= */ /* * FSInitCmapPrivates * Colormap privates may be allocated after the default colormap has * already been created for some screens. This initialization procedure * is called for each default colormap that is found. */ static Bool FSInitCmapPrivates( ColormapPtr pCmap) { return TRUE; } /* * FSCreateColormap * This is a callback from X after a new colormap is created. * We allocate a new CoreGraphics pallete for each colormap. */ static Bool FSCreateColormap( ColormapPtr pCmap) { CGDirectPaletteRef pallete; // Allocate private storage for the hardware dependent colormap info. if (darwinCmapGeneration != serverGeneration) { if ((darwinCmapPrivateIndex = AllocateColormapPrivateIndex(FSInitCmapPrivates)) < 0) { return FALSE; } darwinCmapGeneration = serverGeneration; } pallete = CGPaletteCreateDefaultColorPalette(); if (!pallete) return FALSE; CMAP_PRIV(pCmap) = pallete; return TRUE; } /* * FSDestroyColormap * This is called by DIX FreeColormap after it has uninstalled a colormap * and notified all interested parties. We deallocated the corresponding * CoreGraphics pallete. */ static void FSDestroyColormap( ColormapPtr pCmap) { CGPaletteRelease( CMAP_PRIV(pCmap) ); } /* * FSInstallColormap * Set the current CoreGraphics pallete to the pallete corresponding * to the provided colormap. */ static void FSInstallColormap( ColormapPtr pCmap) { CGDirectPaletteRef palette = CMAP_PRIV(pCmap); ScreenPtr pScreen = pCmap->pScreen; FSScreenPtr fsDisplayInfo = FULLSCREEN_PRIV(pScreen); // Inform all interested parties that the map is being changed. miInstallColormap(pCmap); if (quartzServerVisible) CGDisplaySetPalette(fsDisplayInfo->displayID, palette); fsDisplayInfo->xPalette = palette; } /* * FSStoreColors * This is a callback from X to change the hardware colormap * when using PsuedoColor in full screen mode. */ static void FSStoreColors( ColormapPtr pCmap, int numEntries, xColorItem *pdefs) { CGDirectPaletteRef palette = CMAP_PRIV(pCmap); ScreenPtr pScreen = pCmap->pScreen; FSScreenPtr fsDisplayInfo = FULLSCREEN_PRIV(pScreen); CGDeviceColor color; int i; if (! palette) return; for (i = 0; i < numEntries; i++) { color.red = pdefs[i].red / 65535.0; color.green = pdefs[i].green / 65535.0; color.blue = pdefs[i].blue / 65535.0; CGPaletteSetColorAtIndex(palette, color, pdefs[i].pixel); } // Update hardware colormap if (quartzServerVisible) CGDisplaySetPalette(fsDisplayInfo->displayID, palette); } /* ============================================================================= Switching between Aqua and X ============================================================================= */ /* * FSCapture * Capture the screen so we can draw. Called directly from the main thread * to synchronize with hiding the menubar. */ static void FSCapture(void) { int i; if (quartzRootless) return; for (i = 0; i < quartzNumScreens; i++) { FSScreenPtr fsDisplayInfo = quartzScreens[i]; CGDirectDisplayID cgID = fsDisplayInfo->displayID; if (!CGDisplayIsCaptured(cgID)) { CGDisplayCapture(cgID); fsDisplayInfo->aquaDisplayMode = CGDisplayCurrentMode(cgID); if (fsDisplayInfo->xDisplayMode != fsDisplayInfo->aquaDisplayMode) CGDisplaySwitchToMode(cgID, fsDisplayInfo->xDisplayMode); if (fsDisplayInfo->xPalette) CGDisplaySetPalette(cgID, fsDisplayInfo->xPalette); } } } /* * FSRelease * Release the screen so others can draw. */ static void FSRelease(void) { int i; if (quartzRootless) return; for (i = 0; i < quartzNumScreens; i++) { FSScreenPtr fsDisplayInfo = quartzScreens[i]; CGDirectDisplayID cgID = fsDisplayInfo->displayID; if (CGDisplayIsCaptured(cgID)) { if (fsDisplayInfo->xDisplayMode != fsDisplayInfo->aquaDisplayMode) CGDisplaySwitchToMode(cgID, fsDisplayInfo->aquaDisplayMode); if (fsDisplayInfo->aquaPalette) CGDisplaySetPalette(cgID, fsDisplayInfo->aquaPalette); CGDisplayRelease(cgID); } } } /* * FSSuspendScreen * Suspend X11 cursor and drawing to the screen. */ static void FSSuspendScreen( ScreenPtr pScreen) { QuartzSuspendXCursor(pScreen); xf86SetRootClip(pScreen, FALSE); } /* * FSResumeScreen * Resume X11 cursor and drawing to the screen. */ static void FSResumeScreen( ScreenPtr pScreen, int x, // cursor location int y ) { QuartzResumeXCursor(pScreen, x, y); xf86SetRootClip(pScreen, TRUE); } /* ============================================================================= Screen initialization ============================================================================= */ /* * FSDisplayInit * Full screen specific initialization called from InitOutput. */ static void FSDisplayInit(void) { static unsigned long generation = 0; CGDisplayCount quartzDisplayCount = 0; ErrorF("Display mode: Full screen Quartz -- Direct Display\n"); // Allocate private storage for each screen's mode specific info if (generation != serverGeneration) { fsScreenIndex = AllocateScreenPrivateIndex(); generation = serverGeneration; } // Find all the CoreGraphics displays CGGetActiveDisplayList(0, NULL, &quartzDisplayCount); quartzDisplayList = xalloc(quartzDisplayCount * sizeof(CGDirectDisplayID)); CGGetActiveDisplayList(quartzDisplayCount, quartzDisplayList, &quartzDisplayCount); darwinScreensFound = quartzDisplayCount; atexit(FSRelease); } /* * FSFindDisplayMode * Find the appropriate display mode to use in full screen mode. * If display mode is not the same as the current Aqua mode, switch * to the new mode. */ static Bool FSFindDisplayMode( FSScreenPtr fsDisplayInfo) { CGDirectDisplayID cgID = fsDisplayInfo->displayID; size_t height, width, bpp; boolean_t exactMatch; fsDisplayInfo->aquaDisplayMode = CGDisplayCurrentMode(cgID); // If no user options, use current display mode if (darwinDesiredWidth == 0 && darwinDesiredDepth == -1 && darwinDesiredRefresh == -1) { fsDisplayInfo->xDisplayMode = fsDisplayInfo->aquaDisplayMode; return TRUE; } // If the user has no choice for size, use current if (darwinDesiredWidth == 0) { width = CGDisplayPixelsWide(cgID); height = CGDisplayPixelsHigh(cgID); } else { width = darwinDesiredWidth; height = darwinDesiredHeight; } switch (darwinDesiredDepth) { case 0: bpp = 8; break; case 1: bpp = 16; break; case 2: bpp = 32; break; default: bpp = CGDisplayBitsPerPixel(cgID); } if (darwinDesiredRefresh == -1) { fsDisplayInfo->xDisplayMode = CGDisplayBestModeForParameters(cgID, bpp, width, height, &exactMatch); } else { fsDisplayInfo->xDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(cgID, bpp, width, height, darwinDesiredRefresh, &exactMatch); } if (!exactMatch) { fsDisplayInfo->xDisplayMode = fsDisplayInfo->aquaDisplayMode; return FALSE; } // Switch to the new display mode CGDisplaySwitchToMode(cgID, fsDisplayInfo->xDisplayMode); return TRUE; } /* * FSAddScreen * Do initialization of each screen for Quartz in full screen mode. */ static Bool FSAddScreen( int index, ScreenPtr pScreen) { DarwinFramebufferPtr dfb = SCREEN_PRIV(pScreen); QuartzScreenPtr displayInfo = QUARTZ_PRIV(pScreen); CGDirectDisplayID cgID = quartzDisplayList[index]; CGRect bounds; FSScreenPtr fsDisplayInfo; // Allocate space for private per screen fullscreen specific storage. fsDisplayInfo = xalloc(sizeof(FSScreenRec)); FULLSCREEN_PRIV(pScreen) = fsDisplayInfo; displayInfo->displayCount = 1; displayInfo->displayIDs = xrealloc(displayInfo->displayIDs, 1 * sizeof(CGDirectDisplayID)); displayInfo->displayIDs[0] = cgID; fsDisplayInfo->displayID = cgID; fsDisplayInfo->xDisplayMode = 0; fsDisplayInfo->aquaDisplayMode = 0; fsDisplayInfo->xPalette = 0; fsDisplayInfo->aquaPalette = 0; // Capture full screen because X doesn't like read-only framebuffer. // We need to do this before we (potentially) switch the display mode. CGDisplayCapture(cgID); if (! FSFindDisplayMode(fsDisplayInfo)) { ErrorF("Could not support specified display mode on screen %i.\n", index); xfree(fsDisplayInfo); return FALSE; } // Don't need to flip y-coordinate as CoreGraphics treats (0, 0) // as the top left of main screen. bounds = CGDisplayBounds(cgID); dfb->x = bounds.origin.x; dfb->y = bounds.origin.y; dfb->width = bounds.size.width; dfb->height = bounds.size.height; dfb->pitch = CGDisplayBytesPerRow(cgID); dfb->bitsPerPixel = CGDisplayBitsPerPixel(cgID); if (dfb->bitsPerPixel == 8) { if (CGDisplayCanSetPalette(cgID)) { dfb->colorType = PseudoColor; } else { dfb->colorType = StaticColor; } dfb->bitsPerComponent = 8; dfb->colorBitsPerPixel = 8; } else { dfb->colorType = TrueColor; dfb->bitsPerComponent = CGDisplayBitsPerSample(cgID); dfb->colorBitsPerPixel = CGDisplaySamplesPerPixel(cgID) * dfb->bitsPerComponent; } fsDisplayInfo->framebuffer = CGDisplayBaseAddress(cgID); // allocate shadow framebuffer fsDisplayInfo->shadowPtr = xalloc(dfb->pitch * dfb->height); dfb->framebuffer = fsDisplayInfo->shadowPtr; return TRUE; } /* * FSShadowUpdate * Update the damaged regions of the shadow framebuffer on the display. */ static void FSShadowUpdate( ScreenPtr pScreen, shadowBufPtr pBuf) { DarwinFramebufferPtr dfb = SCREEN_PRIV(pScreen); FSScreenPtr fsDisplayInfo = FULLSCREEN_PRIV(pScreen); RegionPtr damage = &pBuf->damage; int numBox = REGION_NUM_RECTS(damage); BoxPtr pBox = REGION_RECTS(damage); int pitch = dfb->pitch; int bpp = dfb->bitsPerPixel/8; // Don't update if the X server is not visible if (!quartzServerVisible) return; // Loop through all the damaged boxes while (numBox--) { int width, height, offset; unsigned char *src, *dst; width = (pBox->x2 - pBox->x1) * bpp; height = pBox->y2 - pBox->y1; offset = (pBox->y1 * pitch) + (pBox->x1 * bpp); src = fsDisplayInfo->shadowPtr + offset; dst = fsDisplayInfo->framebuffer + offset; while (height--) { memcpy(dst, src, width); dst += pitch; src += pitch; } // Get the next box pBox++; } } /* * FSSetupScreen * Finalize full screen specific setup of each screen. */ static Bool FSSetupScreen( int index, ScreenPtr pScreen) { DarwinFramebufferPtr dfb = SCREEN_PRIV(pScreen); FSScreenPtr fsDisplayInfo = FULLSCREEN_PRIV(pScreen); CGDirectDisplayID cgID = fsDisplayInfo->displayID; // Initialize shadow framebuffer support if (! shadowInit(pScreen, FSShadowUpdate, NULL)) { ErrorF("Failed to initalize shadow framebuffer for screen %i.\n", index); return FALSE; } if (dfb->colorType == PseudoColor) { // Initialize colormap handling size_t aquaBpp; // If Aqua is using 8 bits we need to keep track of its pallete. CFNumberGetValue(CFDictionaryGetValue(fsDisplayInfo->aquaDisplayMode, kCGDisplayBitsPerPixel), kCFNumberLongType, &aquaBpp); if (aquaBpp <= 8) fsDisplayInfo->aquaPalette = CGPaletteCreateWithDisplay(cgID); pScreen->CreateColormap = FSCreateColormap; pScreen->DestroyColormap = FSDestroyColormap; pScreen->InstallColormap = FSInstallColormap; pScreen->StoreColors = FSStoreColors; } quartzScreens[quartzNumScreens++] = fsDisplayInfo; return TRUE; } /* * Quartz display mode function list. */ static QuartzModeProcsRec fsModeProcs = { FSDisplayInit, FSAddScreen, FSSetupScreen, NULL, // Not needed QuartzInitCursor, QuartzReallySetCursor, FSSuspendScreen, FSResumeScreen, FSCapture, FSRelease, NULL, // No dynamic screen change support NULL, NULL, NULL, // No rootless code in fullscreen NULL, NULL, NULL, NULL, // No support for DRI surfaces NULL }; /* * QuartzModeBundleInit * Initialize the display mode bundle after loading. */ Bool QuartzModeBundleInit(void) { quartzProcs = &fsModeProcs; quartzOpenGLBundle = NULL; // Only Mesa support for now return TRUE; }