1/* **********************************************************
2 * Copyright (C) 1998-2001 VMware, Inc.
3 * All Rights Reserved
4 * **********************************************************/
5#ifdef VMX86_DEVEL
6char rcsId_vmwarecurs[] =
7    "Id: vmwarecurs.c,v 1.5 2001/01/30 23:33:02 bennett Exp $";
8#endif
9
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
14#include "vmware.h"
15#include "vmware_common.h"
16#include "bits2pixels.h"
17
18static void VMWAREGetImage(DrawablePtr src, int x, int y, int w, int h,
19                           unsigned int format, unsigned long planeMask,
20                           char *pBinImage);
21static void VMWARECopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg,
22                             RegionPtr prgnSrc);
23
24#ifdef RENDER
25static void VMWAREComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask,
26			    PicturePtr pDst, INT16 xSrc, INT16 ySrc,
27			    INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst,
28			    CARD16 width, CARD16 height);
29#endif /* RENDER */
30
31static void
32RedefineCursor(VMWAREPtr pVMWARE)
33{
34    int i;
35
36    VmwareLog(("RedefineCursor\n"));
37
38    pVMWARE->cursorDefined = FALSE;
39
40    /* Define cursor */
41    vmwareWriteWordToFIFO(pVMWARE, SVGA_CMD_DEFINE_CURSOR);
42    vmwareWriteWordToFIFO(pVMWARE, MOUSE_ID);
43    vmwareWriteWordToFIFO(pVMWARE, pVMWARE->hwcur.hotX);
44    vmwareWriteWordToFIFO(pVMWARE, pVMWARE->hwcur.hotY);
45    vmwareWriteWordToFIFO(pVMWARE, pVMWARE->CursorInfoRec->MaxWidth);
46    vmwareWriteWordToFIFO(pVMWARE, pVMWARE->CursorInfoRec->MaxHeight);
47    vmwareWriteWordToFIFO(pVMWARE, 1);
48    vmwareWriteWordToFIFO(pVMWARE, pVMWARE->bitsPerPixel);
49
50    /*
51     * Since we have AND and XOR masks rather than 'source' and 'mask',
52     * color expand 'mask' with all zero as its foreground and all one as
53     * its background.  This makes 'image & 0 ^ 'source' = source.  We
54     * arange for 'image' & 1 ^ 'source' = 'image' below when we clip
55     * 'source' below.
56     */
57    vmwareRaster_BitsToPixels((uint8 *) pVMWARE->hwcur.mask,
58                        SVGA_BITMAP_INCREMENT(pVMWARE->CursorInfoRec->MaxWidth),
59                        (uint8 *) pVMWARE->hwcur.maskPixmap,
60                        SVGA_PIXMAP_INCREMENT(pVMWARE->CursorInfoRec->MaxWidth,
61                                              pVMWARE->bitsPerPixel),
62                        pVMWARE->bitsPerPixel / 8,
63                        pVMWARE->CursorInfoRec->MaxWidth,
64                        pVMWARE->CursorInfoRec->MaxHeight, 0, ~0);
65    for (i = 0; i < SVGA_BITMAP_SIZE(pVMWARE->CursorInfoRec->MaxWidth,
66                                     pVMWARE->CursorInfoRec->MaxHeight); i++) {
67        vmwareWriteWordToFIFO(pVMWARE, ~pVMWARE->hwcur.mask[i]);
68    }
69
70    vmwareRaster_BitsToPixels((uint8 *) pVMWARE->hwcur.source,
71                        SVGA_BITMAP_INCREMENT(pVMWARE->CursorInfoRec->MaxWidth),
72                        (uint8 *) pVMWARE->hwcur.sourcePixmap,
73                        SVGA_PIXMAP_INCREMENT(pVMWARE->CursorInfoRec->MaxWidth,
74                                              pVMWARE->bitsPerPixel),
75                        pVMWARE->bitsPerPixel / 8,
76                        pVMWARE->CursorInfoRec->MaxWidth,
77                        pVMWARE->CursorInfoRec->MaxHeight,
78                        pVMWARE->hwcur.fg, pVMWARE->hwcur.bg);
79    /*
80     * As pointed out above, we need to clip the expanded 'source' against
81     * the expanded 'mask' since we actually have AND and XOR masks in the
82     * virtual hardware.  Effectively, 'source' becomes a three color fg/bg/0
83     * pixmap that XORs appropriately.
84     */
85    for (i = 0; i < SVGA_PIXMAP_SIZE(pVMWARE->CursorInfoRec->MaxWidth,
86                                     pVMWARE->CursorInfoRec->MaxHeight,
87                                     pVMWARE->bitsPerPixel); i++) {
88        pVMWARE->hwcur.sourcePixmap[i] &= ~pVMWARE->hwcur.maskPixmap[i];
89	vmwareWriteWordToFIFO(pVMWARE, pVMWARE->hwcur.sourcePixmap[i]);
90    }
91
92    /* Sync the FIFO, so that the definition precedes any use of the cursor */
93    vmwareWaitForFB(pVMWARE);
94    pVMWARE->cursorDefined = TRUE;
95}
96
97static void
98vmwareSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg)
99{
100    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
101    TRACEPOINT
102
103    if (pVMWARE->hwcur.fg != fg || pVMWARE->hwcur.bg != bg) {
104        VmwareLog(("SetCursorColors(0x%08x, 0x%08x)\n", bg, fg));
105        pVMWARE->hwcur.fg = fg;
106        pVMWARE->hwcur.bg = bg;
107        RedefineCursor(pVMWARE);
108    }
109}
110
111static Bool
112vmwareUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs)
113{
114    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
115    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
116    VmwareLog(("UseHWCursor new cursor %p refcnt %i old cursor %p refcnt %i\n",
117              pCurs, pCurs->refcnt, pVMWARE->oldCurs, pVMWARE->oldCurs ? pVMWARE->oldCurs->refcnt : 0));
118    pCurs->refcnt++;
119    if (pVMWARE->oldCurs)
120       FreeCursor(pVMWARE->oldCurs, None);
121    pVMWARE->oldCurs = pCurs;
122
123    pVMWARE->hwcur.hotX = pCurs->bits->xhot;
124    pVMWARE->hwcur.hotY = pCurs->bits->yhot;
125
126    return pScrn->bitsPerPixel > 8;
127}
128
129static void
130vmwareLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *src )
131{
132    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
133    const int imageSize = SVGA_BITMAP_SIZE(pVMWARE->CursorInfoRec->MaxWidth,
134                                           pVMWARE->CursorInfoRec->MaxHeight);
135    TRACEPOINT
136
137    memcpy(pVMWARE->hwcur.source, src, imageSize * sizeof(uint32));
138    memcpy(pVMWARE->hwcur.mask,
139           src + imageSize * sizeof(uint32), imageSize * sizeof(uint32));
140    RedefineCursor(pVMWARE);
141}
142
143#ifdef ARGB_CURSOR
144#include "cursorstr.h"
145
146static Bool
147vmwareUseHWCursorARGB(ScreenPtr pScreen, CursorPtr pCurs)
148{
149    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
150    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
151    VmwareLog(("UseHWCursorARGB new cursor %p refcnt %i old cursor %p refcnt %i\n",
152              pCurs, pCurs->refcnt, pVMWARE->oldCurs, pVMWARE->oldCurs ? pVMWARE->oldCurs->refcnt : 0));
153    pCurs->refcnt++;
154    if (pVMWARE->oldCurs)
155       FreeCursor(pVMWARE->oldCurs, None);
156    pVMWARE->oldCurs = pCurs;
157
158    return pCurs->bits->height <= MAX_CURS &&
159           pCurs->bits->width <= MAX_CURS &&
160           pScrn->bitsPerPixel > 8;
161}
162
163static void
164vmwareLoadCursorARGB(ScrnInfoPtr pScrn, CursorPtr pCurs)
165{
166    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
167    CARD32 width = pCurs->bits->width;
168    CARD32 height = pCurs->bits->height;
169    CARD32* image = pCurs->bits->argb;
170    CARD32* imageEnd = image + (width * height);
171
172    pVMWARE->cursorDefined = FALSE;
173
174    pVMWARE->hwcur.hotX = pCurs->bits->xhot;
175    pVMWARE->hwcur.hotY = pCurs->bits->yhot;
176
177    vmwareWriteWordToFIFO(pVMWARE, SVGA_CMD_DEFINE_ALPHA_CURSOR);
178    vmwareWriteWordToFIFO(pVMWARE, MOUSE_ID);
179    vmwareWriteWordToFIFO(pVMWARE, pCurs->bits->xhot);
180    vmwareWriteWordToFIFO(pVMWARE, pCurs->bits->yhot);
181    vmwareWriteWordToFIFO(pVMWARE, width);
182    vmwareWriteWordToFIFO(pVMWARE, height);
183
184    while (image != imageEnd) {
185        vmwareWriteWordToFIFO(pVMWARE, *image++);
186    }
187
188    vmwareWaitForFB(pVMWARE);
189    pVMWARE->cursorDefined = TRUE;
190}
191#endif
192
193void
194vmwareWriteCursorRegs(VMWAREPtr pVMWARE, Bool visible, Bool force)
195{
196    int enableVal;
197
198    vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_ID, MOUSE_ID);
199    if (visible) {
200        vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_X,
201                       pVMWARE->hwcur.x + pVMWARE->hwcur.hotX);
202        vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_Y,
203                       pVMWARE->hwcur.y + pVMWARE->hwcur.hotY);
204    }
205
206    if (force) {
207        enableVal = visible ? SVGA_CURSOR_ON_SHOW : SVGA_CURSOR_ON_HIDE;
208    } else {
209        enableVal = visible ? pVMWARE->cursorRestoreToFB :
210            pVMWARE->cursorRemoveFromFB;
211    }
212    vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_ON, enableVal);
213}
214
215/* disabled by default to reduce spew in DEBUG_LOGGING mode. */
216/* #define DEBUG_LOG_MOUSE_HIDE_SHOW */
217
218static void
219vmwareShowCursor(ScrnInfoPtr pScrn)
220{
221    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
222#ifdef DEBUG_LOG_MOUSE_HIDE_SHOW
223    VmwareLog(("Show: %d %d %d\n", pVMWARE->cursorSema, pVMWARE->cursorDefined,
224	       pVMWARE->cursorShouldBeHidden));
225#endif
226    pVMWARE->cursorShouldBeHidden = FALSE;
227    if (pVMWARE->cursorSema == 0 && pVMWARE->cursorDefined) {
228        vmwareWriteCursorRegs(pVMWARE, TRUE, TRUE);
229    }
230}
231
232static void
233vmwareHideCursor(ScrnInfoPtr pScrn)
234{
235    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
236#ifdef DEBUG_LOG_MOUSE_HIDE_SHOW
237    VmwareLog(("Hide: %d %d %d\n", pVMWARE->cursorSema, pVMWARE->cursorDefined,
238	       pVMWARE->cursorShouldBeHidden));
239#endif
240    if (pVMWARE->cursorDefined) {
241        vmwareWriteCursorRegs(pVMWARE, FALSE, TRUE);
242    }
243    pVMWARE->cursorShouldBeHidden = TRUE;
244}
245
246/* disabled by default to reduce spew in DEBUG_LOGGING mode. */
247/* #define DEBUG_LOG_MOUSE_MOVE */
248
249static void
250vmwareSetCursorPosition(ScrnInfoPtr pScrn, int x, int y)
251{
252    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
253#ifdef DEBUG_LOG_MOUSE_MOVE
254    VmwareLog(("Move: %d %d %d\n", pVMWARE->cursorSema, pVMWARE->cursorDefined,
255	       pVMWARE->cursorShouldBeHidden));
256#endif
257    /*
258     * We're bad people.  We have no concept of a frame (VMWAREAdjustFrame()
259     * is a NOP).  The hwcursor code expects us to be frame aware though, so
260     * we have to do this.  I'm open to suggestions.  I tried not even
261     * hooking AdjustFrame and it didn't help.
262     */
263    pVMWARE->hwcur.x = x + pScrn->frameX0;
264    pVMWARE->hwcur.y = y + pScrn->frameY0;
265    pVMWARE->hwcur.box.x1 = pVMWARE->hwcur.x;
266    pVMWARE->hwcur.box.x2 = pVMWARE->hwcur.x + pVMWARE->CursorInfoRec->MaxWidth;
267    pVMWARE->hwcur.box.y1 = pVMWARE->hwcur.y;
268    pVMWARE->hwcur.box.y2 = pVMWARE->hwcur.y + pVMWARE->CursorInfoRec->MaxHeight;
269
270    vmwareShowCursor(pScrn);
271}
272
273void
274vmwareCursorModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
275{
276    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
277
278    if (pVMWARE->cursorDefined) {
279        vmwareWriteCursorRegs(pVMWARE, !pVMWARE->cursorShouldBeHidden, TRUE);
280    }
281}
282
283Bool
284vmwareCursorInit(ScreenPtr pScreen)
285{
286    xf86CursorInfoPtr infoPtr;
287    VMWAREPtr pVMWARE = VMWAREPTR(xf86ScreenToScrn(pScreen));
288    Bool ret;
289
290    TRACEPOINT
291
292    /* Require cursor bypass for hwcursor.  Ignore deprecated FIFO hwcursor */
293    if (!(pVMWARE->vmwareCapability & SVGA_CAP_CURSOR_BYPASS)) {
294        return FALSE;
295    }
296
297    infoPtr = xf86CreateCursorInfoRec();
298    if (!infoPtr)
299        return FALSE;
300
301    pVMWARE->CursorInfoRec = infoPtr;
302    pVMWARE->oldCurs = NULL;
303
304    infoPtr->MaxWidth = MAX_CURS;
305    infoPtr->MaxHeight = MAX_CURS;
306    infoPtr->Flags = HARDWARE_CURSOR_BIT_ORDER_MSBFIRST |
307                     HARDWARE_CURSOR_UPDATE_UNHIDDEN |
308                     HARDWARE_CURSOR_SOURCE_MASK_NOT_INTERLEAVED;
309    infoPtr->SetCursorColors = vmwareSetCursorColors;
310    infoPtr->SetCursorPosition = vmwareSetCursorPosition;
311    infoPtr->LoadCursorImage = vmwareLoadCursorImage;
312    infoPtr->HideCursor = vmwareHideCursor;
313    infoPtr->ShowCursor = vmwareShowCursor;
314    infoPtr->UseHWCursor = vmwareUseHWCursor;
315
316#ifdef ARGB_CURSOR
317    if (pVMWARE->vmwareCapability & SVGA_CAP_ALPHA_CURSOR) {
318        infoPtr->UseHWCursorARGB = vmwareUseHWCursorARGB;
319        infoPtr->LoadCursorARGB = vmwareLoadCursorARGB;
320    }
321#endif
322
323    ret = xf86InitCursor(pScreen, infoPtr);
324    if (!ret) {
325        xf86DestroyCursorInfoRec(infoPtr);
326        pVMWARE->CursorInfoRec = NULL;
327    }
328    return ret;
329}
330
331void
332vmwareCursorCloseScreen(ScreenPtr pScreen)
333{
334    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
335    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
336#ifdef RENDER
337    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
338#endif
339
340    pScreen->GetImage = pVMWARE->ScrnFuncs.GetImage;
341    pScreen->CopyWindow = pVMWARE->ScrnFuncs.CopyWindow;
342#ifdef RENDER
343    if (ps) {
344        ps->Composite = pVMWARE->Composite;
345    }
346#endif /* RENDER */
347
348    vmwareHideCursor(pScrn);
349    if (pVMWARE->oldCurs)
350       FreeCursor(pVMWARE->oldCurs, None);
351    pVMWARE->oldCurs = NULL;
352    xf86DestroyCursorInfoRec(pVMWARE->CursorInfoRec);
353}
354
355/***  Wrap functions that read from the framebuffer ***/
356
357void
358vmwareCursorHookWrappers(ScreenPtr pScreen)
359{
360    VMWAREPtr pVMWARE = VMWAREPTR(xf86ScreenToScrn(pScreen));
361#ifdef RENDER
362    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
363#endif
364
365    TRACEPOINT
366
367    pVMWARE->ScrnFuncs.GetImage = pScreen->GetImage;
368    pVMWARE->ScrnFuncs.CopyWindow = pScreen->CopyWindow;
369    pScreen->GetImage = VMWAREGetImage;
370    pScreen->CopyWindow = VMWARECopyWindow;
371
372#ifdef RENDER
373    if (ps) {
374        pVMWARE->Composite = ps->Composite;
375        ps->Composite = VMWAREComposite;
376    }
377#endif /* RENDER */
378
379}
380
381static void
382VMWAREGetImage(DrawablePtr src, int x, int y, int w, int h,
383               unsigned int format, unsigned long planeMask, char *pBinImage)
384{
385    ScreenPtr pScreen = src->pScreen;
386    VMWAREPtr pVMWARE = VMWAREPTR(xf86ScreenToScrn(src->pScreen));
387    BoxRec box;
388    Bool hidden = FALSE;
389
390    VmwareLog(("VMWAREGetImage(%p, %d, %d, %d, %d, %d, %d, %p)\n",
391               src, x, y, w, h, format, planeMask, pBinImage));
392
393    box.x1 = src->x + x;
394    box.y1 = src->y + y;
395    box.x2 = box.x1 + w;
396    box.y2 = box.y1 + h;
397
398    if (BOX_INTERSECT(box, pVMWARE->hwcur.box)) {
399        PRE_OP_HIDE_CURSOR();
400        hidden = TRUE;
401    }
402
403    pScreen->GetImage = pVMWARE->ScrnFuncs.GetImage;
404    (*pScreen->GetImage)(src, x, y, w, h, format, planeMask, pBinImage);
405    pScreen->GetImage = VMWAREGetImage;
406
407    if (hidden) {
408        POST_OP_SHOW_CURSOR();
409    }
410}
411
412static void
413VMWARECopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
414{
415    ScreenPtr pScreen = pWin->drawable.pScreen;
416    VMWAREPtr pVMWARE = VMWAREPTR(xf86ScreenToScrn(pWin->drawable.pScreen));
417    BoxPtr pBB;
418    Bool hidden = FALSE;
419
420    /*
421     * We only worry about the source region here, since shadowfb will
422     * take care of the destination region.
423     */
424    pBB = REGION_EXTENTS(pWin->drawable.pScreen, prgnSrc);
425
426    VmwareLog(("VMWARECopyWindow(%p, (%d, %d), (%d, %d - %d, %d)\n",
427               pWin, ptOldOrg.x, ptOldOrg.y,
428               pBB->x1, pBB->y1, pBB->x2, pBB->y2));
429
430    if (BOX_INTERSECT(*pBB, pVMWARE->hwcur.box)) {
431        PRE_OP_HIDE_CURSOR();
432        hidden = TRUE;
433    }
434
435    pScreen->CopyWindow = pVMWARE->ScrnFuncs.CopyWindow;
436    (*pScreen->CopyWindow)(pWin, ptOldOrg, prgnSrc);
437    pScreen->CopyWindow = VMWARECopyWindow;
438
439    if (hidden) {
440        POST_OP_SHOW_CURSOR();
441    }
442}
443
444#ifdef RENDER
445static void
446VMWAREComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask,
447		PicturePtr pDst, INT16 xSrc, INT16 ySrc,
448		INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst,
449		CARD16 width, CARD16 height)
450{
451    ScreenPtr pScreen = pDst->pDrawable->pScreen;
452    VMWAREPtr pVMWARE = VMWAREPTR(xf86ScreenToScrn(pScreen));
453    PictureScreenPtr ps = GetPictureScreen(pScreen);
454    BoxRec box;
455    Bool hidden = FALSE;
456
457    if (pSrc->pDrawable) {
458        VmwareLog(("VMWAREComposite op = %d, pSrc = %p, pMask = %p, pDst = %p,"
459                   " src = (%d, %d), mask = (%d, %d), dst = (%d, %d), w = %d,"
460                   " h = %d\n", op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask,
461                   xDst, yDst, width, height));
462
463        /*
464         * We only worry about the source region here, since shadowfb or XAA
465         * will take care of the destination region.
466         */
467        box.x1 = pSrc->pDrawable->x + xSrc;
468        box.y1 = pSrc->pDrawable->y + ySrc;
469        box.x2 = box.x1 + width;
470        box.y2 = box.y1 + height;
471
472        if (BOX_INTERSECT(box, pVMWARE->hwcur.box)) {
473            PRE_OP_HIDE_CURSOR();
474            hidden = TRUE;
475        }
476    }
477
478    ps->Composite = pVMWARE->Composite;
479    (*ps->Composite)(op, pSrc, pMask, pDst, xSrc, ySrc,
480		     xMask, yMask, xDst, yDst, width, height);
481    ps->Composite = VMWAREComposite;
482
483    if (hidden) {
484        POST_OP_SHOW_CURSOR();
485    }
486}
487#endif /* RENDER */
488