pointer.c revision e07dc26b
1/* $Id: pointer.c,v 1.1.1.1 2019/01/09 23:50:31 mrg Exp $ */
2/** @file
3 * VirtualBox X11 Additions graphics driver utility functions
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#ifndef PCIACCESS
29# include "xf86Pci.h"
30# include <Pci.h>
31#endif
32
33#include "xf86.h"
34#define NEED_XF86_TYPES
35#include "compiler.h"
36#include "cursorstr.h"
37#include "servermd.h"
38
39#include "vboxvideo.h"
40
41#ifdef XORG_7X
42# include <stdlib.h>
43# include <string.h>
44#endif
45
46#define VBOX_MAX_CURSOR_WIDTH 64
47#define VBOX_MAX_CURSOR_HEIGHT 64
48
49/**************************************************************************
50* Debugging functions and macros                                          *
51**************************************************************************/
52
53/* #define DEBUG_POINTER */
54
55#ifdef DEBUG
56# define PUT_PIXEL(c) ErrorF ("%c", c)
57#else /* DEBUG_VIDEO not defined */
58# define PUT_PIXEL(c) do { } while(0)
59#endif /* DEBUG_VIDEO not defined */
60
61/** Macro to printf an error message and return from a function */
62#define RETERROR(scrnIndex, RetVal, ...) \
63    do \
64    { \
65        xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
66        return RetVal; \
67    } \
68    while (0)
69
70/** Structure to pass cursor image data between realise_cursor() and
71 * load_cursor_image().  The members match the parameters to
72 * @a VBoxHGSMIUpdatePointerShape(). */
73struct vboxCursorImage
74{
75    uint32_t fFlags;
76    uint32_t cHotX;
77    uint32_t cHotY;
78    uint32_t cWidth;
79    uint32_t cHeight;
80    uint8_t *pPixels;
81    uint32_t cbLength;
82};
83
84#ifdef DEBUG_POINTER
85static void
86vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image)
87{
88    size_t x, y;
89    unsigned short pitch;
90    CARD32 *color;
91    unsigned char *mask;
92    size_t sizeMask;
93
94    image    += sizeof(struct vboxCursorImage);
95    mask      = image;
96    pitch     = (w + 7) / 8;
97    sizeMask  = (pitch * h + 3) & ~3;
98    color     = (CARD32 *)(image + sizeMask);
99
100    TRACE_ENTRY();
101    for (y = 0; y < h; ++y, mask += pitch, color += w)
102    {
103        for (x = 0; x < w; ++x)
104        {
105            if (mask[x / 8] & (1 << (7 - (x % 8))))
106                ErrorF (" ");
107            else
108            {
109                CARD32 c = color[x];
110                if (c == bg)
111                    ErrorF("Y");
112                else
113                    ErrorF("X");
114            }
115        }
116        ErrorF("\n");
117    }
118}
119#endif
120
121/**************************************************************************
122* Main functions                                                          *
123**************************************************************************/
124
125void vbvxCursorTerm(VBOXPtr pVBox)
126{
127    TRACE_ENTRY();
128
129    xf86DestroyCursorInfoRec(pVBox->pCurs);
130    pVBox->pCurs = NULL;
131    TRACE_EXIT();
132}
133
134static void
135vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
136{
137    int rc;
138    RT_NOREF(pScrn);
139
140    rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, 0, 0, 0, 0, 0, NULL, 0);
141    AssertMsg(rc == VINF_SUCCESS, ("Could not hide the virtual mouse pointer, VBox error %d.\n", rc));
142}
143
144static void
145vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
146{
147    int rc;
148    RT_NOREF(pScrn);
149
150    if (!pVBox->fUseHardwareCursor)
151        return;
152    rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, VBOX_MOUSE_POINTER_VISIBLE,
153                                     0, 0, 0, 0, NULL, 0);
154    AssertMsg(rc == VINF_SUCCESS, ("Could not unhide the virtual mouse pointer.\n"));
155}
156
157static void
158vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
159                           unsigned char *pvImage)
160{
161    int rc;
162    struct vboxCursorImage *pImage;
163    pImage = (struct vboxCursorImage *)pvImage;
164    RT_NOREF(pScrn);
165
166#ifdef DEBUG_POINTER
167    vbox_show_shape(pImage->cWidth, pImage->cHeight, 0, pvImage);
168#endif
169
170    rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, pImage->fFlags,
171             pImage->cHotX, pImage->cHotY, pImage->cWidth, pImage->cHeight,
172             pImage->pPixels, pImage->cbLength);
173    AssertMsg(rc == VINF_SUCCESS, ("Unable to set the virtual mouse pointer image.\n"));
174}
175
176static void
177vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
178{
179    RT_NOREF(pScrn);
180    RT_NOREF(bg);
181    RT_NOREF(fg);
182    /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
183}
184
185
186static void
187vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
188{
189    VBOXPtr pVBox = pScrn->driverPrivate;
190
191    /* This currently does nothing. */
192    VBoxHGSMICursorPosition(&pVBox->guestCtx, true, x, y, NULL, NULL);
193}
194
195static void
196vbox_hide_cursor(ScrnInfoPtr pScrn)
197{
198    VBOXPtr pVBox = pScrn->driverPrivate;
199
200    vbox_vmm_hide_cursor(pScrn, pVBox);
201}
202
203static void
204vbox_show_cursor(ScrnInfoPtr pScrn)
205{
206    VBOXPtr pVBox = pScrn->driverPrivate;
207
208    vbox_vmm_show_cursor(pScrn, pVBox);
209}
210
211static void
212vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image)
213{
214    VBOXPtr pVBox = pScrn->driverPrivate;
215
216    vbox_vmm_load_cursor_image(pScrn, pVBox, image);
217}
218
219static Bool
220vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
221{
222    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
223    VBOXPtr pVBox = pScrn->driverPrivate;
224    RT_NOREF(pCurs);
225    return pVBox->fUseHardwareCursor;
226}
227
228static unsigned char
229color_to_byte(unsigned c)
230{
231    return (c >> 8) & 0xff;
232}
233
234static unsigned char *
235vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
236{
237    VBOXPtr pVBox;
238    CursorBitsPtr bitsp;
239    unsigned short w, h, x, y;
240    unsigned char *c, *p, *pm, *ps, *m;
241    size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
242    CARD32 fc, bc, *cp;
243    int scrnIndex = infoPtr->pScrn->scrnIndex;
244    struct vboxCursorImage *pImage;
245
246    pVBox = infoPtr->pScrn->driverPrivate;
247    bitsp = pCurs->bits;
248    w = bitsp->width;
249    h = bitsp->height;
250
251    if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
252        RETERROR(scrnIndex, NULL,
253            "Error invalid cursor dimensions %dx%d\n", w, h);
254
255    if ((bitsp->xhot > w) || (bitsp->yhot > h))
256        RETERROR(scrnIndex, NULL,
257            "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
258            bitsp->xhot, bitsp->yhot, w, h);
259
260    srcPitch = PixmapBytePad (bitsp->width, 1);
261    dstPitch = (w + 7) / 8;
262    sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
263    sizeRgba = w * h * 4;
264    sizeRequest = sizeMask + sizeRgba + sizeof(*pImage);
265
266    p = c = calloc (1, sizeRequest);
267    if (!c)
268        RETERROR(scrnIndex, NULL,
269                 "Error failed to alloc %lu bytes for cursor\n",
270                 (unsigned long) sizeRequest);
271
272    pImage = (struct vboxCursorImage *)p;
273    pImage->pPixels = m = p + sizeof(*pImage);
274    cp = (CARD32 *)(m + sizeMask);
275
276    TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
277           w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
278    TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp);
279
280    fc = color_to_byte (pCurs->foreBlue)
281      | (color_to_byte (pCurs->foreGreen) << 8)
282      | (color_to_byte (pCurs->foreRed)   << 16);
283
284    bc = color_to_byte (pCurs->backBlue)
285      | (color_to_byte (pCurs->backGreen) << 8)
286      | (color_to_byte (pCurs->backRed)   << 16);
287
288    /*
289     * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
290     * Xorg:
291     *   The mask is a bitmap indicating which parts of the cursor are
292     *   transparent and which parts are drawn. The source is a bitmap
293     *   indicating which parts of the non-transparent portion of the
294     *   the cursor should be painted in the foreground color and which
295     *   should be painted in the background color. By default, set bits
296     *   indicate the opaque part of the mask bitmap and clear bits
297     *   indicate the transparent part.
298     * VBox:
299     *   The color data is the XOR mask. The AND mask bits determine
300     *   which pixels of the color data (XOR mask) will replace (overwrite)
301     *   the screen pixels (AND mask bit = 0) and which ones will be XORed
302     *   with existing screen pixels (AND mask bit = 1).
303     *   For example when you have the AND mask all 0, then you see the
304     *   correct mouse pointer image surrounded by black square.
305     */
306    for (pm = bitsp->mask, ps = bitsp->source, y = 0;
307         y < h;
308         ++y, pm += srcPitch, ps += srcPitch, m += dstPitch)
309    {
310        for (x = 0; x < w; ++x)
311        {
312            if (pm[x / 8] & (1 << (x % 8)))
313            {
314                /* opaque, leave AND mask bit at 0 */
315                if (ps[x / 8] & (1 << (x % 8)))
316                {
317                    *cp++ = fc;
318                    PUT_PIXEL('X');
319                }
320                else
321                {
322                    *cp++ = bc;
323                    PUT_PIXEL('*');
324                }
325            }
326            else
327            {
328                /* transparent, set AND mask bit */
329                m[x / 8] |= 1 << (7 - (x % 8));
330                /* don't change the screen pixel */
331                *cp++ = 0;
332                PUT_PIXEL(' ');
333            }
334        }
335        PUT_PIXEL('\n');
336    }
337
338    pImage->cWidth   = w;
339    pImage->cHeight  = h;
340    pImage->cHotX    = bitsp->xhot;
341    pImage->cHotY    = bitsp->yhot;
342    pImage->fFlags   = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
343    pImage->cbLength = sizeRequest - sizeof(*pImage);
344
345#ifdef DEBUG_POINTER
346    ErrorF("shape = %p\n", p);
347    vbox_show_shape(w, h, bc, c);
348#endif
349
350    return p;
351}
352
353#ifdef ARGB_CURSOR
354static Bool
355vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
356{
357    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
358    VBOXPtr pVBox = pScrn->driverPrivate;
359
360    if (!pVBox->fUseHardwareCursor)
361        return FALSE;
362    if (   (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
363        || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
364        || (pScrn->bitsPerPixel <= 8))
365        return FALSE;
366    return TRUE;
367}
368
369
370static void
371vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
372{
373    VBOXPtr pVBox;
374    CursorBitsPtr bitsp;
375    unsigned short w, h;
376    unsigned short cx, cy;
377    unsigned char *pm;
378    CARD32 *pc;
379    size_t sizeData, sizeMask;
380    CARD8 *p;
381    int scrnIndex;
382    uint32_t fFlags =   VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE
383                      | VBOX_MOUSE_POINTER_ALPHA;
384
385    pVBox = pScrn->driverPrivate;
386    bitsp = pCurs->bits;
387    w     = bitsp->width;
388    h     = bitsp->height;
389    scrnIndex = pScrn->scrnIndex;
390
391    /* Mask must be generated for alpha cursors, that is required by VBox. */
392    /* note: (michael) the next struct must be 32bit aligned. */
393    sizeMask  = ((w + 7) / 8 * h + 3) & ~3;
394
395    if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
396        RETERROR(scrnIndex, ,
397                 "Error invalid cursor dimensions %dx%d\n", w, h);
398
399    if ((bitsp->xhot > w) || (bitsp->yhot > h))
400        RETERROR(scrnIndex, ,
401                 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
402                 bitsp->xhot, bitsp->yhot, w, h);
403
404    sizeData = w * h * 4 + sizeMask;
405    p = calloc(1, sizeData);
406    if (!p)
407        RETERROR(scrnIndex, ,
408                 "Error failed to alloc %lu bytes for cursor\n",
409                 (unsigned long)sizeData);
410
411    memcpy(p + sizeMask, bitsp->argb, w * h * 4);
412
413    /* Emulate the AND mask. */
414    pm = p;
415    pc = bitsp->argb;
416
417    /* Init AND mask to 1 */
418    memset(pm, 0xFF, sizeMask);
419
420    /*
421     * The additions driver must provide the AND mask for alpha cursors. The host frontend
422     * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
423     * But if the host does not support ARGB, then it simply uses the AND mask and the color
424     * data to draw a normal color cursor.
425     */
426    for (cy = 0; cy < h; cy++)
427    {
428        unsigned char bitmask = 0x80;
429
430        for (cx = 0; cx < w; cx++, bitmask >>= 1)
431        {
432            if (bitmask == 0)
433                bitmask = 0x80;
434
435            if (pc[cx] >= 0xF0000000)
436                pm[cx / 8] &= ~bitmask;
437        }
438
439        /* Point to next source and dest scans */
440        pc += w;
441        pm += (w + 7) / 8;
442    }
443
444    VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, fFlags, bitsp->xhot,
445                                bitsp->yhot, w, h, p, sizeData);
446    free(p);
447}
448#endif
449
450Bool vbvxCursorInit(ScreenPtr pScreen)
451{
452    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
453    VBOXPtr pVBox = pScrn->driverPrivate;
454    xf86CursorInfoPtr pCurs = NULL;
455    Bool rc = TRUE;
456
457    TRACE_ENTRY();
458    pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
459    if (!pCurs) {
460        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
461                   "Failed to create X Window cursor information structures for virtual mouse.\n");
462        rc = FALSE;
463    }
464    if (rc) {
465        pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
466        pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
467        pCurs->Flags =   HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
468                       | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
469                       | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST
470                       | HARDWARE_CURSOR_UPDATE_UNHIDDEN;
471
472        pCurs->SetCursorColors   = vbox_set_cursor_colors;
473        pCurs->SetCursorPosition = vbox_set_cursor_position;
474        pCurs->LoadCursorImage   = vbox_load_cursor_image;
475        pCurs->HideCursor        = vbox_hide_cursor;
476        pCurs->ShowCursor        = vbox_show_cursor;
477        pCurs->UseHWCursor       = vbox_use_hw_cursor;
478        pCurs->RealizeCursor     = vbox_realize_cursor;
479
480#ifdef ARGB_CURSOR
481        pCurs->UseHWCursorARGB   = vbox_use_hw_cursor_argb;
482        pCurs->LoadCursorARGB    = vbox_load_cursor_argb;
483#endif
484
485        rc = xf86InitCursor(pScreen, pCurs);
486    }
487    if (!rc)
488        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
489                   "Failed to enable mouse pointer integration.\n");
490    if (!rc && (pCurs != NULL))
491        xf86DestroyCursorInfoRec(pCurs);
492    return rc;
493}
494