2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
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:
12 *The above copyright notice and this permission notice shall be
13 *included in all copies or substantial portions of the Software.
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.
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.
28 * Authors: Dakshinamurthy Karra
34 #ifdef HAVE_XWIN_CONFIG_H
35 #include <xwin-config.h>
39 #include <cursorstr.h>
40 #include <mipointrst.h>
44 #define BRIGHTNESS(x) (x##Red * 0.299 + x##Green * 0.587 + x##Blue * 0.114)
47 #define WIN_DEBUG_MSG winDebug
49 #define WIN_DEBUG_MSG(...)
53 * Local function prototypes
57 winPointerWarpCursor(DeviceIntPtr pDev
, ScreenPtr pScreen
, int x
, int y
);
60 winCursorOffScreen(ScreenPtr
*ppScreen
, int *x
, int *y
);
63 winCrossScreen(ScreenPtr pScreen
, Bool fEntering
);
65 miPointerScreenFuncRec g_winPointerCursorFuncs
= {
72 winPointerWarpCursor(DeviceIntPtr pDev
, ScreenPtr pScreen
, int x
, int y
)
74 winScreenPriv(pScreen
);
76 static Bool s_fInitialWarp
= TRUE
;
78 /* Discard first warp call */
80 /* First warp moves mouse to center of window, just ignore it */
82 /* Don't ignore subsequent warps */
83 s_fInitialWarp
= FALSE
;
86 "winPointerWarpCursor - Discarding first warp: %d %d\n",
93 Only update the Windows cursor position if root window is active,
94 or we are in a rootless mode
96 if ((pScreenPriv
->hwndScreen
== GetForegroundWindow())
97 || pScreenPriv
->pScreenInfo
->fRootless
98 #ifdef XWIN_MULTIWINDOW
99 || pScreenPriv
->pScreenInfo
->fMultiWindow
102 /* Get the client area coordinates */
103 GetClientRect(pScreenPriv
->hwndScreen
, &rcClient
);
105 /* Translate the client area coords to screen coords */
106 MapWindowPoints(pScreenPriv
->hwndScreen
,
107 HWND_DESKTOP
, (LPPOINT
) &rcClient
, 2);
110 * Update the Windows cursor position so that we don't
111 * immediately warp back to the current position.
113 SetCursorPos(rcClient
.left
+ x
, rcClient
.top
+ y
);
116 /* Call the mi warp procedure to do the actual warping in X. */
117 miPointerWarpCursor(pDev
, pScreen
, x
, y
);
121 winCursorOffScreen(ScreenPtr
*ppScreen
, int *x
, int *y
)
127 winCrossScreen(ScreenPtr pScreen
, Bool fEntering
)
132 reverse(unsigned char c
)
135 unsigned char ret
= 0;
137 for (i
= 0; i
< 8; ++i
) {
138 ret
|= ((c
>> i
) & 1) << (7 - i
);
144 * Convert X cursor to Windows cursor
145 * FIXME: Perhaps there are more smart code
148 winLoadCursor(ScreenPtr pScreen
, CursorPtr pCursor
, int screen
)
150 winScreenPriv(pScreen
);
151 HCURSOR hCursor
= NULL
;
156 double dForeY
, dBackY
;
167 WIN_DEBUG_MSG("winLoadCursor: Win32: %dx%d X11: %dx%d hotspot: %d,%d\n",
168 pScreenPriv
->cursor
.sm_cx
, pScreenPriv
->cursor
.sm_cy
,
169 pCursor
->bits
->width
, pCursor
->bits
->height
,
170 pCursor
->bits
->xhot
, pCursor
->bits
->yhot
);
172 /* We can use only White and Black, so calc brightness of color
173 * Also check if the cursor is inverted */
174 dForeY
= BRIGHTNESS(pCursor
->fore
);
175 dBackY
= BRIGHTNESS(pCursor
->back
);
176 fReverse
= dForeY
< dBackY
;
178 /* Check wether the X11 cursor is bigger than the win32 cursor */
179 if (pScreenPriv
->cursor
.sm_cx
< pCursor
->bits
->width
||
180 pScreenPriv
->cursor
.sm_cy
< pCursor
->bits
->height
) {
182 "winLoadCursor - Windows requires %dx%d cursor but X requires %dx%d\n",
183 pScreenPriv
->cursor
.sm_cx
, pScreenPriv
->cursor
.sm_cy
,
184 pCursor
->bits
->width
, pCursor
->bits
->height
);
187 /* Get the number of bytes required to store the whole cursor image
188 * This is roughly (sm_cx * sm_cy) / 8
189 * round up to 8 pixel boundary so we can convert whole bytes */
191 bits_to_bytes(pScreenPriv
->cursor
.sm_cx
) * pScreenPriv
->cursor
.sm_cy
;
193 /* Get the effective width and height */
194 nCX
= min(pScreenPriv
->cursor
.sm_cx
, pCursor
->bits
->width
);
195 nCY
= min(pScreenPriv
->cursor
.sm_cy
, pCursor
->bits
->height
);
197 /* Allocate memory for the bitmaps */
198 pAnd
= malloc(nBytes
);
199 memset(pAnd
, 0xFF, nBytes
);
200 pXor
= calloc(1, nBytes
);
202 /* Convert the X11 bitmap to a win32 bitmap
203 * The first is for an empty mask */
204 if (pCursor
->bits
->emptyMask
) {
205 int x
, y
, xmax
= bits_to_bytes(nCX
);
207 for (y
= 0; y
< nCY
; ++y
)
208 for (x
= 0; x
< xmax
; ++x
) {
209 int nWinPix
= bits_to_bytes(pScreenPriv
->cursor
.sm_cx
) * y
+ x
;
210 int nXPix
= BitmapBytePad(pCursor
->bits
->width
) * y
+ x
;
214 pXor
[nWinPix
] = reverse(~pCursor
->bits
->source
[nXPix
]);
216 pXor
[nWinPix
] = reverse(pCursor
->bits
->source
[nXPix
]);
220 int x
, y
, xmax
= bits_to_bytes(nCX
);
222 for (y
= 0; y
< nCY
; ++y
)
223 for (x
= 0; x
< xmax
; ++x
) {
224 int nWinPix
= bits_to_bytes(pScreenPriv
->cursor
.sm_cx
) * y
+ x
;
225 int nXPix
= BitmapBytePad(pCursor
->bits
->width
) * y
+ x
;
227 unsigned char mask
= pCursor
->bits
->mask
[nXPix
];
229 pAnd
[nWinPix
] = reverse(~mask
);
232 reverse(~pCursor
->bits
->source
[nXPix
] & mask
);
235 reverse(pCursor
->bits
->source
[nXPix
] & mask
);
239 /* prepare the pointers */
243 /* We have a truecolor alpha-blended cursor and can use it! */
244 if (pCursor
->bits
->argb
) {
245 WIN_DEBUG_MSG("winLoadCursor: Trying truecolor alphablended cursor\n");
246 memset(&bi
, 0, sizeof(BITMAPV4HEADER
));
247 bi
.bV4Size
= sizeof(BITMAPV4HEADER
);
248 bi
.bV4Width
= pScreenPriv
->cursor
.sm_cx
;
249 bi
.bV4Height
= -(pScreenPriv
->cursor
.sm_cy
); /* right-side up */
252 bi
.bV4V4Compression
= BI_BITFIELDS
;
253 bi
.bV4RedMask
= 0x00FF0000;
254 bi
.bV4GreenMask
= 0x0000FF00;
255 bi
.bV4BlueMask
= 0x000000FF;
256 bi
.bV4AlphaMask
= 0xFF000000;
259 (uint32_t *) calloc(pScreenPriv
->cursor
.sm_cx
*
260 pScreenPriv
->cursor
.sm_cy
,
265 for (y
= 0; y
< nCY
; y
++) {
267 src
= &(pCursor
->bits
->argb
[y
* pCursor
->bits
->width
]);
268 dst
= &(lpBits
[y
* pScreenPriv
->cursor
.sm_cx
]);
269 memcpy(dst
, src
, 4 * nCX
);
272 } /* End if-truecolor-icon */
276 /* Bicolor, use a palettized DIB */
277 WIN_DEBUG_MSG("winLoadCursor: Trying two color cursor\n");
278 pbmi
= (BITMAPINFO
*) &bi
;
279 pbmiColors
= &(pbmi
->bmiColors
[0]);
281 memset(pbmi
, 0, sizeof(BITMAPINFOHEADER
));
282 pbmi
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
283 pbmi
->bmiHeader
.biWidth
= pScreenPriv
->cursor
.sm_cx
;
284 pbmi
->bmiHeader
.biHeight
= -abs(pScreenPriv
->cursor
.sm_cy
); /* right-side up */
285 pbmi
->bmiHeader
.biPlanes
= 1;
286 pbmi
->bmiHeader
.biBitCount
= 8;
287 pbmi
->bmiHeader
.biCompression
= BI_RGB
;
288 pbmi
->bmiHeader
.biSizeImage
= 0;
289 pbmi
->bmiHeader
.biClrUsed
= 3;
290 pbmi
->bmiHeader
.biClrImportant
= 3;
292 pbmiColors
[0].rgbRed
= 0; /* Empty */
293 pbmiColors
[0].rgbGreen
= 0;
294 pbmiColors
[0].rgbBlue
= 0;
295 pbmiColors
[0].rgbReserved
= 0;
296 pbmiColors
[1].rgbRed
= pCursor
->backRed
>> 8; /* Background */
297 pbmiColors
[1].rgbGreen
= pCursor
->backGreen
>> 8;
298 pbmiColors
[1].rgbBlue
= pCursor
->backBlue
>> 8;
299 pbmiColors
[1].rgbReserved
= 0;
300 pbmiColors
[2].rgbRed
= pCursor
->foreRed
>> 8; /* Foreground */
301 pbmiColors
[2].rgbGreen
= pCursor
->foreGreen
>> 8;
302 pbmiColors
[2].rgbBlue
= pCursor
->foreBlue
>> 8;
303 pbmiColors
[2].rgbReserved
= 0;
306 (uint32_t *) calloc(pScreenPriv
->cursor
.sm_cx
*
307 pScreenPriv
->cursor
.sm_cy
, sizeof(char));
309 pCur
= (unsigned char *) lpBits
;
312 for (y
= 0; y
< pScreenPriv
->cursor
.sm_cy
; y
++) {
313 for (x
= 0; x
< pScreenPriv
->cursor
.sm_cx
; x
++) {
314 if (x
>= nCX
|| y
>= nCY
) /* Outside of X11 icon bounds */
316 else { /* Within X11 icon bounds */
319 bits_to_bytes(pScreenPriv
->cursor
.sm_cx
) * y
+
323 bit
= bit
& (1 << (7 - (x
& 7)));
324 if (!bit
) { /* Within the cursor mask? */
326 BitmapBytePad(pCursor
->bits
->width
) * y
+
329 ~reverse(~pCursor
->bits
->
330 source
[nXPix
] & pCursor
->bits
->
332 bit
= bit
& (1 << (7 - (x
& 7)));
333 if (bit
) /* Draw foreground */
335 else /* Draw background */
338 else /* Outside the cursor mask */
343 } /* end if (lpbits) */
346 /* If one of the previous two methods gave us the bitmap we need, make a cursor */
348 WIN_DEBUG_MSG("winLoadCursor: Creating bitmap cursor: hotspot %d,%d\n",
349 pCursor
->bits
->xhot
, pCursor
->bits
->yhot
);
355 CreateBitmap(pScreenPriv
->cursor
.sm_cx
, pScreenPriv
->cursor
.sm_cy
,
361 CreateCompatibleBitmap(hDC
, pScreenPriv
->cursor
.sm_cx
,
362 pScreenPriv
->cursor
.sm_cy
);
363 SetDIBits(hDC
, hXor
, 0, pScreenPriv
->cursor
.sm_cy
, lpBits
,
364 (BITMAPINFO
*) &bi
, DIB_RGB_COLORS
);
365 ReleaseDC(NULL
, hDC
);
371 ii
.xHotspot
= pCursor
->bits
->xhot
;
372 ii
.yHotspot
= pCursor
->bits
->yhot
;
375 hCursor
= (HCURSOR
) CreateIconIndirect(&ii
);
378 winW32Error(2, "winLoadCursor - CreateIconIndirect failed:");
380 if (GetIconInfo(hCursor
, &ii
)) {
383 ("winLoadCursor: CreateIconIndirect returned no cursor. Trying again.\n");
385 DestroyCursor(hCursor
);
388 ii
.xHotspot
= pCursor
->bits
->xhot
;
389 ii
.yHotspot
= pCursor
->bits
->yhot
;
390 hCursor
= (HCURSOR
) CreateIconIndirect(&ii
);
394 "winLoadCursor - CreateIconIndirect failed:");
396 /* GetIconInfo creates new bitmaps. Destroy them again */
398 DeleteObject(ii
.hbmMask
);
400 DeleteObject(ii
.hbmColor
);
412 /* We couldn't make a color cursor for this screen, use
413 black and white instead */
414 hCursor
= CreateCursor(g_hInstance
,
415 pCursor
->bits
->xhot
, pCursor
->bits
->yhot
,
416 pScreenPriv
->cursor
.sm_cx
,
417 pScreenPriv
->cursor
.sm_cy
, pAnd
, pXor
);
419 winW32Error(2, "winLoadCursor - CreateCursor failed:");
428 ===========================================================================
430 Pointer sprite functions
432 ===========================================================================
437 * Convert the X cursor representation to native format if possible.
440 winRealizeCursor(DeviceIntPtr pDev
, ScreenPtr pScreen
, CursorPtr pCursor
)
442 if (pCursor
== NULL
|| pCursor
->bits
== NULL
)
445 /* FIXME: cache ARGB8888 representation? */
452 * Free the storage space associated with a realized cursor.
455 winUnrealizeCursor(DeviceIntPtr pDev
, ScreenPtr pScreen
, CursorPtr pCursor
)
462 * Set the cursor sprite and position.
465 winSetCursor(DeviceIntPtr pDev
, ScreenPtr pScreen
, CursorPtr pCursor
, int x
,
468 POINT ptCurPos
, ptTemp
;
473 winScreenPriv(pScreen
);
474 WIN_DEBUG_MSG("winSetCursor: cursor=%p\n", pCursor
);
476 /* Inhibit changing the cursor if the mouse is not in a client area */
478 if (GetCursorPos(&ptCurPos
)) {
479 hwnd
= WindowFromPoint(ptCurPos
);
481 if (GetClientRect(hwnd
, &rcClient
)) {
482 ptTemp
.x
= rcClient
.left
;
483 ptTemp
.y
= rcClient
.top
;
484 if (ClientToScreen(hwnd
, &ptTemp
)) {
485 rcClient
.left
= ptTemp
.x
;
486 rcClient
.top
= ptTemp
.y
;
487 ptTemp
.x
= rcClient
.right
;
488 ptTemp
.y
= rcClient
.bottom
;
489 if (ClientToScreen(hwnd
, &ptTemp
)) {
490 rcClient
.right
= ptTemp
.x
;
491 rcClient
.bottom
= ptTemp
.y
;
492 if (!PtInRect(&rcClient
, ptCurPos
))
500 if (pCursor
== NULL
) {
501 if (pScreenPriv
->cursor
.visible
) {
502 if (!bInhibit
&& g_fSoftwareCursor
)
504 pScreenPriv
->cursor
.visible
= FALSE
;
508 if (pScreenPriv
->cursor
.handle
) {
511 DestroyCursor(pScreenPriv
->cursor
.handle
);
512 pScreenPriv
->cursor
.handle
= NULL
;
514 pScreenPriv
->cursor
.handle
=
515 winLoadCursor(pScreen
, pCursor
, pScreen
->myNum
);
516 WIN_DEBUG_MSG("winSetCursor: handle=%p\n", pScreenPriv
->cursor
.handle
);
519 SetCursor(pScreenPriv
->cursor
.handle
);
521 if (!pScreenPriv
->cursor
.visible
) {
522 if (!bInhibit
&& g_fSoftwareCursor
)
524 pScreenPriv
->cursor
.visible
= TRUE
;
531 * Move the cursor. This is a noop for us.
534 winMoveCursor(DeviceIntPtr pDev
, ScreenPtr pScreen
, int x
, int y
)
539 winDeviceCursorInitialize(DeviceIntPtr pDev
, ScreenPtr pScr
)
542 return pScreenPriv
->cursor
.spriteFuncs
->DeviceCursorInitialize(pDev
, pScr
);
546 winDeviceCursorCleanup(DeviceIntPtr pDev
, ScreenPtr pScr
)
549 pScreenPriv
->cursor
.spriteFuncs
->DeviceCursorCleanup(pDev
, pScr
);
552 static miPointerSpriteFuncRec winSpriteFuncsRec
= {
557 winDeviceCursorInitialize
,
558 winDeviceCursorCleanup
562 ===========================================================================
564 Other screen functions
566 ===========================================================================
570 * winCursorQueryBestSize
571 * Handle queries for best cursor size
574 winCursorQueryBestSize(int class, unsigned short *width
,
575 unsigned short *height
, ScreenPtr pScreen
)
577 winScreenPriv(pScreen
);
579 if (class == CursorShape
) {
580 *width
= pScreenPriv
->cursor
.sm_cx
;
581 *height
= pScreenPriv
->cursor
.sm_cy
;
584 if (pScreenPriv
->cursor
.QueryBestSize
)
585 (*pScreenPriv
->cursor
.QueryBestSize
) (class, width
, height
,
592 * Initialize cursor support
595 winInitCursor(ScreenPtr pScreen
)
597 winScreenPriv(pScreen
);
598 miPointerScreenPtr pPointPriv
;
600 /* override some screen procedures */
601 pScreenPriv
->cursor
.QueryBestSize
= pScreen
->QueryBestSize
;
602 pScreen
->QueryBestSize
= winCursorQueryBestSize
;
604 pPointPriv
= (miPointerScreenPtr
)
605 dixLookupPrivate(&pScreen
->devPrivates
, miPointerScreenKey
);
607 pScreenPriv
->cursor
.spriteFuncs
= pPointPriv
->spriteFuncs
;
608 pPointPriv
->spriteFuncs
= &winSpriteFuncsRec
;
610 pScreenPriv
->cursor
.handle
= NULL
;
611 pScreenPriv
->cursor
.visible
= FALSE
;
613 pScreenPriv
->cursor
.sm_cx
= GetSystemMetrics(SM_CXCURSOR
);
614 pScreenPriv
->cursor
.sm_cy
= GetSystemMetrics(SM_CYCURSOR
);