- Fix launch of X clients by double clicking in the Finder when there is a space in the path (Torrey T. Lyons). - Interpret scroll wheel mouse events correctly when shift is held down (Benjamin Burke). - Add option to always use Mac command key equivalents (John Harper and Torrey T. Lyons). - Add support for dynamic screen configuration changes in rootless mode (John Harper and Torrey T. Lyons). - Add documentation on generic rootless layer (Torrey T. Lyons).
570 lines
16 KiB
C
570 lines
16 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
/* $XdotOrg: xc/programs/Xserver/hw/darwin/quartz/fullscreen/fullscreen.c,v 1.2 2004/04/23 19:16:21 eich Exp $ */
|
|
/* $XFree86: xc/programs/Xserver/hw/darwin/quartz/fullscreen/fullscreen.c,v 1.3 2003/11/27 01:59:53 torrey Exp $ */
|
|
|
|
#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;
|
|
}
|