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