diff options
Diffstat (limited to 'xorg-server/hw/xwin/wincursor.c')
-rw-r--r-- | xorg-server/hw/xwin/wincursor.c | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/xorg-server/hw/xwin/wincursor.c b/xorg-server/hw/xwin/wincursor.c new file mode 100644 index 000000000..021b8b82c --- /dev/null +++ b/xorg-server/hw/xwin/wincursor.c @@ -0,0 +1,614 @@ +/* + *Copyright (C) 1994-2000 The XFree86 Project, Inc. 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 XFREE86 PROJECT 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 of the XFree86 Project + *shall not be used in advertising or otherwise to promote the sale, use + *or other dealings in this Software without prior written authorization + *from the XFree86 Project. + * + * Authors: Dakshinamurthy Karra + * Suhaib M Siddiqi + * Peter Busch + * Harold L Hunt II + */ + +#ifdef HAVE_XWIN_CONFIG_H +#include <xwin-config.h> +#endif +#include "win.h" +#include "winmsg.h" +#include <cursorstr.h> +#include <mipointrst.h> +#include <servermd.h> + +extern Bool g_fSoftwareCursor; + + +#ifndef MIN +#define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#define BYTE_COUNT(x) (((x) + 7) / 8) + +#define BRIGHTNESS(x) (x##Red * 0.299 + x##Green * 0.587 + x##Blue * 0.114) + +#if 0 +# define WIN_DEBUG_MSG winDebug +#else +# define WIN_DEBUG_MSG(...) +#endif + +/* + * Local function prototypes + */ + +static void +winPointerWarpCursor (ScreenPtr pScreen, int x, int y); + +static Bool +winCursorOffScreen (ScreenPtr *ppScreen, int *x, int *y); + +static void +winCrossScreen (ScreenPtr pScreen, Bool fEntering); + +miPointerScreenFuncRec g_winPointerCursorFuncs = +{ + winCursorOffScreen, + winCrossScreen, + winPointerWarpCursor +}; + + +static void +winPointerWarpCursor (ScreenPtr pScreen, int x, int y) +{ + winScreenPriv(pScreen); + RECT rcClient; + static Bool s_fInitialWarp = TRUE; + + /* Discard first warp call */ + if (s_fInitialWarp) + { + /* First warp moves mouse to center of window, just ignore it */ + + /* Don't ignore subsequent warps */ + s_fInitialWarp = FALSE; + + winErrorFVerb (2, "winPointerWarpCursor - Discarding first warp: %d %d\n", + x, y); + + return; + } + + /* Only update the Windows cursor position if we are active */ + if (pScreenPriv->hwndScreen == GetForegroundWindow ()) + { + /* Get the client area coordinates */ + GetClientRect (pScreenPriv->hwndScreen, &rcClient); + + /* Translate the client area coords to screen coords */ + MapWindowPoints (pScreenPriv->hwndScreen, + HWND_DESKTOP, + (LPPOINT)&rcClient, + 2); + + /* + * Update the Windows cursor position so that we don't + * immediately warp back to the current position. + */ + SetCursorPos (rcClient.left + x, rcClient.top + y); + } + + /* Call the mi warp procedure to do the actual warping in X. */ + miPointerWarpCursor (pScreen, x, y); +} + +static Bool +winCursorOffScreen (ScreenPtr *ppScreen, int *x, int *y) +{ + return FALSE; +} + +static void +winCrossScreen (ScreenPtr pScreen, Bool fEntering) +{ +} + +static unsigned char +reverse(unsigned char c) +{ + int i; + unsigned char ret = 0; + for (i = 0; i < 8; ++i) + { + ret |= ((c >> i)&1) << (7 - i); + } + return ret; +} + +/* + * Convert X cursor to Windows cursor + * FIXME: Perhaps there are more smart code + */ +static HCURSOR +winLoadCursor (ScreenPtr pScreen, CursorPtr pCursor, int screen) +{ + winScreenPriv(pScreen); + HCURSOR hCursor = NULL; + unsigned char *pAnd; + unsigned char *pXor; + int nCX, nCY; + int nBytes; + double dForeY, dBackY; + BOOL fReverse; + HBITMAP hAnd, hXor; + ICONINFO ii; + unsigned char *pCur; + int x, y; + unsigned char bit; + HDC hDC; + BITMAPV4HEADER bi; + BITMAPINFO *pbmi; + unsigned long *lpBits; + + WIN_DEBUG_MSG("winLoadCursor: Win32: %dx%d X11: %dx%d hotspot: %d,%d\n", + pScreenPriv->cursor.sm_cx, pScreenPriv->cursor.sm_cy, + pCursor->bits->width, pCursor->bits->height, + pCursor->bits->xhot, pCursor->bits->yhot + ); + + /* We can use only White and Black, so calc brightness of color + * Also check if the cursor is inverted */ + dForeY = BRIGHTNESS(pCursor->fore); + dBackY = BRIGHTNESS(pCursor->back); + fReverse = dForeY < dBackY; + + /* Check wether the X11 cursor is bigger than the win32 cursor */ + if (pScreenPriv->cursor.sm_cx < pCursor->bits->width || + pScreenPriv->cursor.sm_cy < pCursor->bits->height) + { + winErrorFVerb (2, "winLoadCursor - Windows requires %dx%d cursor\n" + "\tbut X requires %dx%d\n", + pScreenPriv->cursor.sm_cx, pScreenPriv->cursor.sm_cy, + pCursor->bits->width, pCursor->bits->height); + } + + /* Get the number of bytes required to store the whole cursor image + * This is roughly (sm_cx * sm_cy) / 8 + * round up to 8 pixel boundary so we can convert whole bytes */ + nBytes = BYTE_COUNT(pScreenPriv->cursor.sm_cx) * pScreenPriv->cursor.sm_cy; + + /* Get the effective width and height */ + nCX = MIN(pScreenPriv->cursor.sm_cx, pCursor->bits->width); + nCY = MIN(pScreenPriv->cursor.sm_cy, pCursor->bits->height); + + /* Allocate memory for the bitmaps */ + pAnd = malloc (nBytes); + memset (pAnd, 0xFF, nBytes); + pXor = malloc (nBytes); + memset (pXor, 0x00, nBytes); + + /* Convert the X11 bitmap to a win32 bitmap + * The first is for an empty mask */ + if (pCursor->bits->emptyMask) + { + int x, y, xmax = BYTE_COUNT(nCX); + for (y = 0; y < nCY; ++y) + for (x = 0; x < xmax; ++x) + { + int nWinPix = BYTE_COUNT(pScreenPriv->cursor.sm_cx) * y + x; + int nXPix = BitmapBytePad(pCursor->bits->width) * y + x; + + pAnd[nWinPix] = 0; + if (fReverse) + pXor[nWinPix] = reverse (~pCursor->bits->source[nXPix]); + else + pXor[nWinPix] = reverse (pCursor->bits->source[nXPix]); + } + } + else + { + int x, y, xmax = BYTE_COUNT(nCX); + for (y = 0; y < nCY; ++y) + for (x = 0; x < xmax; ++x) + { + int nWinPix = BYTE_COUNT(pScreenPriv->cursor.sm_cx) * y + x; + int nXPix = BitmapBytePad(pCursor->bits->width) * y + x; + + unsigned char mask = pCursor->bits->mask[nXPix]; + pAnd[nWinPix] = reverse (~mask); + if (fReverse) + pXor[nWinPix] = reverse (~pCursor->bits->source[nXPix] & mask); + else + pXor[nWinPix] = reverse (pCursor->bits->source[nXPix] & mask); + } + } + + /* prepare the pointers */ + hCursor = NULL; + lpBits = NULL; + + /* We have a truecolor alpha-blended cursor and can use it! */ + if (pCursor->bits->argb) + { + WIN_DEBUG_MSG("winLoadCursor: Trying truecolor alphablended cursor\n"); + memset (&bi, 0, sizeof (BITMAPV4HEADER)); + bi.bV4Size = sizeof(BITMAPV4HEADER); + bi.bV4Width = pScreenPriv->cursor.sm_cx; + bi.bV4Height = -(pScreenPriv->cursor.sm_cy); /* right-side up */ + bi.bV4Planes = 1; + bi.bV4BitCount = 32; + bi.bV4V4Compression = BI_BITFIELDS; + bi.bV4RedMask = 0x00FF0000; + bi.bV4GreenMask = 0x0000FF00; + bi.bV4BlueMask = 0x000000FF; + bi.bV4AlphaMask = 0xFF000000; + + lpBits = (unsigned long *) calloc (pScreenPriv->cursor.sm_cx*pScreenPriv->cursor.sm_cy, + sizeof (unsigned long)); + + if (lpBits) + { + for (y=0; y<nCY; y++) + { + unsigned long *src, *dst; + src = &(pCursor->bits->argb[y * pCursor->bits->width]); + dst = &(lpBits[y * pScreenPriv->cursor.sm_cx]); + memcpy (dst, src, 4*nCX); + } + } + } /* End if-truecolor-icon */ + + if (!lpBits) + { + /* Bicolor, use a palettized DIB */ + WIN_DEBUG_MSG("winLoadCursor: Trying two color cursor\n"); + pbmi = (BITMAPINFO*)&bi; + memset (pbmi, 0, sizeof (BITMAPINFOHEADER)); + pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = pScreenPriv->cursor.sm_cx; + pbmi->bmiHeader.biHeight = -abs(pScreenPriv->cursor.sm_cy); /* right-side up */ + pbmi->bmiHeader.biPlanes = 1; + pbmi->bmiHeader.biBitCount = 8; + pbmi->bmiHeader.biCompression = BI_RGB; + pbmi->bmiHeader.biSizeImage = 0; + pbmi->bmiHeader.biClrUsed = 3; + pbmi->bmiHeader.biClrImportant = 3; + pbmi->bmiColors[0].rgbRed = 0; /* Empty */ + pbmi->bmiColors[0].rgbGreen = 0; + pbmi->bmiColors[0].rgbBlue = 0; + pbmi->bmiColors[0].rgbReserved = 0; + pbmi->bmiColors[1].rgbRed = pCursor->backRed>>8; /* Background */ + pbmi->bmiColors[1].rgbGreen = pCursor->backGreen>>8; + pbmi->bmiColors[1].rgbBlue = pCursor->backBlue>>8; + pbmi->bmiColors[1].rgbReserved = 0; + pbmi->bmiColors[2].rgbRed = pCursor->foreRed>>8; /* Foreground */ + pbmi->bmiColors[2].rgbGreen = pCursor->foreGreen>>8; + pbmi->bmiColors[2].rgbBlue = pCursor->foreBlue>>8; + pbmi->bmiColors[2].rgbReserved = 0; + + lpBits = (unsigned long *) calloc (pScreenPriv->cursor.sm_cx*pScreenPriv->cursor.sm_cy, + sizeof (char)); + + pCur = (unsigned char *)lpBits; + if (lpBits) + { + for (y=0; y<pScreenPriv->cursor.sm_cy; y++) + { + for (x=0; x<pScreenPriv->cursor.sm_cx; x++) + { + if (x>=nCX || y>=nCY) /* Outside of X11 icon bounds */ + (*pCur++) = 0; + else /* Within X11 icon bounds */ + { + int nWinPix = BYTE_COUNT(pScreenPriv->cursor.sm_cx) * y + (x/8); + + bit = pAnd[nWinPix]; + bit = bit & (1<<(7-(x&7))); + if (!bit) /* Within the cursor mask? */ + { + int nXPix = BitmapBytePad(pCursor->bits->width) * y + (x/8); + bit = ~reverse(~pCursor->bits->source[nXPix] & pCursor->bits->mask[nXPix]); + bit = bit & (1<<(7-(x&7))); + if (bit) /* Draw foreground */ + (*pCur++) = 2; + else /* Draw background */ + (*pCur++) = 1; + } + else /* Outside the cursor mask */ + (*pCur++) = 0; + } + } /* end for (x) */ + } /* end for (y) */ + } /* end if (lpbits) */ + } + + /* If one of the previous two methods gave us the bitmap we need, make a cursor */ + if (lpBits) + { + WIN_DEBUG_MSG("winLoadCursor: Creating bitmap cursor: hotspot %d,%d\n", + pCursor->bits->xhot, pCursor->bits->yhot); + + hAnd = NULL; + hXor = NULL; + + hAnd = CreateBitmap (pScreenPriv->cursor.sm_cx, pScreenPriv->cursor.sm_cy, 1, 1, pAnd); + + hDC = GetDC (NULL); + if (hDC) + { + hXor = CreateCompatibleBitmap (hDC, pScreenPriv->cursor.sm_cx, pScreenPriv->cursor.sm_cy); + SetDIBits (hDC, hXor, 0, pScreenPriv->cursor.sm_cy, lpBits, (BITMAPINFO*)&bi, DIB_RGB_COLORS); + ReleaseDC (NULL, hDC); + } + free (lpBits); + + + if (hAnd && hXor) + { + ii.fIcon = FALSE; + ii.xHotspot = pCursor->bits->xhot; + ii.yHotspot = pCursor->bits->yhot; + ii.hbmMask = hAnd; + ii.hbmColor = hXor; + hCursor = (HCURSOR) CreateIconIndirect( &ii ); + + if (hCursor == NULL) + winW32Error(2, "winLoadCursor - CreateIconIndirect failed:"); + else + { + if (GetIconInfo(hCursor, &ii)) + { + if (ii.fIcon) + { + WIN_DEBUG_MSG("winLoadCursor: CreateIconIndirect returned no cursor. Trying again.\n"); + + DestroyCursor(hCursor); + + ii.fIcon = FALSE; + ii.xHotspot = pCursor->bits->xhot; + ii.yHotspot = pCursor->bits->yhot; + hCursor = (HCURSOR) CreateIconIndirect( &ii ); + + if (hCursor == NULL) + winW32Error(2, "winLoadCursor - CreateIconIndirect failed:"); + } + /* GetIconInfo creates new bitmaps. Destroy them again */ + if (ii.hbmMask) + DeleteObject(ii.hbmMask); + if (ii.hbmColor) + DeleteObject(ii.hbmColor); + } + } + } + + if (hAnd) + DeleteObject (hAnd); + if (hXor) + DeleteObject (hXor); + } + + if (!hCursor) + { + /* We couldn't make a color cursor for this screen, use + black and white instead */ + hCursor = CreateCursor (g_hInstance, + pCursor->bits->xhot, pCursor->bits->yhot, + pScreenPriv->cursor.sm_cx, pScreenPriv->cursor.sm_cy, + pAnd, pXor); + if (hCursor == NULL) + winW32Error(2, "winLoadCursor - CreateCursor failed:"); + } + free (pAnd); + free (pXor); + + return hCursor; +} + +/* +=========================================================================== + + Pointer sprite functions + +=========================================================================== +*/ + +/* + * winRealizeCursor + * Convert the X cursor representation to native format if possible. + */ +static Bool +winRealizeCursor (ScreenPtr pScreen, CursorPtr pCursor) +{ + if(pCursor == NULL || pCursor->bits == NULL) + return FALSE; + + /* FIXME: cache ARGB8888 representation? */ + + return TRUE; +} + + +/* + * winUnrealizeCursor + * Free the storage space associated with a realized cursor. + */ +static Bool +winUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) +{ + return TRUE; +} + + +/* + * winSetCursor + * Set the cursor sprite and position. + */ +static void +winSetCursor (ScreenPtr pScreen, CursorPtr pCursor, int x, int y) +{ + POINT ptCurPos, ptTemp; + HWND hwnd; + RECT rcClient; + BOOL bInhibit; + winScreenPriv(pScreen); + WIN_DEBUG_MSG("winSetCursor: cursor=%p\n", pCursor); + + /* Inhibit changing the cursor if the mouse is not in a client area */ + bInhibit = FALSE; + if (GetCursorPos (&ptCurPos)) + { + hwnd = WindowFromPoint (ptCurPos); + if (hwnd) + { + if (GetClientRect (hwnd, &rcClient)) + { + ptTemp.x = rcClient.left; + ptTemp.y = rcClient.top; + if (ClientToScreen (hwnd, &ptTemp)) + { + rcClient.left = ptTemp.x; + rcClient.top = ptTemp.y; + ptTemp.x = rcClient.right; + ptTemp.y = rcClient.bottom; + if (ClientToScreen (hwnd, &ptTemp)) + { + rcClient.right = ptTemp.x; + rcClient.bottom = ptTemp.y; + if (!PtInRect (&rcClient, ptCurPos)) + bInhibit = TRUE; + } + } + } + } + } + + if (pCursor == NULL) + { + if (pScreenPriv->cursor.visible) + { + if (!bInhibit && g_fSoftwareCursor) + ShowCursor (FALSE); + pScreenPriv->cursor.visible = FALSE; + } + } + else + { + if (pScreenPriv->cursor.handle) + { + if (!bInhibit) + SetCursor (NULL); + DestroyCursor (pScreenPriv->cursor.handle); + pScreenPriv->cursor.handle = NULL; + } + pScreenPriv->cursor.handle = + winLoadCursor (pScreen, pCursor, pScreen->myNum); + WIN_DEBUG_MSG("winSetCursor: handle=%p\n", pScreenPriv->cursor.handle); + + if (!bInhibit) + SetCursor (pScreenPriv->cursor.handle); + + if (!pScreenPriv->cursor.visible) + { + if (!bInhibit && g_fSoftwareCursor) + ShowCursor (TRUE); + pScreenPriv->cursor.visible = TRUE; + } + } +} + + +/* + * QuartzMoveCursor + * Move the cursor. This is a noop for us. + */ +static void +winMoveCursor (ScreenPtr pScreen, int x, int y) +{ +} + + +static miPointerSpriteFuncRec winSpriteFuncsRec = { + winRealizeCursor, + winUnrealizeCursor, + winSetCursor, + winMoveCursor +}; + + +/* +=========================================================================== + + Other screen functions + +=========================================================================== +*/ + +/* + * winCursorQueryBestSize + * Handle queries for best cursor size + */ +static void +winCursorQueryBestSize (int class, unsigned short *width, + unsigned short *height, ScreenPtr pScreen) +{ + winScreenPriv(pScreen); + + if (class == CursorShape) + { + *width = pScreenPriv->cursor.sm_cx; + *height = pScreenPriv->cursor.sm_cy; + } + else + { + if (pScreenPriv->cursor.QueryBestSize) + (*pScreenPriv->cursor.QueryBestSize)(class, width, height, pScreen); + } +} + +/* + * winInitCursor + * Initialize cursor support + */ +Bool +winInitCursor (ScreenPtr pScreen) +{ + winScreenPriv(pScreen); + miPointerScreenPtr pPointPriv; + /* override some screen procedures */ + pScreenPriv->cursor.QueryBestSize = pScreen->QueryBestSize; + pScreen->QueryBestSize = winCursorQueryBestSize; + + pPointPriv = (miPointerScreenPtr) + dixLookupPrivate(&pScreen->devPrivates, miPointerScreenKey); + + pScreenPriv->cursor.spriteFuncs = pPointPriv->spriteFuncs; + pPointPriv->spriteFuncs = &winSpriteFuncsRec; + + pScreenPriv->cursor.handle = NULL; + pScreenPriv->cursor.visible = FALSE; + + pScreenPriv->cursor.sm_cx = GetSystemMetrics (SM_CXCURSOR); + pScreenPriv->cursor.sm_cy = GetSystemMetrics (SM_CYCURSOR); + + return TRUE; +} |