vboxutils.c revision c1a5f1b4426f17a6635b0eadbab627340b5c4f69
/** @file
* VirtualBox X11 Additions graphics driver utility functions
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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.
*/
#include <VBox/VBoxGuestLib.h>
#ifndef PCIACCESS
# include <xf86Pci.h>
# include <Pci.h>
#endif
#include "xf86.h"
#define NEED_XF86_TYPES
#ifdef NO_ANSIC
# include <string.h>
#else
# include "xf86_ansic.h"
#endif
#include "compiler.h"
#include "cursorstr.h"
#include "vboxvideo.h"
#define VBOX_MAX_CURSOR_WIDTH 64
#define VBOX_MAX_CURSOR_HEIGHT 64
/**************************************************************************
* Debugging functions and macros *
**************************************************************************/
/* #define DEBUG_POINTER */
#ifdef DEBUG
#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 */
do \
{ \
return RetVal; \
} \
while (0)
#ifdef DEBUG_POINTER
static void
{
size_t x, y;
unsigned short pitch;
unsigned char *mask;
TRACE_ENTRY();
{
for (x = 0; x < w; ++x)
{
ErrorF (" ");
else
{
if (c == bg)
ErrorF("Y");
else
ErrorF("X");
}
}
ErrorF("\n");
}
}
#endif
/**************************************************************************
* Helper functions and macros *
**************************************************************************/
/* This is called by the X server every time it loads a new cursor to see
* whether our "cursor hardware" can handle the cursor. This provides us with
* a mechanism (the only one!) to switch back from a software to a hardware
* cursor. */
static Bool
{
TRACE_ENTRY();
/* We may want to force the use of a software cursor. Currently this is
* needed if the guest uses a large virtual resolution, as in this case
* the host and guest tend to disagree about the pointer location. */
if (pVBox->forceSWCursor)
/* Query information about mouse integration from the host. */
if (rc) {
if (RT_FAILURE(vrc)) {
"Unable to determine whether the virtual machine supports mouse pointer integration - request initialization failed with return code %d\n", vrc);
}
}
/* If we got the information from the host then make sure the host wants
* to draw the pointer. */
if (rc)
{
if ( (fFeatures & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE)
/* As of this version (server 1.6) all major Linux releases
* are known to handle USB tablets correctly. */
#endif
)
/* Assume this will never be unloaded as long as the X session is
* running. */
|| !pVBox->guestCanAbsolute
)
}
return rc;
}
/**
* Macro to disable VBVA extensions and return, for use when an
* unexplained error occurs.
*/
#define DISABLE_VBVA_AND_RETURN(pScrn, ...) \
do \
{ \
vboxDisableVbva(pScrn); \
return; \
} \
while (0)
/**************************************************************************
* Main functions *
**************************************************************************/
void
{
TRACE_ENTRY();
TRACE_EXIT();
}
/**
* 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
{
int scrnIndex;
int i;
return;
/* Just return quietly if VBVA is not currently active. */
return;
for (i = 0; i < iRects; i++)
{
/* Get the active record and move the pointer along */
{
/* All slots in the records queue are used. */
if (VbglR3VideoAccelFlush() < 0)
"Unable to clear the VirtualBox graphics acceleration queue "
"- the request to the virtual machine failed. Switching to "
"unaccelerated mode.\n");
}
"Failed to clear the VirtualBox graphics acceleration queue. "
"Switching to unaccelerated mode.\n");
/* Mark the record as being updated. */
/* Compute how many bytes we have in the ring buffer. */
/* 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.
*/
{
if (VbglR3VideoAccelFlush() < 0)
"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. */
"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 */
{
}
else
{
/* The following is impressively ugly! */
if (u32Second)
}
/* Mark the record completed. */
}
}
#ifdef PCIACCESS
/* As of X.org server 1.5, we are using the pciaccess library functions to
* access PCI. This structure describes our VMM device. */
/** Structure describing the VMM device */
static const struct pci_id_match vboxVMMDevID =
0, 0, 0 };
#endif
/**
* Initialise VirtualBox's accelerated video extensions.
*
* @returns TRUE on success, FALSE on failure
*/
static Bool
{
#ifdef PCIACCESS
TRACE_ENTRY();
if (devIter)
{
}
if (pVBox->vmmDevInfo)
{
{
"Failed to probe VMM device (vendor=%04x, devid=%04x)\n",
}
else
{
(void **)&pVBox->pVMMDevMemory) != 0)
"Failed to map VMM device range\n");
}
}
#else
TRACE_ENTRY();
/* Locate the device. It should already have been enabled by
the kernel driver. */
(CARD32) ~0);
if (pciTagDev == PCI_NOT_FOUND)
{
"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. */
"The VirtualBox base device contains an invalid memory address.\n");
if (PCI_MAP_IS64BITMEM(pciAddrDev))
"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. */
sizeof(VMMDevMemory));
#endif
{
"Failed to map VirtualBox video extension memory.\n");
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. */
{
"Unable to install dirty rectangle handler for VirtualBox graphics acceleration.\n");
return FALSE;
}
return TRUE;
}
{
int vrc;
uint32_t fMouseFeatures = 0;
TRACE_ENTRY();
vrc = VbglR3Init();
if (RT_FAILURE(vrc))
{
"Failed to initialize the VirtualBox device (rc=%d) - make sure that the VirtualBox guest additions are properly installed. If you are not sure, try reinstalling them. The X Window graphics drivers will run in compatibility mode.\n",
vrc);
}
/* We can't switch to a software cursor at will without help from
* VBoxClient. So tell that to the host and wait for VBoxClient to
* change this. */
if (RT_SUCCESS(vrc))
return rc;
}
{
int rc;
void *p;
TRACE_ENTRY();
return FALSE;
{
/* still open, just re-enable VBVA after CloseScreen was called */
return TRUE;
}
if (p)
{
if (RT_SUCCESS(rc))
{
return TRUE;
}
free(p);
}
xf86DrvMsg(scrnIndex, X_ERROR, "Could not allocate %lu bytes for VMM request\n", (unsigned long)size);
return FALSE;
}
{
}
static void
{
int rc;
if (RT_FAILURE(rc))
{
/* Play safe, and disable the hardware cursor until the next mode
* switch, since obviously something happened that we didn't
* anticipate. */
}
}
static void
{
int rc;
if (vbox_host_uses_hwcursor(pScrn)) {
if (RT_FAILURE(rc)) {
/* Play safe, and disable the hardware cursor until the next mode
* switch, since obviously something happened that we didn't
* anticipate. */
}
}
}
static void
unsigned char *image)
{
int rc;
#ifdef DEBUG_POINTER
#endif
if (RT_FAILURE(rc)) {
/* Play safe, and disable the hardware cursor until the next mode
* switch, since obviously something happened that we didn't
* anticipate. */
}
}
static void
{
/* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
}
static void
{
/* Nothing to do here, as we are telling the guest where the mouse is,
* not vice versa. */
NOREF(x);
NOREF(y);
}
static void
{
}
static void
{
}
static void
{
}
static Bool
{
return vbox_host_uses_hwcursor(pScrn);
}
static unsigned char
color_to_byte(unsigned c)
{
return (c >> 8) & 0xff;
}
static unsigned char *
{
unsigned short w, h, x, y;
if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
"Error invalid cursor dimensions %dx%d\n", w, h);
"Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
sizeRgba = w * h * 4;
if (!c)
"Error failed to alloc %lu bytes for cursor\n",
(unsigned long) sizeRequest);
if (RT_FAILURE(rc))
{
free(p);
return NULL;
}
TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
/*
* 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.
*/
y < h;
{
for (x = 0; x < w; ++x)
{
{
/* opaque, leave AND mask bit at 0 */
{
PUT_PIXEL('X');
}
else
{
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;
#ifdef DEBUG_POINTER
ErrorF("shape = %p\n", p);
vbox_show_shape(w, h, bc, c);
#endif
return p;
}
#ifdef ARGB_CURSOR
static Bool
{
if (!vbox_host_uses_hwcursor(pScrn))
if ( rc
)
)
#ifndef VBOXVIDEO_13
/* Evil hack - we use this as another way of poking the driver to update
* our list of video modes. */
#endif
return rc;
}
static void
{
unsigned short w, h;
unsigned char *pm;
CARD8 *p;
int scrnIndex;
TRACE_ENTRY();
/* Mask must be generated for alpha cursors, that is required by VBox. */
/* note: (michael) the next struct must be 32bit aligned. */
if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
"Error invalid cursor dimensions %dx%d\n", w, h);
"Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
if (!p)
"Error failed to alloc %lu bytes for cursor\n",
(unsigned long)sizeRequest);
reqp = (VMMDevReqMousePointer *)p;
/* Emulate the AND mask. */
/* Init AND mask to 1 */
/*
* 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.
*/
{
unsigned char bitmask = 0x80;
{
if (bitmask == 0)
bitmask = 0x80;
}
/* Point to next source and dest scans */
pc += w;
}
free(p);
}
#endif
{
TRACE_ENTRY();
return FALSE;
if (!pCurs) {
"Failed to create X Window cursor information structures for virtual mouse.\n");
}
if (rc) {
#ifdef ARGB_CURSOR
#endif
/* Hide the host cursor before we initialise if we wish to use a
* software cursor. */
if (pVBox->forceSWCursor)
}
if (!rc)
"Failed to enable mouse pointer integration.\n");
return rc;
}
/**
* 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 pScrn Pointer to a structure describing the X screen in use
*/
{
TRACE_ENTRY();
/* Request not accepted - disable for old hosts. */
"Unable to activate VirtualBox graphics acceleration "
"- the request to the virtual machine failed. "
"You may be running an old version of VirtualBox.\n");
if (!rc)
VbglR3VideoAccelEnable(false);
return rc;
}
/**
* 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 pScrn Pointer to a structure describing the X screen in use
*/
{
int rc;
TRACE_ENTRY();
return FALSE;
rc = VbglR3VideoAccelEnable(false);
if (RT_FAILURE(rc))
{
"Unable to disable VirtualBox graphics acceleration "
"- the request to the virtual machine failed.\n");
}
else
return TRUE;
}
/**
* Inform VBox that we are aware of advanced graphics functions
* (i.e. dynamic resizing, seamless).
*
* @returns TRUE for success, FALSE for failure
*/
{
TRACE_ENTRY();
return FALSE;
}
/**
* Inform VBox that we are no longer aware of advanced graphics functions
* (i.e. dynamic resizing, seamless).
*
* @returns TRUE for success, FALSE for failure
*/
{
TRACE_ENTRY();
return FALSE;
}
/**
* Query the last display change request.
*
* @returns boolean success indicator.
* @param pScrn Pointer to the X screen info structure.
* @param pcx Where to store the horizontal pixel resolution (0 = do not change).
* @param pcy Where to store the vertical pixel resolution (0 = do not change).
* @param pcBits Where to store the bits per pixel (0 = do not change).
* @param iDisplay Where to store the display number the request was for - 0 for the
* primary display, 1 for the first secondary, etc.
*/
{
TRACE_ENTRY();
return FALSE;
if (RT_SUCCESS(rc))
return TRUE;
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to obtain the last resolution requested by the guest, rc=%d.\n", rc);
return FALSE;
}
/**
* Query the host as to whether it likes a specific video mode.
*
* @returns the result of the query
* @param cx the width of the mode being queried
* @param cy the height of the mode being queried
* @param cBits the bpp of the mode being queried
*/
{
TRACE_ENTRY();
return TRUE; /* If we can't ask the host then we like everything. */
}
/**
* Check if any seamless mode is enabled.
* Seamless is only relevant for the newer Xorg modules.
*
* @returns the result of the query
* (true = seamless enabled, false = seamless not enabled)
* @param pScrn Screen info pointer.
*/
{
TRACE_ENTRY();
return FALSE;
return FALSE;
return (mode != VMMDev_Seamless_Disabled);
}
/**
* Save video mode parameters to the registry.
*
* @returns iprt status value
* @param pszName the name to save the mode parameters under
* @param cx mode width
* @param cy mode height
* @param cBits bits per pixel for the mode
*/
{
TRACE_ENTRY();
return FALSE;
}
/**
* Retrieve video mode parameters from the registry.
*
* @returns iprt status value
* @param pszName the name under which the mode parameters are saved
* @param pcx where to store the mode width
* @param pcy where to store the mode height
* @param pcBits where to store the bits per pixel for the mode
*/
{
TRACE_ENTRY();
return FALSE;
if (RT_SUCCESS(rc))
else
return (RT_SUCCESS(rc));
}
/**
* Fills a display mode M with a built-in mode of name pszName and dimensions
* cx and cy.
*/
{
m->type = M_T_BUILTIN;
/* VBox only supports screen widths which are a multiple of 8 */
if (pszName)
{
if (m->name)
}
}
/** vboxvideo's list of standard video modes */
struct
{
/** mode width */
/** mode height */
} vboxStandardModes[] =
{
{ 1600, 1200 },
{ 1440, 1050 },
{ 1280, 960 },
{ 1024, 768 },
{ 800, 600 },
{ 640, 480 },
{ 0, 0 }
};
enum
{
};
/**
* Returns a standard mode which the host likes. Can be called multiple
* times with the index returned by the previous call to get a list of modes.
* @returns the index of the mode in the list, or 0 if no more modes are
* available
* @param pScrn the screen information structure
* @param pScrn->bitsPerPixel
* if this is non-null, only modes with this BPP will be
* returned
* @param cIndex the index of the last mode queried, or 0 to query the
* first mode available. Note: the first index is 1
* @param pcx where to store the mode's width
* @param pcy where to store the mode's height
* @param pcBits where to store the mode's BPP
*/
{
("cIndex = %d, vboxNumStdModes = %d\n", cIndex,
{
continue;
cBits = 32;
cBits = 16;
else
continue;
if (pcx)
if (pcy)
if (pcBits)
return i + 1;
}
return 0;
}
/**
* Returns the preferred video mode. The current order of preference is
* (from highest to least preferred):
* - The mode corresponding to the last size hint from the host
* - The video mode saved from the last session
* - The largest standard mode which the host likes, falling back to
* 640x480x32 as a worst case
* - If the host can't be contacted at all, we return 1024x768x32
*
* The return type is void as we guarantee we will return some mode.
*/
{
/* Query the host for the preferred resolution and colour depth */
TRACE_ENTRY();
{
found = false;
if (!found)
found = false;
if (found)
/* Adjust to a multiple of eight */
cx &= ~7;
if (!found)
if (!found)
{
/* Last resort */
cx = 640;
cy = 480;
cBits = 32;
}
}
else
{
cx = 1024;
cy = 768;
}
if (pcx)
if (pcy)
if (pcx)
}
/* Move a screen mode found to the end of the list, so that RandR will give
* it the highest priority when a mode switch is requested. Returns the mode
* that was previously before the mode in the list in order to allow the
* caller to continue walking the list. */
{
{
}
return pPrev;
}
/**
* Rewrites the first dynamic mode found which is not the current screen mode
* to contain the host's currently preferred screen size, then moves that
* mode to the front of the screen information structure's mode list.
* Additionally, if the current mode is not dynamic, the second dynamic mode
* will be set to match the current mode and also added to the front. This
* ensures that the user can always reset the current size to kick the driver
* to update its mode list.
*/
{
bool found = false;
TRACE_ENTRY();
#ifdef DEBUG
/* Count the number of modes for sanity */
break;
#endif
{
#ifdef DEBUG
#endif
{
if (!found)
else if (pCurrent)
found = true;
}
break;
}
("vboxvideo: no free dynamic mode found. Exiting.\n"));
("pScrn->modes->HDisplay=%u, pScrn->modes->next->HDisplay=%u\n",
("pScrn->modes->VDisplay=%u, pScrn->modes->next->VDisplay=%u\n",
}
/**
* Allocates an empty display mode and links it into the doubly linked list of
* modes pointed to by pScrn->modes. Returns a pointer to the newly allocated
* memory.
*/
{
TRACE_ENTRY();
{
}
else
{
}
return pMode;
}
/**
* Create display mode entries in the screen information structure for each
* of the initial graphics modes that we wish to support. This includes:
* - An initial mode, of the size requested by the caller
* - Two dynamic modes, one of which will be updated to match the last size
* hint from the host on each mode switch, but initially also of the
* requested size
* - Several standard modes, if possible ones that the host likes
* - Any modes that the user requested in xorg.conf/XFree86Config
*/
{
/* For reasons related to the way RandR 1.1 is implemented, we need to
* make sure that the initial mode (more precisely, a mode equal to the
* initial virtual resolution) is always present in the mode list. RandR
* has the assumption build in that there will either be a mode of that
* size present at all times, or that the first mode in the list will
* always be smaller than the initial virtual resolution. Since our
* approach to dynamic resizing isn't quite the way RandR was intended to
* be, and breaks the second assumption, we guarantee the first. */
/* Create our two dynamic modes. */
/* Add standard modes supported by the host */
for ( ; ; )
{
char szName[256];
if (cIndex == 0)
break;
}
/* And finally any modes specified by the user. We assume here that
* the mode names reflect the mode sizes. */
{
{
}
}
}