summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/x11/vboxvideo/pointer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/x11/vboxvideo/pointer.c')
-rw-r--r--src/VBox/Additions/x11/vboxvideo/pointer.c496
1 files changed, 496 insertions, 0 deletions
diff --git a/src/VBox/Additions/x11/vboxvideo/pointer.c b/src/VBox/Additions/x11/vboxvideo/pointer.c
new file mode 100644
index 00000000..9a6ade80
--- /dev/null
+++ b/src/VBox/Additions/x11/vboxvideo/pointer.c
@@ -0,0 +1,496 @@
+/* $Id: pointer.c $ */
+/** @file
+ * VirtualBox X11 Additions graphics driver utility functions
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef PCIACCESS
+# include "xf86Pci.h"
+# include <Pci.h>
+#endif
+
+#include "xf86.h"
+#define NEED_XF86_TYPES
+#include "compiler.h"
+#include "cursorstr.h"
+#include "servermd.h"
+
+#include "vboxvideo.h"
+
+#ifdef XORG_7X
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#define VBOX_MAX_CURSOR_WIDTH 64
+#define VBOX_MAX_CURSOR_HEIGHT 64
+
+/**************************************************************************
+* Debugging functions and macros *
+**************************************************************************/
+
+/* #define DEBUG_POINTER */
+
+#ifdef DEBUG
+# define PUT_PIXEL(c) ErrorF ("%c", c)
+#else /* DEBUG_VIDEO not defined */
+# define PUT_PIXEL(c) do { } while(0)
+#endif /* DEBUG_VIDEO not defined */
+
+/** Macro to printf an error message and return from a function */
+#define RETERROR(scrnIndex, RetVal, ...) \
+ do \
+ { \
+ xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
+ return RetVal; \
+ } \
+ while (0)
+
+/** Structure to pass cursor image data between realise_cursor() and
+ * load_cursor_image(). The members match the parameters to
+ * @a VBoxHGSMIUpdatePointerShape(). */
+struct vboxCursorImage
+{
+ uint32_t fFlags;
+ uint32_t cHotX;
+ uint32_t cHotY;
+ uint32_t cWidth;
+ uint32_t cHeight;
+ uint8_t *pPixels;
+ uint32_t cbLength;
+};
+
+#ifdef DEBUG_POINTER
+static void
+vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image)
+{
+ size_t x, y;
+ unsigned short pitch;
+ CARD32 *color;
+ unsigned char *mask;
+ size_t sizeMask;
+
+ image += sizeof(struct vboxCursorImage);
+ mask = image;
+ pitch = (w + 7) / 8;
+ sizeMask = (pitch * h + 3) & ~3;
+ color = (CARD32 *)(image + sizeMask);
+
+ TRACE_ENTRY();
+ for (y = 0; y < h; ++y, mask += pitch, color += w)
+ {
+ for (x = 0; x < w; ++x)
+ {
+ if (mask[x / 8] & (1 << (7 - (x % 8))))
+ ErrorF (" ");
+ else
+ {
+ CARD32 c = color[x];
+ if (c == bg)
+ ErrorF("Y");
+ else
+ ErrorF("X");
+ }
+ }
+ ErrorF("\n");
+ }
+}
+#endif
+
+/**************************************************************************
+* Main functions *
+**************************************************************************/
+
+void vbvxCursorTerm(VBOXPtr pVBox)
+{
+ TRACE_ENTRY();
+
+ xf86DestroyCursorInfoRec(pVBox->pCurs);
+ pVBox->pCurs = NULL;
+ TRACE_EXIT();
+}
+
+static void
+vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
+{
+ int rc;
+ RT_NOREF(pScrn);
+
+ rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, 0, 0, 0, 0, 0, NULL, 0);
+ AssertMsg(rc == VINF_SUCCESS, ("Could not hide the virtual mouse pointer, VBox error %d.\n", rc));
+}
+
+static void
+vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
+{
+ int rc;
+ RT_NOREF(pScrn);
+
+ if (!pVBox->fUseHardwareCursor)
+ return;
+ rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, VBOX_MOUSE_POINTER_VISIBLE,
+ 0, 0, 0, 0, NULL, 0);
+ AssertMsg(rc == VINF_SUCCESS, ("Could not unhide the virtual mouse pointer.\n"));
+}
+
+static void
+vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
+ unsigned char *pvImage)
+{
+ int rc;
+ struct vboxCursorImage *pImage;
+ pImage = (struct vboxCursorImage *)pvImage;
+ RT_NOREF(pScrn);
+
+#ifdef DEBUG_POINTER
+ vbox_show_shape(pImage->cWidth, pImage->cHeight, 0, pvImage);
+#endif
+
+ rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, pImage->fFlags,
+ pImage->cHotX, pImage->cHotY, pImage->cWidth, pImage->cHeight,
+ pImage->pPixels, pImage->cbLength);
+ AssertMsg(rc == VINF_SUCCESS, ("Unable to set the virtual mouse pointer image.\n"));
+}
+
+static void
+vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
+{
+ RT_NOREF(pScrn);
+ RT_NOREF(bg);
+ RT_NOREF(fg);
+ /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
+}
+
+
+static void
+vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
+{
+ VBOXPtr pVBox = pScrn->driverPrivate;
+
+ /* This currently does nothing. */
+ VBoxHGSMICursorPosition(&pVBox->guestCtx, true, x, y, NULL, NULL);
+}
+
+static void
+vbox_hide_cursor(ScrnInfoPtr pScrn)
+{
+ VBOXPtr pVBox = pScrn->driverPrivate;
+
+ vbox_vmm_hide_cursor(pScrn, pVBox);
+}
+
+static void
+vbox_show_cursor(ScrnInfoPtr pScrn)
+{
+ VBOXPtr pVBox = pScrn->driverPrivate;
+
+ vbox_vmm_show_cursor(pScrn, pVBox);
+}
+
+static void
+vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image)
+{
+ VBOXPtr pVBox = pScrn->driverPrivate;
+
+ vbox_vmm_load_cursor_image(pScrn, pVBox, image);
+}
+
+static Bool
+vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ VBOXPtr pVBox = pScrn->driverPrivate;
+ RT_NOREF(pCurs);
+ return pVBox->fUseHardwareCursor;
+}
+
+static unsigned char
+color_to_byte(unsigned c)
+{
+ return (c >> 8) & 0xff;
+}
+
+static unsigned char *
+vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
+{
+ VBOXPtr pVBox;
+ CursorBitsPtr bitsp;
+ unsigned short w, h, x, y;
+ unsigned char *c, *p, *pm, *ps, *m;
+ size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
+ CARD32 fc, bc, *cp;
+ int scrnIndex = infoPtr->pScrn->scrnIndex;
+ struct vboxCursorImage *pImage;
+
+ pVBox = infoPtr->pScrn->driverPrivate;
+ bitsp = pCurs->bits;
+ w = bitsp->width;
+ h = bitsp->height;
+
+ if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
+ RETERROR(scrnIndex, NULL,
+ "Error invalid cursor dimensions %dx%d\n", w, h);
+
+ if ((bitsp->xhot > w) || (bitsp->yhot > h))
+ RETERROR(scrnIndex, NULL,
+ "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
+ bitsp->xhot, bitsp->yhot, w, h);
+
+ srcPitch = PixmapBytePad (bitsp->width, 1);
+ dstPitch = (w + 7) / 8;
+ sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
+ sizeRgba = w * h * 4;
+ sizeRequest = sizeMask + sizeRgba + sizeof(*pImage);
+
+ p = c = calloc (1, sizeRequest);
+ if (!c)
+ RETERROR(scrnIndex, NULL,
+ "Error failed to alloc %lu bytes for cursor\n",
+ (unsigned long) sizeRequest);
+
+ pImage = (struct vboxCursorImage *)p;
+ pImage->pPixels = m = p + sizeof(*pImage);
+ cp = (CARD32 *)(m + sizeMask);
+
+ TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
+ w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
+ TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp);
+
+ fc = color_to_byte (pCurs->foreBlue)
+ | (color_to_byte (pCurs->foreGreen) << 8)
+ | (color_to_byte (pCurs->foreRed) << 16);
+
+ bc = color_to_byte (pCurs->backBlue)
+ | (color_to_byte (pCurs->backGreen) << 8)
+ | (color_to_byte (pCurs->backRed) << 16);
+
+ /*
+ * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
+ * Xorg:
+ * The mask is a bitmap indicating which parts of the cursor are
+ * transparent and which parts are drawn. The source is a bitmap
+ * indicating which parts of the non-transparent portion of the
+ * the cursor should be painted in the foreground color and which
+ * should be painted in the background color. By default, set bits
+ * indicate the opaque part of the mask bitmap and clear bits
+ * indicate the transparent part.
+ * VBox:
+ * The color data is the XOR mask. The AND mask bits determine
+ * which pixels of the color data (XOR mask) will replace (overwrite)
+ * the screen pixels (AND mask bit = 0) and which ones will be XORed
+ * with existing screen pixels (AND mask bit = 1).
+ * For example when you have the AND mask all 0, then you see the
+ * correct mouse pointer image surrounded by black square.
+ */
+ for (pm = bitsp->mask, ps = bitsp->source, y = 0;
+ y < h;
+ ++y, pm += srcPitch, ps += srcPitch, m += dstPitch)
+ {
+ for (x = 0; x < w; ++x)
+ {
+ if (pm[x / 8] & (1 << (x % 8)))
+ {
+ /* opaque, leave AND mask bit at 0 */
+ if (ps[x / 8] & (1 << (x % 8)))
+ {
+ *cp++ = fc;
+ PUT_PIXEL('X');
+ }
+ else
+ {
+ *cp++ = bc;
+ PUT_PIXEL('*');
+ }
+ }
+ else
+ {
+ /* transparent, set AND mask bit */
+ m[x / 8] |= 1 << (7 - (x % 8));
+ /* don't change the screen pixel */
+ *cp++ = 0;
+ PUT_PIXEL(' ');
+ }
+ }
+ PUT_PIXEL('\n');
+ }
+
+ pImage->cWidth = w;
+ pImage->cHeight = h;
+ pImage->cHotX = bitsp->xhot;
+ pImage->cHotY = bitsp->yhot;
+ pImage->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
+ pImage->cbLength = sizeRequest - sizeof(*pImage);
+
+#ifdef DEBUG_POINTER
+ ErrorF("shape = %p\n", p);
+ vbox_show_shape(w, h, bc, c);
+#endif
+
+ return p;
+}
+
+#ifdef ARGB_CURSOR
+static Bool
+vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ VBOXPtr pVBox = pScrn->driverPrivate;
+
+ if (!pVBox->fUseHardwareCursor)
+ return FALSE;
+ if ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
+ || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
+ || (pScrn->bitsPerPixel <= 8))
+ return FALSE;
+ return TRUE;
+}
+
+
+static void
+vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
+{
+ VBOXPtr pVBox;
+ CursorBitsPtr bitsp;
+ unsigned short w, h;
+ unsigned short cx, cy;
+ unsigned char *pm;
+ CARD32 *pc;
+ size_t sizeData, sizeMask;
+ CARD8 *p;
+ int scrnIndex;
+ uint32_t fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE
+ | VBOX_MOUSE_POINTER_ALPHA;
+
+ pVBox = pScrn->driverPrivate;
+ bitsp = pCurs->bits;
+ w = bitsp->width;
+ h = bitsp->height;
+ scrnIndex = pScrn->scrnIndex;
+
+ /* Mask must be generated for alpha cursors, that is required by VBox. */
+ /* note: (michael) the next struct must be 32bit aligned. */
+ sizeMask = ((w + 7) / 8 * h + 3) & ~3;
+
+ if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
+ RETERROR(scrnIndex, ,
+ "Error invalid cursor dimensions %dx%d\n", w, h);
+
+ if ((bitsp->xhot > w) || (bitsp->yhot > h))
+ RETERROR(scrnIndex, ,
+ "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
+ bitsp->xhot, bitsp->yhot, w, h);
+
+ sizeData = w * h * 4 + sizeMask;
+ p = calloc(1, sizeData);
+ if (!p)
+ RETERROR(scrnIndex, ,
+ "Error failed to alloc %lu bytes for cursor\n",
+ (unsigned long)sizeData);
+
+ memcpy(p + sizeMask, bitsp->argb, w * h * 4);
+
+ /* Emulate the AND mask. */
+ pm = p;
+ pc = bitsp->argb;
+
+ /* Init AND mask to 1 */
+ memset(pm, 0xFF, sizeMask);
+
+ /*
+ * The additions driver must provide the AND mask for alpha cursors. The host frontend
+ * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
+ * But if the host does not support ARGB, then it simply uses the AND mask and the color
+ * data to draw a normal color cursor.
+ */
+ for (cy = 0; cy < h; cy++)
+ {
+ unsigned char bitmask = 0x80;
+
+ for (cx = 0; cx < w; cx++, bitmask >>= 1)
+ {
+ if (bitmask == 0)
+ bitmask = 0x80;
+
+ if (pc[cx] >= 0xF0000000)
+ pm[cx / 8] &= ~bitmask;
+ }
+
+ /* Point to next source and dest scans */
+ pc += w;
+ pm += (w + 7) / 8;
+ }
+
+ VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, fFlags, bitsp->xhot,
+ bitsp->yhot, w, h, p, sizeData);
+ free(p);
+}
+#endif
+
+Bool vbvxCursorInit(ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ VBOXPtr pVBox = pScrn->driverPrivate;
+ xf86CursorInfoPtr pCurs = NULL;
+ Bool rc = TRUE;
+
+ TRACE_ENTRY();
+ pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
+ if (!pCurs) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to create X Window cursor information structures for virtual mouse.\n");
+ rc = FALSE;
+ }
+ if (rc) {
+ pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
+ pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
+ pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
+ | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
+ | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST
+ | HARDWARE_CURSOR_UPDATE_UNHIDDEN;
+
+ pCurs->SetCursorColors = vbox_set_cursor_colors;
+ pCurs->SetCursorPosition = vbox_set_cursor_position;
+ pCurs->LoadCursorImage = vbox_load_cursor_image;
+ pCurs->HideCursor = vbox_hide_cursor;
+ pCurs->ShowCursor = vbox_show_cursor;
+ pCurs->UseHWCursor = vbox_use_hw_cursor;
+ pCurs->RealizeCursor = vbox_realize_cursor;
+
+#ifdef ARGB_CURSOR
+ pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
+ pCurs->LoadCursorARGB = vbox_load_cursor_argb;
+#endif
+
+ rc = xf86InitCursor(pScreen, pCurs);
+ }
+ if (!rc)
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to enable mouse pointer integration.\n");
+ if (!rc && (pCurs != NULL))
+ xf86DestroyCursorInfoRec(pCurs);
+ return rc;
+}