1/* 2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved. 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 *"Software"), to deal in the Software without restriction, including 7 *without limitation the rights to use, copy, modify, merge, publish, 8 *distribute, sublicense, and/or sell copies of the Software, and to 9 *permit persons to whom the Software is furnished to do so, subject to 10 *the following conditions: 11 * 12 *The above copyright notice and this permission notice shall be 13 *included in all copies or substantial portions of the Software. 14 * 15 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR 19 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 * 23 *Except as contained in this notice, the name of the XFree86 Project 24 *shall not be used in advertising or otherwise to promote the sale, use 25 *or other dealings in this Software without prior written authorization 26 *from the XFree86 Project. 27 * 28 * Authors: Dakshinamurthy Karra 29 * Suhaib M Siddiqi 30 * Peter Busch 31 * Harold L Hunt II 32 */ 33 34#ifdef HAVE_XWIN_CONFIG_H 35#include <xwin-config.h> 36#endif 37#include "win.h" 38#include "winmsg.h" 39#include <cursorstr.h> 40#include <mipointrst.h> 41#include <servermd.h> 42#include "misc.h" 43 44#define BRIGHTNESS(x) (x##Red * 0.299 + x##Green * 0.587 + x##Blue * 0.114) 45 46#if 0 47#define WIN_DEBUG_MSG winDebug 48#else 49#define WIN_DEBUG_MSG(...) 50#endif 51 52/* 53 * Local function prototypes 54 */ 55 56static void 57 winPointerWarpCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y); 58 59static Bool 60 winCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y); 61 62static void 63 winCrossScreen(ScreenPtr pScreen, Bool fEntering); 64 65miPointerScreenFuncRec g_winPointerCursorFuncs = { 66 winCursorOffScreen, 67 winCrossScreen, 68 winPointerWarpCursor 69}; 70 71static void 72winPointerWarpCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y) 73{ 74 winScreenPriv(pScreen); 75 RECT rcClient; 76 static Bool s_fInitialWarp = TRUE; 77 78 /* Discard first warp call */ 79 if (s_fInitialWarp) { 80 /* First warp moves mouse to center of window, just ignore it */ 81 82 /* Don't ignore subsequent warps */ 83 s_fInitialWarp = FALSE; 84 85 winErrorFVerb(2, 86 "winPointerWarpCursor - Discarding first warp: %d %d\n", 87 x, y); 88 89 return; 90 } 91 92 /* 93 Only update the Windows cursor position if root window is active, 94 or we are in a rootless mode 95 */ 96 if ((pScreenPriv->hwndScreen == GetForegroundWindow()) 97 || pScreenPriv->pScreenInfo->fRootless 98 || pScreenPriv->pScreenInfo->fMultiWindow 99 ) { 100 /* Get the client area coordinates */ 101 GetClientRect(pScreenPriv->hwndScreen, &rcClient); 102 103 /* Translate the client area coords to screen coords */ 104 MapWindowPoints(pScreenPriv->hwndScreen, 105 HWND_DESKTOP, (LPPOINT) &rcClient, 2); 106 107 /* 108 * Update the Windows cursor position so that we don't 109 * immediately warp back to the current position. 110 */ 111 SetCursorPos(rcClient.left + x, rcClient.top + y); 112 } 113 114 /* Call the mi warp procedure to do the actual warping in X. */ 115 miPointerWarpCursor(pDev, pScreen, x, y); 116} 117 118static Bool 119winCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y) 120{ 121 return FALSE; 122} 123 124static void 125winCrossScreen(ScreenPtr pScreen, Bool fEntering) 126{ 127} 128 129static unsigned char 130reverse(unsigned char c) 131{ 132 int i; 133 unsigned char ret = 0; 134 135 for (i = 0; i < 8; ++i) { 136 ret |= ((c >> i) & 1) << (7 - i); 137 } 138 return ret; 139} 140 141/* 142 * Convert X cursor to Windows cursor 143 * FIXME: Perhaps there are more smart code 144 */ 145static HCURSOR 146winLoadCursor(ScreenPtr pScreen, CursorPtr pCursor, int screen) 147{ 148 winScreenPriv(pScreen); 149 HCURSOR hCursor = NULL; 150 unsigned char *pAnd; 151 unsigned char *pXor; 152 int nCX, nCY; 153 int nBytes; 154 double dForeY, dBackY; 155 BOOL fReverse; 156 HBITMAP hAnd, hXor; 157 ICONINFO ii; 158 unsigned char *pCur; 159 unsigned char bit; 160 HDC hDC; 161 BITMAPV4HEADER bi; 162 BITMAPINFO *pbmi; 163 uint32_t *lpBits; 164 165 WIN_DEBUG_MSG("winLoadCursor: Win32: %dx%d X11: %dx%d hotspot: %d,%d\n", 166 pScreenPriv->cursor.sm_cx, pScreenPriv->cursor.sm_cy, 167 pCursor->bits->width, pCursor->bits->height, 168 pCursor->bits->xhot, pCursor->bits->yhot); 169 170 /* We can use only White and Black, so calc brightness of color 171 * Also check if the cursor is inverted */ 172 dForeY = BRIGHTNESS(pCursor->fore); 173 dBackY = BRIGHTNESS(pCursor->back); 174 fReverse = dForeY < dBackY; 175 176 /* Check whether the X11 cursor is bigger than the win32 cursor */ 177 if (pScreenPriv->cursor.sm_cx < pCursor->bits->width || 178 pScreenPriv->cursor.sm_cy < pCursor->bits->height) { 179 winErrorFVerb(3, 180 "winLoadCursor - Windows requires %dx%d cursor but X requires %dx%d\n", 181 pScreenPriv->cursor.sm_cx, pScreenPriv->cursor.sm_cy, 182 pCursor->bits->width, pCursor->bits->height); 183 } 184 185 /* Get the number of bytes required to store the whole cursor image 186 * This is roughly (sm_cx * sm_cy) / 8 187 * round up to 8 pixel boundary so we can convert whole bytes */ 188 nBytes = 189 bits_to_bytes(pScreenPriv->cursor.sm_cx) * pScreenPriv->cursor.sm_cy; 190 191 /* Get the effective width and height */ 192 nCX = min(pScreenPriv->cursor.sm_cx, pCursor->bits->width); 193 nCY = min(pScreenPriv->cursor.sm_cy, pCursor->bits->height); 194 195 /* Allocate memory for the bitmaps */ 196 pAnd = malloc(nBytes); 197 memset(pAnd, 0xFF, nBytes); 198 pXor = calloc(1, nBytes); 199 200 /* Convert the X11 bitmap to a win32 bitmap 201 * The first is for an empty mask */ 202 if (pCursor->bits->emptyMask) { 203 int x, y, xmax = bits_to_bytes(nCX); 204 205 for (y = 0; y < nCY; ++y) 206 for (x = 0; x < xmax; ++x) { 207 int nWinPix = bits_to_bytes(pScreenPriv->cursor.sm_cx) * y + x; 208 int nXPix = BitmapBytePad(pCursor->bits->width) * y + x; 209 210 pAnd[nWinPix] = 0; 211 if (fReverse) 212 pXor[nWinPix] = reverse(~pCursor->bits->source[nXPix]); 213 else 214 pXor[nWinPix] = reverse(pCursor->bits->source[nXPix]); 215 } 216 } 217 else { 218 int x, y, xmax = bits_to_bytes(nCX); 219 220 for (y = 0; y < nCY; ++y) 221 for (x = 0; x < xmax; ++x) { 222 int nWinPix = bits_to_bytes(pScreenPriv->cursor.sm_cx) * y + x; 223 int nXPix = BitmapBytePad(pCursor->bits->width) * y + x; 224 225 unsigned char mask = pCursor->bits->mask[nXPix]; 226 227 pAnd[nWinPix] = reverse(~mask); 228 if (fReverse) 229 pXor[nWinPix] = 230 reverse(~pCursor->bits->source[nXPix] & mask); 231 else 232 pXor[nWinPix] = 233 reverse(pCursor->bits->source[nXPix] & mask); 234 } 235 } 236 237 /* prepare the pointers */ 238 hCursor = NULL; 239 lpBits = NULL; 240 241 /* We have a truecolor alpha-blended cursor and can use it! */ 242 if (pCursor->bits->argb) { 243 WIN_DEBUG_MSG("winLoadCursor: Trying truecolor alphablended cursor\n"); 244 memset(&bi, 0, sizeof(BITMAPV4HEADER)); 245 bi.bV4Size = sizeof(BITMAPV4HEADER); 246 bi.bV4Width = pScreenPriv->cursor.sm_cx; 247 bi.bV4Height = -(pScreenPriv->cursor.sm_cy); /* right-side up */ 248 bi.bV4Planes = 1; 249 bi.bV4BitCount = 32; 250 bi.bV4V4Compression = BI_BITFIELDS; 251 bi.bV4RedMask = 0x00FF0000; 252 bi.bV4GreenMask = 0x0000FF00; 253 bi.bV4BlueMask = 0x000000FF; 254 bi.bV4AlphaMask = 0xFF000000; 255 256 lpBits = calloc(pScreenPriv->cursor.sm_cx * pScreenPriv->cursor.sm_cy, 257 sizeof(uint32_t)); 258 259 if (lpBits) { 260 int y; 261 for (y = 0; y < nCY; y++) { 262 void *src, *dst; 263 src = &(pCursor->bits->argb[y * pCursor->bits->width]); 264 dst = &(lpBits[y * pScreenPriv->cursor.sm_cx]); 265 memcpy(dst, src, 4 * nCX); 266 } 267 } 268 } /* End if-truecolor-icon */ 269 270 if (!lpBits) { 271 RGBQUAD *pbmiColors; 272 /* Bicolor, use a palettized DIB */ 273 WIN_DEBUG_MSG("winLoadCursor: Trying two color cursor\n"); 274 pbmi = (BITMAPINFO *) &bi; 275 pbmiColors = &(pbmi->bmiColors[0]); 276 277 memset(pbmi, 0, sizeof(BITMAPINFOHEADER)); 278 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 279 pbmi->bmiHeader.biWidth = pScreenPriv->cursor.sm_cx; 280 pbmi->bmiHeader.biHeight = -abs(pScreenPriv->cursor.sm_cy); /* right-side up */ 281 pbmi->bmiHeader.biPlanes = 1; 282 pbmi->bmiHeader.biBitCount = 8; 283 pbmi->bmiHeader.biCompression = BI_RGB; 284 pbmi->bmiHeader.biSizeImage = 0; 285 pbmi->bmiHeader.biClrUsed = 3; 286 pbmi->bmiHeader.biClrImportant = 3; 287 288 pbmiColors[0].rgbRed = 0; /* Empty */ 289 pbmiColors[0].rgbGreen = 0; 290 pbmiColors[0].rgbBlue = 0; 291 pbmiColors[0].rgbReserved = 0; 292 pbmiColors[1].rgbRed = pCursor->backRed >> 8; /* Background */ 293 pbmiColors[1].rgbGreen = pCursor->backGreen >> 8; 294 pbmiColors[1].rgbBlue = pCursor->backBlue >> 8; 295 pbmiColors[1].rgbReserved = 0; 296 pbmiColors[2].rgbRed = pCursor->foreRed >> 8; /* Foreground */ 297 pbmiColors[2].rgbGreen = pCursor->foreGreen >> 8; 298 pbmiColors[2].rgbBlue = pCursor->foreBlue >> 8; 299 pbmiColors[2].rgbReserved = 0; 300 301 lpBits = calloc(pScreenPriv->cursor.sm_cx * pScreenPriv->cursor.sm_cy, 1); 302 303 pCur = (unsigned char *) lpBits; 304 if (lpBits) { 305 int x, y; 306 for (y = 0; y < pScreenPriv->cursor.sm_cy; y++) { 307 for (x = 0; x < pScreenPriv->cursor.sm_cx; x++) { 308 if (x >= nCX || y >= nCY) /* Outside of X11 icon bounds */ 309 (*pCur++) = 0; 310 else { /* Within X11 icon bounds */ 311 312 int nWinPix = 313 bits_to_bytes(pScreenPriv->cursor.sm_cx) * y + 314 (x / 8); 315 316 bit = pAnd[nWinPix]; 317 bit = bit & (1 << (7 - (x & 7))); 318 if (!bit) { /* Within the cursor mask? */ 319 int nXPix = 320 BitmapBytePad(pCursor->bits->width) * y + 321 (x / 8); 322 bit = 323 ~reverse(~pCursor->bits-> 324 source[nXPix] & pCursor->bits-> 325 mask[nXPix]); 326 bit = bit & (1 << (7 - (x & 7))); 327 if (bit) /* Draw foreground */ 328 (*pCur++) = 2; 329 else /* Draw background */ 330 (*pCur++) = 1; 331 } 332 else /* Outside the cursor mask */ 333 (*pCur++) = 0; 334 } 335 } /* end for (x) */ 336 } /* end for (y) */ 337 } /* end if (lpbits) */ 338 } 339 340 /* If one of the previous two methods gave us the bitmap we need, make a cursor */ 341 if (lpBits) { 342 WIN_DEBUG_MSG("winLoadCursor: Creating bitmap cursor: hotspot %d,%d\n", 343 pCursor->bits->xhot, pCursor->bits->yhot); 344 345 hAnd = NULL; 346 hXor = NULL; 347 348 hAnd = 349 CreateBitmap(pScreenPriv->cursor.sm_cx, pScreenPriv->cursor.sm_cy, 350 1, 1, pAnd); 351 352 hDC = GetDC(NULL); 353 if (hDC) { 354 hXor = 355 CreateCompatibleBitmap(hDC, pScreenPriv->cursor.sm_cx, 356 pScreenPriv->cursor.sm_cy); 357 SetDIBits(hDC, hXor, 0, pScreenPriv->cursor.sm_cy, lpBits, 358 (BITMAPINFO *) &bi, DIB_RGB_COLORS); 359 ReleaseDC(NULL, hDC); 360 } 361 free(lpBits); 362 363 if (hAnd && hXor) { 364 ii.fIcon = FALSE; 365 ii.xHotspot = pCursor->bits->xhot; 366 ii.yHotspot = pCursor->bits->yhot; 367 ii.hbmMask = hAnd; 368 ii.hbmColor = hXor; 369 hCursor = (HCURSOR) CreateIconIndirect(&ii); 370 371 if (hCursor == NULL) 372 winW32Error(2, "winLoadCursor - CreateIconIndirect failed:"); 373 else { 374 if (GetIconInfo(hCursor, &ii)) { 375 if (ii.fIcon) { 376 WIN_DEBUG_MSG 377 ("winLoadCursor: CreateIconIndirect returned no cursor. Trying again.\n"); 378 379 DestroyCursor(hCursor); 380 381 ii.fIcon = FALSE; 382 ii.xHotspot = pCursor->bits->xhot; 383 ii.yHotspot = pCursor->bits->yhot; 384 hCursor = (HCURSOR) CreateIconIndirect(&ii); 385 386 if (hCursor == NULL) 387 winW32Error(2, 388 "winLoadCursor - CreateIconIndirect failed:"); 389 } 390 /* GetIconInfo creates new bitmaps. Destroy them again */ 391 if (ii.hbmMask) 392 DeleteObject(ii.hbmMask); 393 if (ii.hbmColor) 394 DeleteObject(ii.hbmColor); 395 } 396 } 397 } 398 399 if (hAnd) 400 DeleteObject(hAnd); 401 if (hXor) 402 DeleteObject(hXor); 403 } 404 405 if (!hCursor) { 406 /* We couldn't make a color cursor for this screen, use 407 black and white instead */ 408 hCursor = CreateCursor(g_hInstance, 409 pCursor->bits->xhot, pCursor->bits->yhot, 410 pScreenPriv->cursor.sm_cx, 411 pScreenPriv->cursor.sm_cy, pAnd, pXor); 412 if (hCursor == NULL) 413 winW32Error(2, "winLoadCursor - CreateCursor failed:"); 414 } 415 free(pAnd); 416 free(pXor); 417 418 return hCursor; 419} 420 421/* 422=========================================================================== 423 424 Pointer sprite functions 425 426=========================================================================== 427*/ 428 429/* 430 * winRealizeCursor 431 * Convert the X cursor representation to native format if possible. 432 */ 433static Bool 434winRealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor) 435{ 436 if (pCursor == NULL || pCursor->bits == NULL) 437 return FALSE; 438 439 /* FIXME: cache ARGB8888 representation? */ 440 441 return TRUE; 442} 443 444/* 445 * winUnrealizeCursor 446 * Free the storage space associated with a realized cursor. 447 */ 448static Bool 449winUnrealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor) 450{ 451 return TRUE; 452} 453 454/* 455 * winSetCursor 456 * Set the cursor sprite and position. 457 */ 458static void 459winSetCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor, int x, 460 int y) 461{ 462 POINT ptCurPos, ptTemp; 463 HWND hwnd; 464 RECT rcClient; 465 BOOL bInhibit; 466 467 winScreenPriv(pScreen); 468 WIN_DEBUG_MSG("winSetCursor: cursor=%p\n", pCursor); 469 470 /* Inhibit changing the cursor if the mouse is not in a client area */ 471 bInhibit = FALSE; 472 if (GetCursorPos(&ptCurPos)) { 473 hwnd = WindowFromPoint(ptCurPos); 474 if (hwnd) { 475 if (GetClientRect(hwnd, &rcClient)) { 476 ptTemp.x = rcClient.left; 477 ptTemp.y = rcClient.top; 478 if (ClientToScreen(hwnd, &ptTemp)) { 479 rcClient.left = ptTemp.x; 480 rcClient.top = ptTemp.y; 481 ptTemp.x = rcClient.right; 482 ptTemp.y = rcClient.bottom; 483 if (ClientToScreen(hwnd, &ptTemp)) { 484 rcClient.right = ptTemp.x; 485 rcClient.bottom = ptTemp.y; 486 if (!PtInRect(&rcClient, ptCurPos)) 487 bInhibit = TRUE; 488 } 489 } 490 } 491 } 492 } 493 494 if (pCursor == NULL) { 495 if (pScreenPriv->cursor.visible) { 496 if (!bInhibit && g_fSoftwareCursor) 497 ShowCursor(FALSE); 498 pScreenPriv->cursor.visible = FALSE; 499 } 500 } 501 else { 502 if (pScreenPriv->cursor.handle) { 503 if (!bInhibit) 504 SetCursor(NULL); 505 DestroyCursor(pScreenPriv->cursor.handle); 506 pScreenPriv->cursor.handle = NULL; 507 } 508 pScreenPriv->cursor.handle = 509 winLoadCursor(pScreen, pCursor, pScreen->myNum); 510 WIN_DEBUG_MSG("winSetCursor: handle=%p\n", pScreenPriv->cursor.handle); 511 512 if (!bInhibit) 513 SetCursor(pScreenPriv->cursor.handle); 514 515 if (!pScreenPriv->cursor.visible) { 516 if (!bInhibit && g_fSoftwareCursor) 517 ShowCursor(TRUE); 518 pScreenPriv->cursor.visible = TRUE; 519 } 520 } 521} 522 523/* 524 * winMoveCursor 525 * Move the cursor. This is a noop for us. 526 */ 527static void 528winMoveCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y) 529{ 530} 531 532static Bool 533winDeviceCursorInitialize(DeviceIntPtr pDev, ScreenPtr pScr) 534{ 535 winScreenPriv(pScr); 536 return pScreenPriv->cursor.spriteFuncs->DeviceCursorInitialize(pDev, pScr); 537} 538 539static void 540winDeviceCursorCleanup(DeviceIntPtr pDev, ScreenPtr pScr) 541{ 542 winScreenPriv(pScr); 543 pScreenPriv->cursor.spriteFuncs->DeviceCursorCleanup(pDev, pScr); 544} 545 546static miPointerSpriteFuncRec winSpriteFuncsRec = { 547 winRealizeCursor, 548 winUnrealizeCursor, 549 winSetCursor, 550 winMoveCursor, 551 winDeviceCursorInitialize, 552 winDeviceCursorCleanup 553}; 554 555/* 556=========================================================================== 557 558 Other screen functions 559 560=========================================================================== 561*/ 562 563/* 564 * winCursorQueryBestSize 565 * Handle queries for best cursor size 566 */ 567static void 568winCursorQueryBestSize(int class, unsigned short *width, 569 unsigned short *height, ScreenPtr pScreen) 570{ 571 winScreenPriv(pScreen); 572 573 if (class == CursorShape) { 574 *width = pScreenPriv->cursor.sm_cx; 575 *height = pScreenPriv->cursor.sm_cy; 576 } 577 else { 578 if (pScreenPriv->cursor.QueryBestSize) 579 (*pScreenPriv->cursor.QueryBestSize) (class, width, height, 580 pScreen); 581 } 582} 583 584/* 585 * winInitCursor 586 * Initialize cursor support 587 */ 588Bool 589winInitCursor(ScreenPtr pScreen) 590{ 591 winScreenPriv(pScreen); 592 miPointerScreenPtr pPointPriv; 593 594 /* override some screen procedures */ 595 pScreenPriv->cursor.QueryBestSize = pScreen->QueryBestSize; 596 pScreen->QueryBestSize = winCursorQueryBestSize; 597 598 pPointPriv = (miPointerScreenPtr) 599 dixLookupPrivate(&pScreen->devPrivates, miPointerScreenKey); 600 601 pScreenPriv->cursor.spriteFuncs = pPointPriv->spriteFuncs; 602 pPointPriv->spriteFuncs = &winSpriteFuncsRec; 603 604 pScreenPriv->cursor.handle = NULL; 605 pScreenPriv->cursor.visible = FALSE; 606 607 pScreenPriv->cursor.sm_cx = GetSystemMetrics(SM_CXCURSOR); 608 pScreenPriv->cursor.sm_cy = GetSystemMetrics(SM_CYCURSOR); 609 610 return TRUE; 611} 612