Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xwin / wincursor.c
CommitLineData
a09e091a
JB
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#ifdef XWIN_MULTIWINDOW
99 || pScreenPriv->pScreenInfo->fMultiWindow
100#endif
101 ) {
102 /* Get the client area coordinates */
103 GetClientRect(pScreenPriv->hwndScreen, &rcClient);
104
105 /* Translate the client area coords to screen coords */
106 MapWindowPoints(pScreenPriv->hwndScreen,
107 HWND_DESKTOP, (LPPOINT) &rcClient, 2);
108
109 /*
110 * Update the Windows cursor position so that we don't
111 * immediately warp back to the current position.
112 */
113 SetCursorPos(rcClient.left + x, rcClient.top + y);
114 }
115
116 /* Call the mi warp procedure to do the actual warping in X. */
117 miPointerWarpCursor(pDev, pScreen, x, y);
118}
119
120static Bool
121winCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y)
122{
123 return FALSE;
124}
125
126static void
127winCrossScreen(ScreenPtr pScreen, Bool fEntering)
128{
129}
130
131static unsigned char
132reverse(unsigned char c)
133{
134 int i;
135 unsigned char ret = 0;
136
137 for (i = 0; i < 8; ++i) {
138 ret |= ((c >> i) & 1) << (7 - i);
139 }
140 return ret;
141}
142
143/*
144 * Convert X cursor to Windows cursor
145 * FIXME: Perhaps there are more smart code
146 */
147static HCURSOR
148winLoadCursor(ScreenPtr pScreen, CursorPtr pCursor, int screen)
149{
150 winScreenPriv(pScreen);
151 HCURSOR hCursor = NULL;
152 unsigned char *pAnd;
153 unsigned char *pXor;
154 int nCX, nCY;
155 int nBytes;
156 double dForeY, dBackY;
157 BOOL fReverse;
158 HBITMAP hAnd, hXor;
159 ICONINFO ii;
160 unsigned char *pCur;
161 unsigned char bit;
162 HDC hDC;
163 BITMAPV4HEADER bi;
164 BITMAPINFO *pbmi;
165 uint32_t *lpBits;
166
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);
171
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;
177
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) {
181 winErrorFVerb(3,
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);
185 }
186
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 */
190 nBytes =
191 bits_to_bytes(pScreenPriv->cursor.sm_cx) * pScreenPriv->cursor.sm_cy;
192
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);
196
197 /* Allocate memory for the bitmaps */
198 pAnd = malloc(nBytes);
199 memset(pAnd, 0xFF, nBytes);
200 pXor = calloc(1, nBytes);
201
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);
206
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;
211
212 pAnd[nWinPix] = 0;
213 if (fReverse)
214 pXor[nWinPix] = reverse(~pCursor->bits->source[nXPix]);
215 else
216 pXor[nWinPix] = reverse(pCursor->bits->source[nXPix]);
217 }
218 }
219 else {
220 int x, y, xmax = bits_to_bytes(nCX);
221
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;
226
227 unsigned char mask = pCursor->bits->mask[nXPix];
228
229 pAnd[nWinPix] = reverse(~mask);
230 if (fReverse)
231 pXor[nWinPix] =
232 reverse(~pCursor->bits->source[nXPix] & mask);
233 else
234 pXor[nWinPix] =
235 reverse(pCursor->bits->source[nXPix] & mask);
236 }
237 }
238
239 /* prepare the pointers */
240 hCursor = NULL;
241 lpBits = NULL;
242
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 */
250 bi.bV4Planes = 1;
251 bi.bV4BitCount = 32;
252 bi.bV4V4Compression = BI_BITFIELDS;
253 bi.bV4RedMask = 0x00FF0000;
254 bi.bV4GreenMask = 0x0000FF00;
255 bi.bV4BlueMask = 0x000000FF;
256 bi.bV4AlphaMask = 0xFF000000;
257
258 lpBits =
259 (uint32_t *) calloc(pScreenPriv->cursor.sm_cx *
260 pScreenPriv->cursor.sm_cy,
261 sizeof(uint32_t));
262
263 if (lpBits) {
264 int y;
265 for (y = 0; y < nCY; y++) {
266 void *src, *dst;
267 src = &(pCursor->bits->argb[y * pCursor->bits->width]);
268 dst = &(lpBits[y * pScreenPriv->cursor.sm_cx]);
269 memcpy(dst, src, 4 * nCX);
270 }
271 }
272 } /* End if-truecolor-icon */
273
274 if (!lpBits) {
275 RGBQUAD *pbmiColors;
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]);
280
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;
291
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;
304
305 lpBits =
306 (uint32_t *) calloc(pScreenPriv->cursor.sm_cx *
307 pScreenPriv->cursor.sm_cy, sizeof(char));
308
309 pCur = (unsigned char *) lpBits;
310 if (lpBits) {
311 int x, y;
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 */
315 (*pCur++) = 0;
316 else { /* Within X11 icon bounds */
317
318 int nWinPix =
319 bits_to_bytes(pScreenPriv->cursor.sm_cx) * y +
320 (x / 8);
321
322 bit = pAnd[nWinPix];
323 bit = bit & (1 << (7 - (x & 7)));
324 if (!bit) { /* Within the cursor mask? */
325 int nXPix =
326 BitmapBytePad(pCursor->bits->width) * y +
327 (x / 8);
328 bit =
329 ~reverse(~pCursor->bits->
330 source[nXPix] & pCursor->bits->
331 mask[nXPix]);
332 bit = bit & (1 << (7 - (x & 7)));
333 if (bit) /* Draw foreground */
334 (*pCur++) = 2;
335 else /* Draw background */
336 (*pCur++) = 1;
337 }
338 else /* Outside the cursor mask */
339 (*pCur++) = 0;
340 }
341 } /* end for (x) */
342 } /* end for (y) */
343 } /* end if (lpbits) */
344 }
345
346 /* If one of the previous two methods gave us the bitmap we need, make a cursor */
347 if (lpBits) {
348 WIN_DEBUG_MSG("winLoadCursor: Creating bitmap cursor: hotspot %d,%d\n",
349 pCursor->bits->xhot, pCursor->bits->yhot);
350
351 hAnd = NULL;
352 hXor = NULL;
353
354 hAnd =
355 CreateBitmap(pScreenPriv->cursor.sm_cx, pScreenPriv->cursor.sm_cy,
356 1, 1, pAnd);
357
358 hDC = GetDC(NULL);
359 if (hDC) {
360 hXor =
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);
366 }
367 free(lpBits);
368
369 if (hAnd && hXor) {
370 ii.fIcon = FALSE;
371 ii.xHotspot = pCursor->bits->xhot;
372 ii.yHotspot = pCursor->bits->yhot;
373 ii.hbmMask = hAnd;
374 ii.hbmColor = hXor;
375 hCursor = (HCURSOR) CreateIconIndirect(&ii);
376
377 if (hCursor == NULL)
378 winW32Error(2, "winLoadCursor - CreateIconIndirect failed:");
379 else {
380 if (GetIconInfo(hCursor, &ii)) {
381 if (ii.fIcon) {
382 WIN_DEBUG_MSG
383 ("winLoadCursor: CreateIconIndirect returned no cursor. Trying again.\n");
384
385 DestroyCursor(hCursor);
386
387 ii.fIcon = FALSE;
388 ii.xHotspot = pCursor->bits->xhot;
389 ii.yHotspot = pCursor->bits->yhot;
390 hCursor = (HCURSOR) CreateIconIndirect(&ii);
391
392 if (hCursor == NULL)
393 winW32Error(2,
394 "winLoadCursor - CreateIconIndirect failed:");
395 }
396 /* GetIconInfo creates new bitmaps. Destroy them again */
397 if (ii.hbmMask)
398 DeleteObject(ii.hbmMask);
399 if (ii.hbmColor)
400 DeleteObject(ii.hbmColor);
401 }
402 }
403 }
404
405 if (hAnd)
406 DeleteObject(hAnd);
407 if (hXor)
408 DeleteObject(hXor);
409 }
410
411 if (!hCursor) {
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);
418 if (hCursor == NULL)
419 winW32Error(2, "winLoadCursor - CreateCursor failed:");
420 }
421 free(pAnd);
422 free(pXor);
423
424 return hCursor;
425}
426
427/*
428===========================================================================
429
430 Pointer sprite functions
431
432===========================================================================
433*/
434
435/*
436 * winRealizeCursor
437 * Convert the X cursor representation to native format if possible.
438 */
439static Bool
440winRealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
441{
442 if (pCursor == NULL || pCursor->bits == NULL)
443 return FALSE;
444
445 /* FIXME: cache ARGB8888 representation? */
446
447 return TRUE;
448}
449
450/*
451 * winUnrealizeCursor
452 * Free the storage space associated with a realized cursor.
453 */
454static Bool
455winUnrealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
456{
457 return TRUE;
458}
459
460/*
461 * winSetCursor
462 * Set the cursor sprite and position.
463 */
464static void
465winSetCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor, int x,
466 int y)
467{
468 POINT ptCurPos, ptTemp;
469 HWND hwnd;
470 RECT rcClient;
471 BOOL bInhibit;
472
473 winScreenPriv(pScreen);
474 WIN_DEBUG_MSG("winSetCursor: cursor=%p\n", pCursor);
475
476 /* Inhibit changing the cursor if the mouse is not in a client area */
477 bInhibit = FALSE;
478 if (GetCursorPos(&ptCurPos)) {
479 hwnd = WindowFromPoint(ptCurPos);
480 if (hwnd) {
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))
493 bInhibit = TRUE;
494 }
495 }
496 }
497 }
498 }
499
500 if (pCursor == NULL) {
501 if (pScreenPriv->cursor.visible) {
502 if (!bInhibit && g_fSoftwareCursor)
503 ShowCursor(FALSE);
504 pScreenPriv->cursor.visible = FALSE;
505 }
506 }
507 else {
508 if (pScreenPriv->cursor.handle) {
509 if (!bInhibit)
510 SetCursor(NULL);
511 DestroyCursor(pScreenPriv->cursor.handle);
512 pScreenPriv->cursor.handle = NULL;
513 }
514 pScreenPriv->cursor.handle =
515 winLoadCursor(pScreen, pCursor, pScreen->myNum);
516 WIN_DEBUG_MSG("winSetCursor: handle=%p\n", pScreenPriv->cursor.handle);
517
518 if (!bInhibit)
519 SetCursor(pScreenPriv->cursor.handle);
520
521 if (!pScreenPriv->cursor.visible) {
522 if (!bInhibit && g_fSoftwareCursor)
523 ShowCursor(TRUE);
524 pScreenPriv->cursor.visible = TRUE;
525 }
526 }
527}
528
529/*
530 * winMoveCursor
531 * Move the cursor. This is a noop for us.
532 */
533static void
534winMoveCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
535{
536}
537
538static Bool
539winDeviceCursorInitialize(DeviceIntPtr pDev, ScreenPtr pScr)
540{
541 winScreenPriv(pScr);
542 return pScreenPriv->cursor.spriteFuncs->DeviceCursorInitialize(pDev, pScr);
543}
544
545static void
546winDeviceCursorCleanup(DeviceIntPtr pDev, ScreenPtr pScr)
547{
548 winScreenPriv(pScr);
549 pScreenPriv->cursor.spriteFuncs->DeviceCursorCleanup(pDev, pScr);
550}
551
552static miPointerSpriteFuncRec winSpriteFuncsRec = {
553 winRealizeCursor,
554 winUnrealizeCursor,
555 winSetCursor,
556 winMoveCursor,
557 winDeviceCursorInitialize,
558 winDeviceCursorCleanup
559};
560
561/*
562===========================================================================
563
564 Other screen functions
565
566===========================================================================
567*/
568
569/*
570 * winCursorQueryBestSize
571 * Handle queries for best cursor size
572 */
573static void
574winCursorQueryBestSize(int class, unsigned short *width,
575 unsigned short *height, ScreenPtr pScreen)
576{
577 winScreenPriv(pScreen);
578
579 if (class == CursorShape) {
580 *width = pScreenPriv->cursor.sm_cx;
581 *height = pScreenPriv->cursor.sm_cy;
582 }
583 else {
584 if (pScreenPriv->cursor.QueryBestSize)
585 (*pScreenPriv->cursor.QueryBestSize) (class, width, height,
586 pScreen);
587 }
588}
589
590/*
591 * winInitCursor
592 * Initialize cursor support
593 */
594Bool
595winInitCursor(ScreenPtr pScreen)
596{
597 winScreenPriv(pScreen);
598 miPointerScreenPtr pPointPriv;
599
600 /* override some screen procedures */
601 pScreenPriv->cursor.QueryBestSize = pScreen->QueryBestSize;
602 pScreen->QueryBestSize = winCursorQueryBestSize;
603
604 pPointPriv = (miPointerScreenPtr)
605 dixLookupPrivate(&pScreen->devPrivates, miPointerScreenKey);
606
607 pScreenPriv->cursor.spriteFuncs = pPointPriv->spriteFuncs;
608 pPointPriv->spriteFuncs = &winSpriteFuncsRec;
609
610 pScreenPriv->cursor.handle = NULL;
611 pScreenPriv->cursor.visible = FALSE;
612
613 pScreenPriv->cursor.sm_cx = GetSystemMetrics(SM_CXCURSOR);
614 pScreenPriv->cursor.sm_cy = GetSystemMetrics(SM_CYCURSOR);
615
616 return TRUE;
617}