/* *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 (DeviceIntPtr pDev, 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 (DeviceIntPtr pDev, 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 root window is active, or we are in a rootless mode */ if ((pScreenPriv->hwndScreen == GetForegroundWindow ()) || pScreenPriv->pScreenInfo->fRootless #ifdef XWIN_MULTIWINDOW || pScreenPriv->pScreenInfo->fMultiWindow #endif ) { /* 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 (pDev, 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 = calloc (1, 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 (DeviceIntPtr pDev, 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(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor) { return TRUE; } /* * winSetCursor * Set the cursor sprite and position. */ static void winSetCursor (DeviceIntPtr pDev, 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 (DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y) { } static Bool winDeviceInitialize(DeviceIntPtr pDev, ScreenPtr pScreen) { return TRUE; } static void winDeviceCleanup(DeviceIntPtr pDev, ScreenPtr pScreen) { } static miPointerSpriteFuncRec winSpriteFuncsRec = { winRealizeCursor, winUnrealizeCursor, winSetCursor, winMoveCursor, winDeviceInitialize, winDeviceCleanup }; /* =========================================================================== 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); if (pPointPriv) { 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; }