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