diff options
Diffstat (limited to 'hw/darwin/quartz/quartzCursor.c')
-rw-r--r-- | hw/darwin/quartz/quartzCursor.c | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/hw/darwin/quartz/quartzCursor.c b/hw/darwin/quartz/quartzCursor.c new file mode 100644 index 000000000..8abce2318 --- /dev/null +++ b/hw/darwin/quartz/quartzCursor.c @@ -0,0 +1,653 @@ +/************************************************************** + * + * Support for using the Quartz Window Manager cursor + * + **************************************************************/ +/* + * Copyright (c) 2001-2003 Torrey T. Lyons and Greg Parker. + * 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 + * THE ABOVE LISTED COPYRIGHT HOLDER(S) 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. + */ +/* $XFree86: xc/programs/Xserver/hw/darwin/quartz/quartzCursor.c,v 1.5 2003/01/23 00:34:26 torrey Exp $ */ + +#include "quartzCommon.h" +#include "quartzCursor.h" +#include "darwin.h" + +#include <pthread.h> + +#include "mi.h" +#include "scrnintstr.h" +#include "cursorstr.h" +#include "mipointrst.h" +#include "globals.h" + +// Size of the QuickDraw cursor +#define CURSORWIDTH 16 +#define CURSORHEIGHT 16 + +typedef struct { + int qdCursorMode; + int qdCursorVisible; + int useQDCursor; + QueryBestSizeProcPtr QueryBestSize; + miPointerSpriteFuncPtr spriteFuncs; +} QuartzCursorScreenRec, *QuartzCursorScreenPtr; + +static int darwinCursorScreenIndex = -1; +static unsigned long darwinCursorGeneration = 0; +static CursorPtr quartzLatentCursor = NULL; +static QD_Cursor gQDArrow; // QuickDraw arrow cursor + +// Cursor for the main thread to set (NULL = arrow cursor). +static CCrsrHandle currentCursor = NULL; +static pthread_mutex_t cursorMutex; +static pthread_cond_t cursorCondition; + +#define CURSOR_PRIV(pScreen) \ + ((QuartzCursorScreenPtr)pScreen->devPrivates[darwinCursorScreenIndex].ptr) + +#define HIDE_QD_CURSOR(pScreen, visible) \ + if (visible) { \ + int ix; \ + for (ix = 0; ix < QUARTZ_PRIV(pScreen)->displayCount; ix++) { \ + CGDisplayHideCursor(QUARTZ_PRIV(pScreen)->displayIDs[ix]); \ + } \ + visible = FALSE; \ + } ((void)0) + +#define SHOW_QD_CURSOR(pScreen, visible) \ + { \ + int ix; \ + for (ix = 0; ix < QUARTZ_PRIV(pScreen)->displayCount; ix++) { \ + CGDisplayShowCursor(QUARTZ_PRIV(pScreen)->displayIDs[ix]); \ + } \ + visible = TRUE; \ + } ((void)0) + +#define CHANGE_QD_CURSOR(cursorH) \ + if (!quartzServerQuitting) { \ + /* Acquire lock and tell the main thread to change cursor */ \ + pthread_mutex_lock(&cursorMutex); \ + currentCursor = (CCrsrHandle) (cursorH); \ + QuartzMessageMainThread(kQuartzCursorUpdate, NULL, 0); \ + \ + /* Wait for the main thread to change the cursor */ \ + pthread_cond_wait(&cursorCondition, &cursorMutex); \ + pthread_mutex_unlock(&cursorMutex); \ + } ((void)0) + + +/* + * MakeQDCursor helpers: CTAB_ENTER, interleave + */ + +// Add a color entry to a ctab +#define CTAB_ENTER(ctab, index, r, g, b) \ + ctab->ctTable[index].value = index; \ + ctab->ctTable[index].rgb.red = r; \ + ctab->ctTable[index].rgb.green = g; \ + ctab->ctTable[index].rgb.blue = b + +// Make an unsigned short by interleaving the bits of bytes c1 and c2. +// High bit of c1 is first; low bit of c2 is last. +// Interleave is a built-in INTERCAL operator. +static unsigned short +interleave( + unsigned char c1, + unsigned char c2 ) +{ + return + ((c1 & 0x80) << 8) | ((c2 & 0x80) << 7) | + ((c1 & 0x40) << 7) | ((c2 & 0x40) << 6) | + ((c1 & 0x20) << 6) | ((c2 & 0x20) << 5) | + ((c1 & 0x10) << 5) | ((c2 & 0x10) << 4) | + ((c1 & 0x08) << 4) | ((c2 & 0x08) << 3) | + ((c1 & 0x04) << 3) | ((c2 & 0x04) << 2) | + ((c1 & 0x02) << 2) | ((c2 & 0x02) << 1) | + ((c1 & 0x01) << 1) | ((c2 & 0x01) << 0) ; +} + +/* + * MakeQDCursor + * Make a QuickDraw color cursor from the given X11 cursor. + * Warning: This code is nasty. Color cursors were meant to be read + * from resources; constructing the structures programmatically is messy. + */ +/* + QuickDraw cursor representation: + Our color cursor is a 2 bit per pixel pixmap. + Each pixel's bits are (source<<1 | mask) from the original X cursor pixel. + The cursor's color table maps the colors like this: + (2-bit value | X result | colortable | Mac result) + 00 | transparent | white | transparent (white outside mask) + 01 | back color | back color | back color + 10 | undefined | black | invert background (just for fun) + 11 | fore color | fore color | fore color +*/ +static CCrsrHandle +MakeQDCursor( + CursorPtr pCursor ) +{ + CCrsrHandle result; + CCrsrPtr curs; + int i, w, h; + unsigned short rowMask; + PixMap *pix; + ColorTable *ctab; + unsigned short *image; + + result = (CCrsrHandle) NewHandleClear(sizeof(CCrsr)); + if (!result) return NULL; + HLock((Handle)result); + curs = *result; + + // Initialize CCrsr + curs->crsrType = 0x8001; // 0x8000 = b&w, 0x8001 = color + curs->crsrMap = (PixMapHandle) NewHandleClear(sizeof(PixMap)); + if (!curs->crsrMap) goto pixAllocFailed; + HLock((Handle)curs->crsrMap); + pix = *curs->crsrMap; + curs->crsrData = NULL; // raw cursor image data (set below) + curs->crsrXData = NULL; // QD's processed data + curs->crsrXValid = 0; // zero means QD must re-process cursor data + curs->crsrXHandle = NULL; // reserved + memset(curs->crsr1Data, 0, CURSORWIDTH*CURSORHEIGHT/8); // b&w data + memset(curs->crsrMask, 0, CURSORWIDTH*CURSORHEIGHT/8); // b&w & color mask + curs->crsrHotSpot.h = min(CURSORWIDTH, pCursor->bits->xhot); // hot spot + curs->crsrHotSpot.v = min(CURSORHEIGHT, pCursor->bits->yhot); // hot spot + curs->crsrXTable = 0; // reserved + curs->crsrID = GetCTSeed(); // unique ID from Color Manager + + // Set the b&w data and mask + w = min(pCursor->bits->width, CURSORWIDTH); + h = min(pCursor->bits->height, CURSORHEIGHT); + rowMask = ~((1 << (CURSORWIDTH - w)) - 1); + for (i = 0; i < h; i++) { + curs->crsr1Data[i] = rowMask & + ((pCursor->bits->source[i*4]<<8) | pCursor->bits->source[i*4+1]); + curs->crsrMask[i] = rowMask & + ((pCursor->bits->mask[i*4]<<8) | pCursor->bits->mask[i*4+1]); + } + + // Set the color data and mask + // crsrMap: defines bit depth and size and colortable only + pix->rowBytes = (CURSORWIDTH * 2 / 8) | 0x8000; // last bit on means PixMap + SetRect(&pix->bounds, 0, 0, CURSORWIDTH, CURSORHEIGHT); // see TN 1020 + pix->pixelSize = 2; + pix->cmpCount = 1; + pix->cmpSize = 2; + // pix->pmTable set below + + // crsrData is the pixel data. crsrMap's baseAddr is not used. + curs->crsrData = NewHandleClear(CURSORWIDTH*CURSORHEIGHT * 2 / 8); + if (!curs->crsrData) goto imageAllocFailed; + HLock((Handle)curs->crsrData); + image = (unsigned short *) *curs->crsrData; + // Pixel data is just 1-bit data and mask interleaved (see above) + for (i = 0; i < h; i++) { + unsigned char s, m; + s = pCursor->bits->source[i*4] & (rowMask >> 8); + m = pCursor->bits->mask[i*4] & (rowMask >> 8); + image[2*i] = interleave(s, m); + s = pCursor->bits->source[i*4+1] & (rowMask & 0x00ff); + m = pCursor->bits->mask[i*4+1] & (rowMask & 0x00ff); + image[2*i+1] = interleave(s, m); + } + + // Build the color table (entries described above) + // NewPixMap allocates a color table handle. + pix->pmTable = (CTabHandle) NewHandleClear(sizeof(ColorTable) + 3 + * sizeof(ColorSpec)); + if (!pix->pmTable) goto ctabAllocFailed; + HLock((Handle)pix->pmTable); + ctab = *pix->pmTable; + ctab->ctSeed = GetCTSeed(); + ctab->ctFlags = 0; + ctab->ctSize = 3; // color count - 1 + CTAB_ENTER(ctab, 0, 0xffff, 0xffff, 0xffff); + CTAB_ENTER(ctab, 1, pCursor->backRed, pCursor->backGreen, + pCursor->backBlue); + CTAB_ENTER(ctab, 2, 0x0000, 0x0000, 0x0000); + CTAB_ENTER(ctab, 3, pCursor->foreRed, pCursor->foreGreen, + pCursor->foreBlue); + + HUnlock((Handle)pix->pmTable); // ctab + HUnlock((Handle)curs->crsrData); // image data + HUnlock((Handle)curs->crsrMap); // pix + HUnlock((Handle)result); // cursor + + return result; + + // "What we have here is a failure to allocate" +ctabAllocFailed: + HUnlock((Handle)curs->crsrData); + DisposeHandle((Handle)curs->crsrData); +imageAllocFailed: + HUnlock((Handle)curs->crsrMap); + DisposeHandle((Handle)curs->crsrMap); +pixAllocFailed: + HUnlock((Handle)result); + DisposeHandle((Handle)result); + return NULL; +} + + +/* + * FreeQDCursor + * Destroy a QuickDraw color cursor created with MakeQDCursor(). + * The cursor must not currently be on screen. + */ +static void FreeQDCursor(CCrsrHandle cursHandle) +{ + CCrsrPtr curs; + PixMap *pix; + + HLock((Handle)cursHandle); + curs = *cursHandle; + HLock((Handle)curs->crsrMap); + pix = *curs->crsrMap; + DisposeHandle((Handle)pix->pmTable); + HUnlock((Handle)curs->crsrMap); + DisposeHandle((Handle)curs->crsrMap); + DisposeHandle((Handle)curs->crsrData); + HUnlock((Handle)cursHandle); + DisposeHandle((Handle)cursHandle); +} + + +/* +=========================================================================== + + Pointer sprite functions + +=========================================================================== +*/ + +/* + * QuartzRealizeCursor + * Convert the X cursor representation to QuickDraw format if possible. + */ +Bool +QuartzRealizeCursor( + ScreenPtr pScreen, + CursorPtr pCursor ) +{ + CCrsrHandle qdCursor; + QuartzCursorScreenPtr ScreenPriv = CURSOR_PRIV(pScreen); + + if(!pCursor || !pCursor->bits) + return FALSE; + + // if the cursor is too big we use a software cursor + if ((pCursor->bits->height > CURSORHEIGHT) || + (pCursor->bits->width > CURSORWIDTH) || !ScreenPriv->useQDCursor) + { + if (quartzRootless) { + // rootless can't use a software cursor + return TRUE; + } else { + return (*ScreenPriv->spriteFuncs->RealizeCursor) + (pScreen, pCursor); + } + } + + // make new cursor image + qdCursor = MakeQDCursor(pCursor); + if (!qdCursor) return FALSE; + + // save the result + pCursor->devPriv[pScreen->myNum] = (pointer) qdCursor; + + return TRUE; +} + + +/* + * QuartzUnrealizeCursor + * Free the storage space associated with a realized cursor. + */ +Bool +QuartzUnrealizeCursor( + ScreenPtr pScreen, + CursorPtr pCursor ) +{ + QuartzCursorScreenPtr ScreenPriv = CURSOR_PRIV(pScreen); + + if ((pCursor->bits->height > CURSORHEIGHT) || + (pCursor->bits->width > CURSORWIDTH) || !ScreenPriv->useQDCursor) + { + if (quartzRootless) { + return TRUE; + } else { + return (*ScreenPriv->spriteFuncs->UnrealizeCursor) + (pScreen, pCursor); + } + } else { + CCrsrHandle oldCursor = (CCrsrHandle) pCursor->devPriv[pScreen->myNum]; + + if (currentCursor != oldCursor) { + // This should only fail when quitting, in which case we just leak. + FreeQDCursor(oldCursor); + } + pCursor->devPriv[pScreen->myNum] = NULL; + return TRUE; + } +} + + +/* + * QuartzSetCursor + * Set the cursor sprite and position. + * Use QuickDraw cursor if possible. + */ +static void +QuartzSetCursor( + ScreenPtr pScreen, + CursorPtr pCursor, + int x, + int y) +{ + QuartzCursorScreenPtr ScreenPriv = CURSOR_PRIV(pScreen); + + quartzLatentCursor = pCursor; + + // Don't touch Mac OS cursor if X is hidden! + if (!quartzServerVisible) + return; + + if (!pCursor) { + // Remove the cursor completely. + HIDE_QD_CURSOR(pScreen, ScreenPriv->qdCursorVisible); + if (! ScreenPriv->qdCursorMode) + (*ScreenPriv->spriteFuncs->SetCursor)(pScreen, 0, x, y); + } + else if ((pCursor->bits->height <= CURSORHEIGHT) && + (pCursor->bits->width <= CURSORWIDTH) && ScreenPriv->useQDCursor) + { + // Cursor is small enough to use QuickDraw directly. + if (! ScreenPriv->qdCursorMode) // remove the X cursor + (*ScreenPriv->spriteFuncs->SetCursor)(pScreen, 0, x, y); + ScreenPriv->qdCursorMode = TRUE; + + CHANGE_QD_CURSOR(pCursor->devPriv[pScreen->myNum]); + SHOW_QD_CURSOR(pScreen, ScreenPriv->qdCursorVisible); + } + else if (quartzRootless) { + // Rootless can't use a software cursor, so we just use Mac OS arrow. + CHANGE_QD_CURSOR(NULL); + SHOW_QD_CURSOR(pScreen, ScreenPriv->qdCursorVisible); + } + else { + // Cursor is too big for QuickDraw. Use X software cursor. + HIDE_QD_CURSOR(pScreen, ScreenPriv->qdCursorVisible); + ScreenPriv->qdCursorMode = FALSE; + (*ScreenPriv->spriteFuncs->SetCursor)(pScreen, pCursor, x, y); + } +} + + +/* + * QuartzReallySetCursor + * Set the QuickDraw cursor. Called from the main thread since changing the + * cursor with QuickDraw is not thread safe on dual processor machines. + */ +void +QuartzReallySetCursor() +{ + pthread_mutex_lock(&cursorMutex); + + if (currentCursor) { + SetCCursor(currentCursor); + } else { + SetCursor(&gQDArrow); + } + + pthread_cond_signal(&cursorCondition); + pthread_mutex_unlock(&cursorMutex); +} + + +/* + * QuartzMoveCursor + * Move the cursor. This is a noop for QuickDraw. + */ +static void +QuartzMoveCursor( + ScreenPtr pScreen, + int x, + int y) +{ + QuartzCursorScreenPtr ScreenPriv = CURSOR_PRIV(pScreen); + + // only the X cursor needs to be explicitly moved + if (!ScreenPriv->qdCursorMode) + (*ScreenPriv->spriteFuncs->MoveCursor)(pScreen, x, y); +} + + +static miPointerSpriteFuncRec quartzSpriteFuncsRec = { + QuartzRealizeCursor, + QuartzUnrealizeCursor, + QuartzSetCursor, + QuartzMoveCursor +}; + + +/* +=========================================================================== + + Pointer screen functions + +=========================================================================== +*/ + +/* + * QuartzCursorOffScreen + */ +static Bool QuartzCursorOffScreen(ScreenPtr *pScreen, int *x, int *y) +{ + return FALSE; +} + + +/* + * QuartzCrossScreen + */ +static void QuartzCrossScreen(ScreenPtr pScreen, Bool entering) +{ + return; +} + + +/* + * QuartzWarpCursor + * Change the cursor position without generating an event or motion history. + * The input coordinates (x,y) are in pScreen-local X11 coordinates. + * + */ +static void +QuartzWarpCursor( + ScreenPtr pScreen, + int x, + int y) +{ + static int neverMoved = TRUE; + + if (neverMoved) { + // Don't move the cursor the first time. This is the jump-to-center + // initialization, and it's annoying because we may still be in MacOS. + neverMoved = FALSE; + return; + } + + if (quartzServerVisible) { + CGDisplayErr cgErr; + CGPoint cgPoint; + // Only need to do this for one display. Any display will do. + CGDirectDisplayID cgID = QUARTZ_PRIV(pScreen)->displayIDs[0]; + CGRect cgRect = CGDisplayBounds(cgID); + + // Convert (x,y) to CoreGraphics screen-local CG coordinates. + // This is necessary because the X11 screen and CG screen may not + // coincide. (e.g. X11 screen may be moved to dodge the menu bar) + + // Make point in X11 global coordinates + cgPoint = CGPointMake(x + dixScreenOrigins[pScreen->myNum].x, + y + dixScreenOrigins[pScreen->myNum].y); + // Shift to CoreGraphics global screen coordinates + cgPoint.x += darwinMainScreenX; + cgPoint.y += darwinMainScreenY; + // Shift to CoreGraphics screen-local coordinates + cgPoint.x -= cgRect.origin.x; + cgPoint.y -= cgRect.origin.y; + + cgErr = CGDisplayMoveCursorToPoint(cgID, cgPoint); + if (cgErr != CGDisplayNoErr) { + ErrorF("Could not set cursor position with error code 0x%x.\n", + cgErr); + } + } + + miPointerWarpCursor(pScreen, x, y); + miPointerUpdate(); +} + + +static miPointerScreenFuncRec quartzScreenFuncsRec = { + QuartzCursorOffScreen, + QuartzCrossScreen, + QuartzWarpCursor, + DarwinEQPointerPost, + DarwinEQSwitchScreen +}; + + +/* +=========================================================================== + + Other screen functions + +=========================================================================== +*/ + +/* + * QuartzCursorQueryBestSize + * Handle queries for best cursor size + */ +static void +QuartzCursorQueryBestSize( + int class, + unsigned short *width, + unsigned short *height, + ScreenPtr pScreen) +{ + QuartzCursorScreenPtr ScreenPriv = CURSOR_PRIV(pScreen); + + if (class == CursorShape) { + *width = CURSORWIDTH; + *height = CURSORHEIGHT; + } else { + (*ScreenPriv->QueryBestSize)(class, width, height, pScreen); + } +} + + +/* + * QuartzInitCursor + * Initialize cursor support + */ +Bool +QuartzInitCursor( + ScreenPtr pScreen ) +{ + QuartzCursorScreenPtr ScreenPriv; + miPointerScreenPtr PointPriv; + DarwinFramebufferPtr dfb = SCREEN_PRIV(pScreen); + + // initialize software cursor handling (always needed as backup) + if (!miDCInitialize(pScreen, &quartzScreenFuncsRec)) { + return FALSE; + } + + // allocate private storage for this screen's QuickDraw cursor info + if (darwinCursorGeneration != serverGeneration) { + if ((darwinCursorScreenIndex = AllocateScreenPrivateIndex()) < 0) + return FALSE; + darwinCursorGeneration = serverGeneration; + } + + ScreenPriv = xcalloc( 1, sizeof(QuartzCursorScreenRec) ); + if (!ScreenPriv) return FALSE; + + CURSOR_PRIV(pScreen) = ScreenPriv; + + // override some screen procedures + ScreenPriv->QueryBestSize = pScreen->QueryBestSize; + pScreen->QueryBestSize = QuartzCursorQueryBestSize; + + // initialize QuickDraw cursor handling + GetQDGlobalsArrow(&gQDArrow); + PointPriv = (miPointerScreenPtr) + pScreen->devPrivates[miPointerScreenIndex].ptr; + + ScreenPriv->spriteFuncs = PointPriv->spriteFuncs; + PointPriv->spriteFuncs = &quartzSpriteFuncsRec; + + if (!quartzRootless) + ScreenPriv->useQDCursor = QuartzFSUseQDCursor(dfb->colorBitsPerPixel); + else + ScreenPriv->useQDCursor = TRUE; + ScreenPriv->qdCursorMode = TRUE; + ScreenPriv->qdCursorVisible = TRUE; + + // initialize cursor mutex lock + pthread_mutex_init(&cursorMutex, NULL); + + // initialize condition for waiting + pthread_cond_init(&cursorCondition, NULL); + + return TRUE; +} + + +// X server is hiding. Restore the Aqua cursor. +void QuartzSuspendXCursor( + ScreenPtr pScreen ) +{ + QuartzCursorScreenPtr ScreenPriv = CURSOR_PRIV(pScreen); + + CHANGE_QD_CURSOR(NULL); + SHOW_QD_CURSOR(pScreen, ScreenPriv->qdCursorVisible); +} + + +// X server is showing. Restore the X cursor. +void QuartzResumeXCursor( + ScreenPtr pScreen, + int x, + int y ) +{ + QuartzSetCursor(pScreen, quartzLatentCursor, x, y); +} |