/** @file * * Linux Additions X11 graphics driver helper module */ /* * Copyright (C) 2006-2007 Sun Microsystems, Inc. * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 USA or visit http://www.sun.com if you need * additional information or have any questions. */ #include #include #include #include "xf86.h" #define NEED_XF86_TYPES #include "xf86_ansic.h" #include "compiler.h" #include "cursorstr.h" #include "vboxvideo_68.h" #define VBOX_MAX_CURSOR_WIDTH 64 #define VBOX_MAX_CURSOR_HEIGHT 64 #if 0 #define DEBUG_X #endif #ifdef DEBUG_X #define TRACE_ENTRY() do \ { \ ErrorF ("%s\n", __FUNCTION__); \ } while(0) #define TRACE_LINE() do \ { \ ErrorF ("%s: line %d\n", __FUNCTION__, __LINE__); \ } while(0) #define PUT_PIXEL(c) ErrorF ("%c", c) #define dolog(...) ErrorF (__VA_ARGS__) #else #define PUT_PIXEL(c) do { } while(0) #define TRACE_ENTRY() do { } while(0) #define TRACE_LINE() do { } while(0) #define dolog(...) do { } while(0) #endif /** 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) #ifdef DEBUG_X 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 size_mask; image += offsetof (VMMDevReqMousePointer, pointerData); mask = image; pitch = (w + 7) / 8; size_mask = (pitch * h + 3) & ~3; color = (CARD32 *) (image + size_mask); 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 static Bool vbox_vmmcall (ScrnInfoPtr pScrn, VBOXPtr pVBox, VMMDevRequestHeader *hdrp) { int err; TRACE_ENTRY (); #ifdef RT_OS_LINUX err = ioctl (pVBox->vbox_fd, VBOXGUEST_IOCTL_VMMREQUEST(hdrp->size), hdrp); #else /** * @todo this should work fine on other platforms too, but it needs to be * checked for each one. */ # error port me! #endif if (err < 0) RETERROR(pScrn->scrnIndex, FALSE, "Ioctl call failed during a request to the virtual machine: %s\n", strerror (errno)); else if (RT_FAILURE (hdrp->rc)) RETERROR(pScrn->scrnIndex, FALSE, "A request to the virtual machine returned %d\n", hdrp->rc); /* success */ return TRUE; } static Bool vbox_host_uses_hwcursor(ScrnInfoPtr pScrn) { Bool rc = FALSE; VBOXPtr pVBox = pScrn->driverPrivate; VMMDevReqMouseStatus req; int vrc = vmmdevInitRequest ((VMMDevRequestHeader*)&req, VMMDevReq_GetMouseStatus); if (RT_FAILURE (vrc)) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to determine whether the virtual machine supports mouse pointer integration - request initialization failed with return code %d\n", rc); #ifdef RT_OS_LINUX if ( RT_SUCCESS(vrc) && (ioctl(pVBox->vbox_fd, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(req)), (void*)&req) < 0)) #else # error port me! #endif { vrc = VERR_FILE_IO_ERROR; xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to determine whether the virtual machine supports mouse pointer integration - request system call failed: %s.\n", strerror(errno)); } if ( RT_SUCCESS(rc) && !(req.mouseFeatures & VBOXGUEST_MOUSE_HOST_CANNOT_HWPOINTER) && (req.mouseFeatures & VBOXGUEST_MOUSE_GUEST_CAN_ABSOLUTE)) rc = TRUE; return rc; } void vbox_close(ScrnInfoPtr pScrn, VBOXPtr pVBox) { TRACE_ENTRY (); xfree (pVBox->reqp); pVBox->reqp = NULL; #ifdef RT_OS_SOLARIS VbglR3Term(); #else if (close (pVBox->vbox_fd)) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to close the virtual machine device (file %d): %s\n", pVBox->vbox_fd, strerror (errno)); pVBox->vbox_fd = -1; #endif } /** * Macro to disable VBVA extensions and return, for use when an * unexplained error occurs. */ #define DISABLE_VBVA_AND_RETURN(pScrn, ...) \ do \ { \ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, __VA_ARGS__); \ vboxDisableVbva(pScrn); \ pVBox->useVbva = FALSE; \ return; \ } \ while (0) /** * Callback function called by the X server to tell us about dirty * rectangles in the video buffer. * * @param pScreen pointer to the information structure for the current * screen * @param iRects Number of dirty rectangles to update * @param aRects Array of structures containing the coordinates of the * rectangles */ static void vboxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects) { VBVACMDHDR cmdHdr; VBOXPtr pVBox; VBVARECORD *pRecord; VBVAMEMORY *pMem; CARD32 indexRecordNext; CARD32 off32Data; CARD32 off32Free; INT32 i32Diff; CARD32 cbHwBufferAvail; int scrnIndex; int i; pVBox = pScrn->driverPrivate; if (pVBox->useVbva == FALSE) return; pMem = pVBox->pVbvaMemory; /* Just return quietly if VBVA is not currently active. */ if ((pMem->fu32ModeFlags & VBVA_F_MODE_ENABLED) == 0) return; scrnIndex = pScrn->scrnIndex; for (i = 0; i < iRects; i++) { cmdHdr.x = (int16_t)aRects[i].x1; cmdHdr.y = (int16_t)aRects[i].y1; cmdHdr.w = (uint16_t)(aRects[i].x2 - aRects[i].x1); cmdHdr.h = (uint16_t)(aRects[i].y2 - aRects[i].y1); /* Get the active record and move the pointer along */ indexRecordNext = (pMem->indexRecordFree + 1) % VBVA_MAX_RECORDS; if (indexRecordNext == pMem->indexRecordFirst) { /* All slots in the records queue are used. */ if (vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqf) != TRUE) DISABLE_VBVA_AND_RETURN(pScrn, "Unable to clear the VirtualBox graphics acceleration queue " "- the request to the virtual machine failed. Switching to " "unaccelerated mode.\n"); } if (indexRecordNext == pMem->indexRecordFirst) DISABLE_VBVA_AND_RETURN(pScrn, "Failed to clear the VirtualBox graphics acceleration queue. " "Switching to unaccelerated mode.\n"); pRecord = &pMem->aRecords[pMem->indexRecordFree]; /* Mark the record as being updated. */ pRecord->cbRecord = VBVA_F_RECORD_PARTIAL; pMem->indexRecordFree = indexRecordNext; /* Compute how many bytes we have in the ring buffer. */ off32Free = pMem->off32Free; off32Data = pMem->off32Data; /* Free is writing position. Data is reading position. * Data == Free means buffer is free. * There must be always gap between free and data when data * are in the buffer. * Guest only changes free, host only changes data. */ i32Diff = off32Data - off32Free; cbHwBufferAvail = i32Diff > 0? i32Diff: VBVA_RING_BUFFER_SIZE + i32Diff; if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD) { if (vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqf) != TRUE) DISABLE_VBVA_AND_RETURN(pScrn, "Unable to clear the VirtualBox graphics acceleration queue " "- the request to the virtual machine failed. Switching to " "unaccelerated mode.\n"); /* Calculate the free space again. */ off32Free = pMem->off32Free; off32Data = pMem->off32Data; i32Diff = off32Data - off32Free; cbHwBufferAvail = i32Diff > 0? i32Diff: VBVA_RING_BUFFER_SIZE + i32Diff; if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD) DISABLE_VBVA_AND_RETURN(pScrn, "No space left in the VirtualBox graphics acceleration command buffer, " "despite clearing the queue. Switching to unaccelerated mode.\n"); } /* Now copy the data into the buffer */ if (off32Free + sizeof(cmdHdr) < VBVA_RING_BUFFER_SIZE) { memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr, sizeof(cmdHdr)); pMem->off32Free = pMem->off32Free + sizeof(cmdHdr); } else { CARD32 u32First = VBVA_RING_BUFFER_SIZE - off32Free; /* The following is impressively ugly! */ CARD8 *pu8Second = (CARD8 *)&cmdHdr + u32First; CARD32 u32Second = sizeof(cmdHdr) - u32First; memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr, u32First); if (u32Second) memcpy(&pMem->au8RingBuffer[0], (void *)pu8Second, u32Second); pMem->off32Free = u32Second; } pRecord->cbRecord += sizeof(cmdHdr); /* Mark the record completed. */ pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL; } } /** * Initialise VirtualBox's accelerated video extensions. * Note that we assume that the PCI memory is 32bit mapped, * as X doesn't seem to support mapping 64bit memory. * * @returns True on success, false on failure */ static Bool vboxInitVbva(int scrnIndex, ScreenPtr pScreen, VBOXPtr pVBox) { PCITAG pciTag; ADDRESS pciAddress; int rc; /* Locate the device. It should already have been enabled by the kernel driver. */ pciTag = pciFindFirst((unsigned) VMMDEV_DEVICEID << 16 | VMMDEV_VENDORID, (CARD32) ~0); if (pciTag == PCI_NOT_FOUND) { xf86DrvMsg(scrnIndex, X_ERROR, "Could not find the VirtualBox base device on the PCI bus.\n"); return FALSE; } /* Read the address and size of the second I/O region. */ pciAddress = pciReadLong(pciTag, PCI_MAP_REG_START + 4); if (pciAddress == 0 || pciAddress == (CARD32) ~0) RETERROR(scrnIndex, FALSE, "The VirtualBox base device contains an invalid memory address.\n"); if (PCI_MAP_IS64BITMEM(pciAddress)) RETERROR(scrnIndex, FALSE, "The VirtualBox base device has a 64bit mapping address. " "This is currently not supported.\n"); /* Map it. We hardcode the size as X does not export the function needed to determine it. */ pVBox->pVMMDevMemory = xf86MapPciMem(scrnIndex, 0, pciTag, pciAddress, sizeof(VMMDevMemory)); if (pVBox->pVMMDevMemory == NULL) { xf86DrvMsg(scrnIndex, X_ERROR, "Failed to map VirtualBox video extension memory.\n"); return FALSE; } pVBox->pVbvaMemory = &pVBox->pVMMDevMemory->vbvaMemory; /* Initialise requests */ pVBox->reqf = xcalloc(1, vmmdevGetRequestSize(VMMDevReq_VideoAccelFlush)); if (!pVBox->reqf) { xf86DrvMsg(scrnIndex, X_ERROR, "Could not allocate memory for VBVA flush request.\n"); return FALSE; } rc = vmmdevInitRequest ((VMMDevRequestHeader *) pVBox->reqf, VMMDevReq_VideoAccelFlush); if (RT_FAILURE (rc)) { xf86DrvMsg(scrnIndex, X_ERROR, "Could not initialise VBVA flush request: return value %d\n", rc); xfree(pVBox->reqf); return FALSE; } pVBox->reqe = xcalloc(1, vmmdevGetRequestSize(VMMDevReq_VideoAccelEnable)); if (!pVBox->reqe) { xf86DrvMsg(scrnIndex, X_ERROR, "Could not allocate memory for VBVA enable request.\n"); xfree(pVBox->reqf); return FALSE; } rc = vmmdevInitRequest ((VMMDevRequestHeader *) pVBox->reqe, VMMDevReq_VideoAccelEnable); if (RT_FAILURE (rc)) { xf86DrvMsg(scrnIndex, X_ERROR, "Could not initialise VBVA enable request: return value = %d\n", rc); xfree(pVBox->reqf); xfree(pVBox->reqe); return FALSE; } /* Set up the dirty rectangle handler. Since this seems to be a delicate operation, and removing it doubly so, this will remain in place whether it is needed or not, and will simply return if VBVA is not active. I assume that it will be active most of the time. */ if (ShadowFBInit2(pScreen, NULL, vboxHandleDirtyRect) != TRUE) { xf86DrvMsg(scrnIndex, X_ERROR, "Unable to install dirty rectangle handler for VirtualBox graphics acceleration.\n"); xfree(pVBox->reqf); xfree(pVBox->reqe); return FALSE; } return TRUE; } Bool vbox_open (ScrnInfoPtr pScrn, ScreenPtr pScreen, VBOXPtr pVBox) { int fd, vrc; void *p = NULL; size_t size; int scrnIndex = pScrn->scrnIndex; Bool rc = TRUE; TRACE_ENTRY (); pVBox->useVbva = FALSE; #ifdef RT_OS_SOLARIS NOREF(fd); if (pVBox->reqp) { /* still open, just re-enable VBVA after CloseScreen was called */ pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox); return TRUE; } vrc = VbglR3Init(); if (RT_FAILURE(vrc)) { xf86DrvMsg(scrnIndex, X_ERROR, "VbglR3Init failed vrc=%d.\n", vrc); rc = FALSE; } #else if (pVBox->vbox_fd != -1 && pVBox->reqp) { /* still open, just re-enable VBVA after CloseScreen was called */ pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox); return TRUE; } fd = open (VBOXGUEST_DEVICE_NAME, O_RDWR, 0); if (fd < 0) { xf86DrvMsg(scrnIndex, X_ERROR, "Error opening kernel module: %s\n", strerror (errno)); rc = FALSE; } #endif if (rc) { size = vmmdevGetRequestSize (VMMDevReq_SetPointerShape); p = xcalloc (1, size); if (NULL == p) { xf86DrvMsg(scrnIndex, X_ERROR, "Could not allocate %lu bytes for VMM request\n", (unsigned long) size); rc = FALSE; } } if (rc) { vrc = vmmdevInitRequest (p, VMMDevReq_SetPointerShape); if (RT_FAILURE (vrc)) { xf86DrvMsg(scrnIndex, X_ERROR, "Could not init VMM request: vrc = %d\n", vrc); rc = FALSE; } } if (rc) { #ifndef RT_OS_SOLARIS pVBox->vbox_fd = fd; #endif pVBox->reqp = p; pVBox->pCurs = NULL; pVBox->pointerHeaderSize = size; pVBox->pointerOffscreen = FALSE; pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox); } else { if (NULL != p) { xfree (p); } #ifdef RT_OS_SOLARIS VbglR3Term(); #else if (close (fd)) { xf86DrvMsg(scrnIndex, X_ERROR, "Error closing kernel module file descriptor(%d): %s\n", fd, strerror (errno)); } #endif } return rc; } static void vbox_vmm_hide_cursor (ScrnInfoPtr pScrn, VBOXPtr pVBox) { pVBox->reqp->fFlags = 0; if (vbox_vmmcall (pScrn, pVBox, &pVBox->reqp->header) != TRUE) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not hide the virtual mouse pointer.\n"); } static void vbox_vmm_show_cursor (ScrnInfoPtr pScrn, VBOXPtr pVBox) { pVBox->reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE; if (vbox_vmmcall (pScrn, pVBox, &pVBox->reqp->header) != TRUE) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not unhide the virtual mouse pointer.\n"); } static void vbox_vmm_load_cursor_image (ScrnInfoPtr pScrn, VBOXPtr pVBox, unsigned char *image) { VMMDevReqMousePointer *reqp; reqp = (VMMDevReqMousePointer *) image; dolog ("w=%d h=%d size=%d\n", reqp->width, reqp->height, reqp->header.size); #ifdef DEBUG_X vbox_show_shape (reqp->width, reqp->height, 0, image); #endif if (vbox_vmmcall (pScrn, pVBox, &reqp->header) != TRUE) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to set the virtual mouse pointer image.\n"); } static void vbox_set_cursor_colors (ScrnInfoPtr pScrn, int bg, int fg) { TRACE_ENTRY (); (void) pScrn; (void) bg; (void) 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; */ /* TRACE_ENTRY (); */ /* don't disable the mouse cursor if we go out of our visible area * since the mouse cursor is drawn by the host anyway */ #if 0 if (((x < 0) || (x > pScrn->pScreen->width)) || ((y < 0) || (y > pScrn->pScreen->height))) { if (!pVBox->pointerOffscreen) { pVBox->pointerOffscreen = TRUE; vbox_vmm_hide_cursor (pScrn, pVBox); } } else { if (pVBox->pointerOffscreen) { pVBox->pointerOffscreen = FALSE; vbox_vmm_show_cursor (pScrn, pVBox); } } #endif } static void vbox_hide_cursor (ScrnInfoPtr pScrn) { VBOXPtr pVBox = pScrn->driverPrivate; TRACE_ENTRY (); vbox_vmm_hide_cursor (pScrn, pVBox); } static void vbox_show_cursor (ScrnInfoPtr pScrn) { VBOXPtr pVBox = pScrn->driverPrivate; TRACE_ENTRY (); vbox_vmm_show_cursor (pScrn, pVBox); } static void vbox_load_cursor_image (ScrnInfoPtr pScrn, unsigned char *image) { VBOXPtr pVBox = pScrn->driverPrivate; TRACE_ENTRY (); vbox_vmm_load_cursor_image (pScrn, pVBox, image); } static Bool vbox_use_hw_cursor (ScreenPtr pScreen, CursorPtr pCurs) { ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; return vbox_host_uses_hwcursor(pScrn); } 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 size, size_rgba, size_mask, src_pitch, dst_pitch; CARD32 fc, bc, *cp; int rc, scrnIndex = infoPtr->pScrn->scrnIndex; VMMDevReqMousePointer *reqp; 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); src_pitch = PixmapBytePad (bitsp->width, 1); dst_pitch = (w + 7) / 8; size_mask = ((dst_pitch * h) + 3) & (size_t) ~3; size_rgba = w * h * 4; size = size_mask + size_rgba + pVBox->pointerHeaderSize; p = c = xcalloc (1, size); if (!c) RETERROR(scrnIndex, NULL, "Error failed to alloc %lu bytes for cursor\n", (unsigned long) size); rc = vmmdevInitRequest ((VMMDevRequestHeader *) p, VMMDevReq_SetPointerShape); if (RT_FAILURE (rc)) { xfree(p); RETERROR(scrnIndex, NULL, "Could not init VMM request: rc = %d\n", rc); } m = p + offsetof (VMMDevReqMousePointer, pointerData); cp = (CARD32 *) (m + size_mask); dolog ("w=%d h=%d sm=%d sr=%d p=%d\n", w, h, (int) size_mask, (int) size_rgba, (int) dst_pitch); dolog ("m=%p c=%p cp=%p\n", m, c, (void *) (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 += src_pitch, ps += src_pitch, m += dst_pitch) { 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'); } reqp = (VMMDevReqMousePointer *) p; reqp->width = w; reqp->height = h; reqp->xHot = bitsp->xhot; reqp->yHot = bitsp->yhot; reqp->fFlags = VBOX_MOUSE_POINTER_SHAPE; reqp->header.size = size; #ifdef DEBUG_X 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]; Bool rc = TRUE; if (!vbox_host_uses_hwcursor(pScrn)) rc = FALSE; if ( rc && ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT) || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH) || (pScrn->bitsPerPixel <= 8) ) ) rc = FALSE; return rc; } static void vbox_load_cursor_argb (ScrnInfoPtr pScrn, CursorPtr pCurs) { VBOXPtr pVBox; VMMDevReqMousePointer *reqp; CursorBitsPtr bitsp; unsigned short w, h; unsigned short cx, cy; unsigned char *pm; CARD32 *pc; size_t size, mask_size; CARD8 *p; int scrnIndex; 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. */ mask_size = ((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); size = w * h * 4 + pVBox->pointerHeaderSize + mask_size; p = xcalloc (1, size); if (!p) RETERROR(scrnIndex, , "Error failed to alloc %lu bytes for cursor\n", (unsigned long) size); reqp = (VMMDevReqMousePointer *) p; *reqp = *pVBox->reqp; reqp->width = w; reqp->height = h; reqp->xHot = bitsp->xhot; reqp->yHot = bitsp->yhot; reqp->fFlags = VBOX_MOUSE_POINTER_SHAPE | VBOX_MOUSE_POINTER_ALPHA; reqp->header.size = size; memcpy (p + offsetof (VMMDevReqMousePointer, pointerData) + mask_size, bitsp->argb, w * h * 4); /** @ */ /* Emulate the AND mask. */ pm = p + offsetof (VMMDevReqMousePointer, pointerData); pc = bitsp->argb; /* Init AND mask to 1 */ memset (pm, 0xFF, mask_size); /** * 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; } if (vbox_vmmcall (pScrn, pVBox, &reqp->header) != TRUE) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Request to virtual machine failed " "- unable to set the virtual mouse pointer ARGB cursor image.\n"); xfree (p); } #endif Bool vbox_cursor_init (ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; VBOXPtr pVBox = pScrn->driverPrivate; xf86CursorInfoPtr pCurs; Bool rc; pVBox->pCurs = pCurs = xf86CreateCursorInfoRec (); if (!pCurs) RETERROR(pScrn->scrnIndex, FALSE, "Failed to create X Window cursor information structures for virtual mouse.\n"); 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; 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 == TRUE) return TRUE; RETERROR(pScrn->scrnIndex, FALSE, "Failed to enable mouse pointer integration.\n"); } /** * Inform VBox that we will supply it with dirty rectangle information * and install the dirty rectangle handler. * * @returns TRUE for success, FALSE for failure * @param pScreen Pointer to a structure describing the X screen in use */ Bool vboxEnableVbva(ScrnInfoPtr pScrn) { int scrnIndex = pScrn->scrnIndex; VBOXPtr pVBox = pScrn->driverPrivate; if (pVBox->useVbva != TRUE) return FALSE; pVBox->reqe->u32Enable = 1; pVBox->reqe->cbRingBuffer = VBVA_RING_BUFFER_SIZE; pVBox->reqe->fu32Status = 0; if (vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqe) != TRUE) { /* Request not accepted - disable for old hosts. */ xf86DrvMsg(scrnIndex, X_ERROR, "Unable to activate VirtualBox graphics acceleration " "- the request to the virtual machine failed. " "You may be running an old version of VirtualBox.\n"); pVBox->useVbva = FALSE; pVBox->reqe->u32Enable = 0; pVBox->reqe->cbRingBuffer = VBVA_RING_BUFFER_SIZE; pVBox->reqe->fu32Status = 0; vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqe); return FALSE; } return TRUE; } /** * Inform VBox that we will stop supplying it with dirty rectangle * information. This function is intended to be called when an X * virtual terminal is disabled, or the X server is terminated. * * @returns TRUE for success, FALSE for failure * @param pScreen Pointer to a structure describing the X screen in use */ Bool vboxDisableVbva(ScrnInfoPtr pScrn) { int scrnIndex = pScrn->scrnIndex; VBOXPtr pVBox = pScrn->driverPrivate; if (pVBox->useVbva != TRUE) /* Ths function should not have been called */ return FALSE; pVBox->reqe->u32Enable = 0; pVBox->reqe->cbRingBuffer = VBVA_RING_BUFFER_SIZE; pVBox->reqe->fu32Status = 0; if (vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqe) != TRUE) xf86DrvMsg(scrnIndex, X_ERROR, "Unable to disable VirtualBox graphics acceleration " "- the request to the virtual machine failed.\n"); else memset(pVBox->pVbvaMemory, 0, sizeof(VBVAMEMORY)); return TRUE; } /** * Query the last display change request. * * @returns iprt status value * @param xres where to store the horizontal pixel resolution requested * (0 = do not change) * @param yres where to store the vertical pixel resolution requested * (0 = do not change) * @param bpp where to store the bits per pixel requeste * (0 = do not change) * @param display Where to store the display number the request was for - * 0 for the primary display, 1 for the first secondary, etc. */ Bool vboxGetDisplayChangeRequest(ScrnInfoPtr pScrn, uint32_t *px, uint32_t *py, uint32_t *pbpp, uint32_t *display) { int rc, scrnIndex = pScrn->scrnIndex; VBOXPtr pVBox = pScrn->driverPrivate; VMMDevDisplayChangeRequest2 Req = { { 0 } }; vmmdevInitRequest(&Req.header, VMMDevReq_GetDisplayChangeRequest2); rc = vbox_vmmcall(pScrn, pVBox, &Req.header); if (RT_SUCCESS(rc)) { *px = Req.xres; *py = Req.yres; *pbpp = Req.bpp; *display = Req.display; return TRUE; } xf86DrvMsg(scrnIndex, X_ERROR, "Failed to request the last resolution requested from the guest, rc=%d.\n", rc); return FALSE; }