You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xserver/hw/xwin/wincursor.c

612 lines
19 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>
#include "misc.h"
#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
|| pScreenPriv->pScreenInfo->fMultiWindow
) {
/* 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;
unsigned char bit;
HDC hDC;
BITMAPV4HEADER bi;
BITMAPINFO *pbmi;
uint32_t *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 whether 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(3,
"winLoadCursor - Windows requires %dx%d cursor but 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 =
bits_to_bytes(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 = bits_to_bytes(nCX);
for (y = 0; y < nCY; ++y)
for (x = 0; x < xmax; ++x) {
int nWinPix = bits_to_bytes(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 = bits_to_bytes(nCX);
for (y = 0; y < nCY; ++y)
for (x = 0; x < xmax; ++x) {
int nWinPix = bits_to_bytes(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 = calloc(pScreenPriv->cursor.sm_cx * pScreenPriv->cursor.sm_cy,
sizeof(uint32_t));
if (lpBits) {
int y;
for (y = 0; y < nCY; y++) {
void *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) {
RGBQUAD *pbmiColors;
/* Bicolor, use a palettized DIB */
WIN_DEBUG_MSG("winLoadCursor: Trying two color cursor\n");
pbmi = (BITMAPINFO *) &bi;
pbmiColors = &(pbmi->bmiColors[0]);
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;
pbmiColors[0].rgbRed = 0; /* Empty */
pbmiColors[0].rgbGreen = 0;
pbmiColors[0].rgbBlue = 0;
pbmiColors[0].rgbReserved = 0;
pbmiColors[1].rgbRed = pCursor->backRed >> 8; /* Background */
pbmiColors[1].rgbGreen = pCursor->backGreen >> 8;
pbmiColors[1].rgbBlue = pCursor->backBlue >> 8;
pbmiColors[1].rgbReserved = 0;
pbmiColors[2].rgbRed = pCursor->foreRed >> 8; /* Foreground */
pbmiColors[2].rgbGreen = pCursor->foreGreen >> 8;
pbmiColors[2].rgbBlue = pCursor->foreBlue >> 8;
pbmiColors[2].rgbReserved = 0;
lpBits = calloc(pScreenPriv->cursor.sm_cx * pScreenPriv->cursor.sm_cy, 1);
pCur = (unsigned char *) lpBits;
if (lpBits) {
int x, y;
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 =
bits_to_bytes(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;
}
}
}
/*
* winMoveCursor
* Move the cursor. This is a noop for us.
*/
static void
winMoveCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
{
}
static Bool
winDeviceCursorInitialize(DeviceIntPtr pDev, ScreenPtr pScr)
{
winScreenPriv(pScr);
return pScreenPriv->cursor.spriteFuncs->DeviceCursorInitialize(pDev, pScr);
}
static void
winDeviceCursorCleanup(DeviceIntPtr pDev, ScreenPtr pScr)
{
winScreenPriv(pScr);
pScreenPriv->cursor.spriteFuncs->DeviceCursorCleanup(pDev, pScr);
}
static miPointerSpriteFuncRec winSpriteFuncsRec = {
winRealizeCursor,
winUnrealizeCursor,
winSetCursor,
winMoveCursor,
winDeviceCursorInitialize,
winDeviceCursorCleanup
};
/*
===========================================================================
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;
}