xserver-multidpi/hw/xwin/wincursor.c
Eamon Walsh 4017d31902 devPrivates rework: since API is already broken, switch everything
over to new system.

Need to update documentation and address some remaining vestiges of
old system such as CursorRec structure, fb "offman" structure, and
FontRec privates.
2007-08-28 09:28:25 -04:00

615 lines
16 KiB
C

/*
*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;
}