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