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
56 static void
57 winPointerWarpCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y);
58
59 static Bool
60 winCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y);
61
62 static void
63 winCrossScreen(ScreenPtr pScreen, Bool fEntering);
64
65 miPointerScreenFuncRec g_winPointerCursorFuncs = {
66 winCursorOffScreen,
67 winCrossScreen,
68 winPointerWarpCursor
69 };
70
71 static void
winPointerWarpCursor(DeviceIntPtr pDev,ScreenPtr pScreen,int x,int y)72 winPointerWarpCursor(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
118 static Bool
winCursorOffScreen(ScreenPtr * ppScreen,int * x,int * y)119 winCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y)
120 {
121 return FALSE;
122 }
123
124 static void
winCrossScreen(ScreenPtr pScreen,Bool fEntering)125 winCrossScreen(ScreenPtr pScreen, Bool fEntering)
126 {
127 }
128
129 static unsigned char
reverse(unsigned char c)130 reverse(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 */
145 static HCURSOR
winLoadCursor(ScreenPtr pScreen,CursorPtr pCursor,int screen)146 winLoadCursor(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 */
433 static Bool
winRealizeCursor(DeviceIntPtr pDev,ScreenPtr pScreen,CursorPtr pCursor)434 winRealizeCursor(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 */
448 static Bool
winUnrealizeCursor(DeviceIntPtr pDev,ScreenPtr pScreen,CursorPtr pCursor)449 winUnrealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
450 {
451 return TRUE;
452 }
453
454 /*
455 * winSetCursor
456 * Set the cursor sprite and position.
457 */
458 static void
winSetCursor(DeviceIntPtr pDev,ScreenPtr pScreen,CursorPtr pCursor,int x,int y)459 winSetCursor(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 */
527 static void
winMoveCursor(DeviceIntPtr pDev,ScreenPtr pScreen,int x,int y)528 winMoveCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
529 {
530 }
531
532 static Bool
winDeviceCursorInitialize(DeviceIntPtr pDev,ScreenPtr pScr)533 winDeviceCursorInitialize(DeviceIntPtr pDev, ScreenPtr pScr)
534 {
535 winScreenPriv(pScr);
536 return pScreenPriv->cursor.spriteFuncs->DeviceCursorInitialize(pDev, pScr);
537 }
538
539 static void
winDeviceCursorCleanup(DeviceIntPtr pDev,ScreenPtr pScr)540 winDeviceCursorCleanup(DeviceIntPtr pDev, ScreenPtr pScr)
541 {
542 winScreenPriv(pScr);
543 pScreenPriv->cursor.spriteFuncs->DeviceCursorCleanup(pDev, pScr);
544 }
545
546 static 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 */
567 static void
winCursorQueryBestSize(int class,unsigned short * width,unsigned short * height,ScreenPtr pScreen)568 winCursorQueryBestSize(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 */
588 Bool
winInitCursor(ScreenPtr pScreen)589 winInitCursor(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