VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/vboxvideo/vboxutils.c@ 21922

Last change on this file since 21922 was 21912, checked in by vboxsync, 16 years ago

Additions/x11 and common: simplify requests for getting video mode hints and introduce a generic wait event request

  • Property svn:eol-style set to native
File size: 33.8 KB
Line 
1/** @file
2 * VirtualBox X11 Additions graphics driver utility functions
3 */
4
5/*
6 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21#include <VBox/VMMDev.h>
22#include <VBox/VBoxGuestLib.h>
23
24#ifndef PCIACCESS
25# include <xf86Pci.h>
26# include <Pci.h>
27#endif
28
29#include "xf86.h"
30#define NEED_XF86_TYPES
31#ifdef NO_ANSIC
32# include <string.h>
33#else
34# include "xf86_ansic.h"
35#endif
36#include "compiler.h"
37#include "cursorstr.h"
38
39#include "vboxvideo.h"
40
41#define VBOX_MAX_CURSOR_WIDTH 64
42#define VBOX_MAX_CURSOR_HEIGHT 64
43
44/**************************************************************************
45* Debugging functions and macros *
46**************************************************************************/
47
48/* #define DEBUG_POINTER */
49
50#define BOOL_STR(a) ((a) ? "TRUE" : "FALSE")
51
52#ifdef DEBUG_VIDEO
53# define TRACE_LINE() do \
54 { \
55 ErrorF ("%s: line %d\n", __FUNCTION__, __LINE__); \
56 } while(0)
57# define PUT_PIXEL(c) ErrorF ("%c", c)
58#else /* DEBUG_VIDEO not defined */
59# define PUT_PIXEL(c) do { } while(0)
60# define TRACE_LINE() do { } while(0)
61#endif /* DEBUG_VIDEO not defined */
62
63/** Macro to printf an error message and return from a function */
64#define RETERROR(scrnIndex, RetVal, ...) \
65 do \
66 { \
67 xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
68 return RetVal; \
69 } \
70 while (0)
71
72#ifdef DEBUG_POINTER
73static void
74vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image)
75{
76 size_t x, y;
77 unsigned short pitch;
78 CARD32 *color;
79 unsigned char *mask;
80 size_t sizeMask;
81
82 image += offsetof(VMMDevReqMousePointer, pointerData);
83 mask = image;
84 pitch = (w + 7) / 8;
85 sizeMask = (pitch * h + 3) & ~3;
86 color = (CARD32 *)(image + sizeMask);
87
88 TRACE_ENTRY();
89 for (y = 0; y < h; ++y, mask += pitch, color += w)
90 {
91 for (x = 0; x < w; ++x)
92 {
93 if (mask[x / 8] & (1 << (7 - (x % 8))))
94 ErrorF (" ");
95 else
96 {
97 CARD32 c = color[x];
98 if (c == bg)
99 ErrorF("Y");
100 else
101 ErrorF("X");
102 }
103 }
104 ErrorF("\n");
105 }
106}
107#endif
108
109/**************************************************************************
110* Helper functions and macros *
111**************************************************************************/
112
113/* This is called by the X server every time it loads a new cursor to see
114 * whether our "cursor hardware" can handle the cursor. This provides us with
115 * a mechanism (the only one!) to switch back from a software to a hardware
116 * cursor. */
117static Bool
118vbox_host_uses_hwcursor(ScrnInfoPtr pScrn)
119{
120 Bool rc = TRUE;
121 uint32_t fFeatures = 0;
122 VBOXPtr pVBox = pScrn->driverPrivate;
123
124 TRACE_ENTRY();
125 /* We may want to force the use of a software cursor. Currently this is
126 * needed if the guest uses a large virtual resolution, as in this case
127 * the host and guest tend to disagree about the pointer location. */
128 if (pVBox->forceSWCursor)
129 rc = FALSE;
130 /* Query information about mouse integration from the host. */
131 if (rc) {
132 int vrc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);
133 if (RT_FAILURE(vrc)) {
134 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
135 "Unable to determine whether the virtual machine supports mouse pointer integration - request initialization failed with return code %d\n", vrc);
136 rc = FALSE;
137 }
138 }
139 /* If we got the information from the host then make sure the host wants
140 * to draw the pointer. */
141 if (rc)
142 {
143 if (fFeatures & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE)
144 /* Assume this will never be unloaded as long as the X session is
145 * running. */
146 pVBox->mouseDriverLoaded = TRUE;
147 if ( (fFeatures & VMMDEV_MOUSE_HOST_CANNOT_HWPOINTER)
148 || !pVBox->mouseDriverLoaded
149 || !(fFeatures & VMMDEV_MOUSE_HOST_CAN_ABSOLUTE)
150 )
151 rc = FALSE;
152 }
153 TRACE_LOG("rc=%s\n", BOOL_STR(rc));
154 return rc;
155}
156
157/**
158 * Macro to disable VBVA extensions and return, for use when an
159 * unexplained error occurs.
160 */
161#define DISABLE_VBVA_AND_RETURN(pScrn, ...) \
162 do \
163 { \
164 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, __VA_ARGS__); \
165 vboxDisableVbva(pScrn); \
166 pVBox->useVbva = FALSE; \
167 return; \
168 } \
169 while (0)
170
171/**************************************************************************
172* Main functions *
173**************************************************************************/
174
175void
176vbox_close(ScrnInfoPtr pScrn, VBOXPtr pVBox)
177{
178 TRACE_ENTRY();
179
180 xfree (pVBox->reqp);
181 pVBox->reqp = NULL;
182 TRACE_EXIT();
183}
184
185/**
186 * Callback function called by the X server to tell us about dirty
187 * rectangles in the video buffer.
188 *
189 * @param pScreen pointer to the information structure for the current
190 * screen
191 * @param iRects Number of dirty rectangles to update
192 * @param aRects Array of structures containing the coordinates of the
193 * rectangles
194 */
195static void
196vboxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects)
197{
198 VBVACMDHDR cmdHdr;
199 VBOXPtr pVBox;
200 VBVARECORD *pRecord;
201 VBVAMEMORY *pMem;
202 CARD32 indexRecordNext;
203 CARD32 off32Data;
204 CARD32 off32Free;
205 INT32 i32Diff;
206 CARD32 cbHwBufferAvail;
207 int scrnIndex;
208 int i;
209
210 pVBox = pScrn->driverPrivate;
211 TRACE_ENTRY();
212 if (pVBox->useVbva == FALSE)
213 return;
214 pMem = pVBox->pVbvaMemory;
215 /* Just return quietly if VBVA is not currently active. */
216 if ((pMem->fu32ModeFlags & VBVA_F_MODE_ENABLED) == 0)
217 return;
218 scrnIndex = pScrn->scrnIndex;
219
220 for (i = 0; i < iRects; i++)
221 {
222 cmdHdr.x = (int16_t)aRects[i].x1 - pVBox->viewportX;
223 cmdHdr.y = (int16_t)aRects[i].y1 - pVBox->viewportY;
224 cmdHdr.w = (uint16_t)(aRects[i].x2 - aRects[i].x1);
225 cmdHdr.h = (uint16_t)(aRects[i].y2 - aRects[i].y1);
226
227 /* Get the active record and move the pointer along */
228 indexRecordNext = (pMem->indexRecordFree + 1) % VBVA_MAX_RECORDS;
229 if (indexRecordNext == pMem->indexRecordFirst)
230 {
231 /* All slots in the records queue are used. */
232 if (VbglR3VideoAccelFlush() < 0)
233 DISABLE_VBVA_AND_RETURN(pScrn,
234 "Unable to clear the VirtualBox graphics acceleration queue "
235 "- the request to the virtual machine failed. Switching to "
236 "unaccelerated mode.\n");
237 }
238 if (indexRecordNext == pMem->indexRecordFirst)
239 DISABLE_VBVA_AND_RETURN(pScrn,
240 "Failed to clear the VirtualBox graphics acceleration queue. "
241 "Switching to unaccelerated mode.\n");
242 pRecord = &pMem->aRecords[pMem->indexRecordFree];
243 /* Mark the record as being updated. */
244 pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
245 pMem->indexRecordFree = indexRecordNext;
246 /* Compute how many bytes we have in the ring buffer. */
247 off32Free = pMem->off32Free;
248 off32Data = pMem->off32Data;
249 /* Free is writing position. Data is reading position.
250 * Data == Free means buffer is free.
251 * There must be always gap between free and data when data
252 * are in the buffer.
253 * Guest only changes free, host only changes data.
254 */
255 i32Diff = off32Data - off32Free;
256 cbHwBufferAvail = i32Diff > 0 ? i32Diff : VBVA_RING_BUFFER_SIZE + i32Diff;
257 if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD)
258 {
259 if (VbglR3VideoAccelFlush() < 0)
260 DISABLE_VBVA_AND_RETURN(pScrn,
261 "Unable to clear the VirtualBox graphics acceleration queue "
262 "- the request to the virtual machine failed. Switching to "
263 "unaccelerated mode.\n");
264 /* Calculate the free space again. */
265 off32Free = pMem->off32Free;
266 off32Data = pMem->off32Data;
267 i32Diff = off32Data - off32Free;
268 cbHwBufferAvail = i32Diff > 0? i32Diff:
269 VBVA_RING_BUFFER_SIZE + i32Diff;
270 if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD)
271 DISABLE_VBVA_AND_RETURN(pScrn,
272 "No space left in the VirtualBox graphics acceleration command buffer, "
273 "despite clearing the queue. Switching to unaccelerated mode.\n");
274 }
275 /* Now copy the data into the buffer */
276 if (off32Free + sizeof(cmdHdr) < VBVA_RING_BUFFER_SIZE)
277 {
278 memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr, sizeof(cmdHdr));
279 pMem->off32Free = pMem->off32Free + sizeof(cmdHdr);
280 }
281 else
282 {
283 CARD32 u32First = VBVA_RING_BUFFER_SIZE - off32Free;
284 /* The following is impressively ugly! */
285 CARD8 *pu8Second = (CARD8 *)&cmdHdr + u32First;
286 CARD32 u32Second = sizeof(cmdHdr) - u32First;
287 memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr, u32First);
288 if (u32Second)
289 memcpy(&pMem->au8RingBuffer[0], pu8Second, u32Second);
290 pMem->off32Free = u32Second;
291 }
292 pRecord->cbRecord += sizeof(cmdHdr);
293 /* Mark the record completed. */
294 pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
295 }
296}
297
298#ifdef PCIACCESS
299/* As of X.org server 1.5, we are using the pciaccess library functions to
300 * access PCI. This structure describes our VMM device. */
301/** Structure describing the VMM device */
302static const struct pci_id_match vboxVMMDevID =
303{ VMMDEV_VENDORID, VMMDEV_DEVICEID, PCI_MATCH_ANY, PCI_MATCH_ANY,
304 0, 0, 0 };
305#endif
306
307/**
308 * Initialise VirtualBox's accelerated video extensions.
309 *
310 * @returns TRUE on success, FALSE on failure
311 */
312static Bool
313vboxInitVbva(int scrnIndex, ScreenPtr pScreen, VBOXPtr pVBox)
314{
315#ifdef PCIACCESS
316 struct pci_device_iterator *devIter = NULL;
317
318 TRACE_ENTRY();
319 pVBox->vmmDevInfo = NULL;
320 devIter = pci_id_match_iterator_create(&vboxVMMDevID);
321 if (devIter)
322 {
323 pVBox->vmmDevInfo = pci_device_next(devIter);
324 pci_iterator_destroy(devIter);
325 }
326 if (pVBox->vmmDevInfo)
327 {
328 if (pci_device_probe(pVBox->vmmDevInfo) != 0)
329 {
330 xf86DrvMsg (scrnIndex, X_ERROR,
331 "Failed to probe VMM device (vendor=%04x, devid=%04x)\n",
332 pVBox->vmmDevInfo->vendor_id,
333 pVBox->vmmDevInfo->device_id);
334 }
335 else
336 {
337 if (pci_device_map_range(pVBox->vmmDevInfo,
338 pVBox->vmmDevInfo->regions[1].base_addr,
339 pVBox->vmmDevInfo->regions[1].size,
340 PCI_DEV_MAP_FLAG_WRITABLE,
341 (void **)&pVBox->pVMMDevMemory) != 0)
342 xf86DrvMsg (scrnIndex, X_ERROR,
343 "Failed to map VMM device range\n");
344 }
345 }
346#else
347 PCITAG pciTag;
348 ADDRESS pciAddress;
349
350 TRACE_ENTRY();
351 /* Locate the device. It should already have been enabled by
352 the kernel driver. */
353 pciTag = pciFindFirst((unsigned) VMMDEV_DEVICEID << 16 | VMMDEV_VENDORID,
354 (CARD32) ~0);
355 if (pciTag == PCI_NOT_FOUND)
356 {
357 xf86DrvMsg(scrnIndex, X_ERROR,
358 "Could not find the VirtualBox base device on the PCI bus.\n");
359 return FALSE;
360 }
361 /* Read the address and size of the second I/O region. */
362 pciAddress = pciReadLong(pciTag, PCI_MAP_REG_START + 4);
363 if (pciAddress == 0 || pciAddress == (CARD32) ~0)
364 RETERROR(scrnIndex, FALSE,
365 "The VirtualBox base device contains an invalid memory address.\n");
366 if (PCI_MAP_IS64BITMEM(pciAddress))
367 RETERROR(scrnIndex, FALSE,
368 "The VirtualBox base device has a 64bit mapping address. "
369 "This is currently not supported.\n");
370 /* Map it. We hardcode the size as X does not export the
371 function needed to determine it. */
372 pVBox->pVMMDevMemory = xf86MapPciMem(scrnIndex, 0, pciTag, pciAddress,
373 sizeof(VMMDevMemory));
374#endif
375 if (pVBox->pVMMDevMemory == NULL)
376 {
377 xf86DrvMsg(scrnIndex, X_ERROR,
378 "Failed to map VirtualBox video extension memory.\n");
379 return FALSE;
380 }
381 pVBox->pVbvaMemory = &pVBox->pVMMDevMemory->vbvaMemory;
382 /* Set up the dirty rectangle handler. Since this seems to be a
383 delicate operation, and removing it doubly so, this will
384 remain in place whether it is needed or not, and will simply
385 return if VBVA is not active. I assume that it will be active
386 most of the time. */
387 if (ShadowFBInit2(pScreen, NULL, vboxHandleDirtyRect) != TRUE)
388 {
389 xf86DrvMsg(scrnIndex, X_ERROR,
390 "Unable to install dirty rectangle handler for VirtualBox graphics acceleration.\n");
391 return FALSE;
392 }
393 return TRUE;
394}
395
396Bool
397vbox_init(int scrnIndex, VBOXPtr pVBox)
398{
399 Bool rc = TRUE;
400 int vrc;
401
402 TRACE_ENTRY();
403 pVBox->useVbva = FALSE;
404 vrc = VbglR3Init();
405 if (RT_FAILURE(vrc))
406 {
407 xf86DrvMsg(scrnIndex, X_ERROR,
408 "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",
409 vrc);
410 rc = FALSE;
411 }
412 pVBox->useDevice = rc;
413 return rc;
414}
415
416Bool
417vbox_open(ScrnInfoPtr pScrn, ScreenPtr pScreen, VBOXPtr pVBox)
418{
419 int rc;
420 void *p;
421 size_t size;
422 int scrnIndex = pScrn->scrnIndex;
423
424 TRACE_ENTRY();
425
426 if (!pVBox->useDevice)
427 return FALSE;
428
429 if (pVBox->reqp)
430 {
431 /* still open, just re-enable VBVA after CloseScreen was called */
432 pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox);
433 return TRUE;
434 }
435
436 size = vmmdevGetRequestSize(VMMDevReq_SetPointerShape);
437 p = xcalloc(1, size);
438 if (p)
439 {
440 rc = vmmdevInitRequest(p, VMMDevReq_SetPointerShape);
441 if (RT_SUCCESS(rc))
442 {
443 pVBox->reqp = p;
444 pVBox->pCurs = NULL;
445 pVBox->pointerHeaderSize = size;
446 pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox);
447 return TRUE;
448 }
449 xf86DrvMsg(scrnIndex, X_ERROR, "Could not init VMM request: rc = %d\n", rc);
450 xfree(p);
451 }
452 xf86DrvMsg(scrnIndex, X_ERROR, "Could not allocate %lu bytes for VMM request\n", (unsigned long)size);
453 return FALSE;
454}
455
456Bool
457vbox_device_available(VBOXPtr pVBox)
458{
459 return pVBox->useDevice;
460}
461
462static void
463vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
464{
465 int rc;
466
467 TRACE_ENTRY();
468 pVBox->reqp->fFlags = 0;
469 rc = VbglR3SetPointerShapeReq(pVBox->reqp);
470 if (RT_FAILURE(rc))
471 {
472 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not hide the virtual mouse pointer.\n");
473 /* Play safe, and disable the hardware cursor until the next mode
474 * switch, since obviously something happened that we didn't
475 * anticipate. */
476 pVBox->forceSWCursor = TRUE;
477 }
478}
479
480static void
481vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
482{
483 int rc;
484
485 TRACE_ENTRY();
486 if (vbox_host_uses_hwcursor(pScrn)) {
487 pVBox->reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE;
488 rc = VbglR3SetPointerShapeReq(pVBox->reqp);
489 if (RT_FAILURE(rc)) {
490 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not unhide the virtual mouse pointer.\n");
491 /* Play safe, and disable the hardware cursor until the next mode
492 * switch, since obviously something happened that we didn't
493 * anticipate. */
494 pVBox->forceSWCursor = TRUE;
495 }
496 }
497}
498
499static void
500vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
501 unsigned char *image)
502{
503 int rc;
504 VMMDevReqMousePointer *reqp;
505 reqp = (VMMDevReqMousePointer *)image;
506
507 TRACE_LOG("w=%d h=%d size=%d\n", reqp->width, reqp->height, reqp->header.size);
508#ifdef DEBUG_POINTER
509 vbox_show_shape(reqp->width, reqp->height, 0, image);
510#endif
511
512 rc = VbglR3SetPointerShapeReq(reqp);
513 if (RT_FAILURE(rc)) {
514 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to set the virtual mouse pointer image.\n");
515 /* Play safe, and disable the hardware cursor until the next mode
516 * switch, since obviously something happened that we didn't
517 * anticipate. */
518 pVBox->forceSWCursor = TRUE;
519 }
520}
521
522static void
523vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
524{
525 TRACE_ENTRY();
526
527 NOREF(pScrn);
528 NOREF(bg);
529 NOREF(fg);
530 /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
531}
532
533/**
534 * This function is called to set the position of the hardware cursor.
535 * Since we already know the position (exactly where the host pointer is),
536 * we only use this function to poll for whether we need to switch from a
537 * hardware to a software cursor (that is, whether the user has disabled
538 * pointer integration). Sadly this doesn't work the other way round,
539 * as the server updates the software cursor itself without notifying us.
540 */
541static void
542vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
543{
544 VBOXPtr pVBox = pScrn->driverPrivate;
545
546 if (pVBox->accessEnabled && !vbox_host_uses_hwcursor(pScrn))
547 {
548 /* This triggers a cursor image reload, and before reloading, the X
549 * server will check whether we can "handle" the new cursor "in
550 * hardware". We can use this check to force a switch to a software
551 * cursor if we need to do so. */
552 pScrn->EnableDisableFBAccess(pScrn->scrnIndex, FALSE);
553 pScrn->EnableDisableFBAccess(pScrn->scrnIndex, TRUE);
554 }
555}
556
557static void
558vbox_hide_cursor(ScrnInfoPtr pScrn)
559{
560 VBOXPtr pVBox = pScrn->driverPrivate;
561
562 TRACE_ENTRY();
563
564 vbox_vmm_hide_cursor(pScrn, pVBox);
565}
566
567static void
568vbox_show_cursor(ScrnInfoPtr pScrn)
569{
570 VBOXPtr pVBox = pScrn->driverPrivate;
571
572 TRACE_ENTRY();
573
574 vbox_vmm_show_cursor(pScrn, pVBox);
575}
576
577static void
578vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image)
579{
580 VBOXPtr pVBox = pScrn->driverPrivate;
581
582 TRACE_ENTRY();
583
584 vbox_vmm_load_cursor_image(pScrn, pVBox, image);
585}
586
587static Bool
588vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
589{
590 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
591 return vbox_host_uses_hwcursor(pScrn);
592}
593
594static unsigned char
595color_to_byte(unsigned c)
596{
597 return (c >> 8) & 0xff;
598}
599
600static unsigned char *
601vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
602{
603 VBOXPtr pVBox;
604 CursorBitsPtr bitsp;
605 unsigned short w, h, x, y;
606 unsigned char *c, *p, *pm, *ps, *m;
607 size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
608 CARD32 fc, bc, *cp;
609 int rc, scrnIndex = infoPtr->pScrn->scrnIndex;
610 VMMDevReqMousePointer *reqp;
611
612 TRACE_ENTRY();
613 pVBox = infoPtr->pScrn->driverPrivate;
614 bitsp = pCurs->bits;
615 w = bitsp->width;
616 h = bitsp->height;
617
618 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
619 RETERROR(scrnIndex, NULL,
620 "Error invalid cursor dimensions %dx%d\n", w, h);
621
622 if ((bitsp->xhot > w) || (bitsp->yhot > h))
623 RETERROR(scrnIndex, NULL,
624 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
625 bitsp->xhot, bitsp->yhot, w, h);
626
627 srcPitch = PixmapBytePad (bitsp->width, 1);
628 dstPitch = (w + 7) / 8;
629 sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
630 sizeRgba = w * h * 4;
631 pVBox->pointerSize = sizeMask + sizeRgba;
632 sizeRequest = pVBox->pointerSize + pVBox->pointerHeaderSize;
633
634 p = c = xcalloc (1, sizeRequest);
635 if (!c)
636 RETERROR(scrnIndex, NULL,
637 "Error failed to alloc %lu bytes for cursor\n",
638 (unsigned long) sizeRequest);
639
640 rc = vmmdevInitRequest((VMMDevRequestHeader *)p, VMMDevReq_SetPointerShape);
641 if (RT_FAILURE(rc))
642 {
643 xf86DrvMsg(scrnIndex, X_ERROR, "Could not init VMM request: rc = %d\n", rc);
644 xfree(p);
645 return NULL;
646 }
647
648 m = p + offsetof(VMMDevReqMousePointer, pointerData);
649 cp = (CARD32 *)(m + sizeMask);
650
651 TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
652 w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
653 TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp);
654
655 fc = color_to_byte (pCurs->foreBlue)
656 | (color_to_byte (pCurs->foreGreen) << 8)
657 | (color_to_byte (pCurs->foreRed) << 16);
658
659 bc = color_to_byte (pCurs->backBlue)
660 | (color_to_byte (pCurs->backGreen) << 8)
661 | (color_to_byte (pCurs->backRed) << 16);
662
663 /*
664 * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
665 * Xorg:
666 * The mask is a bitmap indicating which parts of the cursor are
667 * transparent and which parts are drawn. The source is a bitmap
668 * indicating which parts of the non-transparent portion of the
669 * the cursor should be painted in the foreground color and which
670 * should be painted in the background color. By default, set bits
671 * indicate the opaque part of the mask bitmap and clear bits
672 * indicate the transparent part.
673 * VBox:
674 * The color data is the XOR mask. The AND mask bits determine
675 * which pixels of the color data (XOR mask) will replace (overwrite)
676 * the screen pixels (AND mask bit = 0) and which ones will be XORed
677 * with existing screen pixels (AND mask bit = 1).
678 * For example when you have the AND mask all 0, then you see the
679 * correct mouse pointer image surrounded by black square.
680 */
681 for (pm = bitsp->mask, ps = bitsp->source, y = 0;
682 y < h;
683 ++y, pm += srcPitch, ps += srcPitch, m += dstPitch)
684 {
685 for (x = 0; x < w; ++x)
686 {
687 if (pm[x / 8] & (1 << (x % 8)))
688 {
689 /* opaque, leave AND mask bit at 0 */
690 if (ps[x / 8] & (1 << (x % 8)))
691 {
692 *cp++ = fc;
693 PUT_PIXEL('X');
694 }
695 else
696 {
697 *cp++ = bc;
698 PUT_PIXEL('*');
699 }
700 }
701 else
702 {
703 /* transparent, set AND mask bit */
704 m[x / 8] |= 1 << (7 - (x % 8));
705 /* don't change the screen pixel */
706 *cp++ = 0;
707 PUT_PIXEL(' ');
708 }
709 }
710 PUT_PIXEL('\n');
711 }
712
713 reqp = (VMMDevReqMousePointer *)p;
714 reqp->width = w;
715 reqp->height = h;
716 reqp->xHot = bitsp->xhot;
717 reqp->yHot = bitsp->yhot;
718 reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
719 reqp->header.size = sizeRequest;
720
721#ifdef DEBUG_POINTER
722 ErrorF("shape = %p\n", p);
723 vbox_show_shape(w, h, bc, c);
724#endif
725
726 return p;
727}
728
729#ifdef ARGB_CURSOR
730static Bool
731vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
732{
733 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
734 Bool rc = TRUE;
735
736 TRACE_ENTRY();
737 if (!vbox_host_uses_hwcursor(pScrn))
738 rc = FALSE;
739 if ( rc
740 && ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
741 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
742 || (pScrn->bitsPerPixel <= 8)
743 )
744 )
745 rc = FALSE;
746 TRACE_LOG("rc=%s\n", BOOL_STR(rc));
747 return rc;
748}
749
750
751static void
752vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
753{
754 VBOXPtr pVBox;
755 VMMDevReqMousePointer *reqp;
756 CursorBitsPtr bitsp;
757 unsigned short w, h;
758 unsigned short cx, cy;
759 unsigned char *pm;
760 CARD32 *pc;
761 size_t sizeRequest, sizeMask;
762 CARD8 *p;
763 int scrnIndex;
764
765 TRACE_ENTRY();
766 pVBox = pScrn->driverPrivate;
767 bitsp = pCurs->bits;
768 w = bitsp->width;
769 h = bitsp->height;
770 scrnIndex = pScrn->scrnIndex;
771
772 /* Mask must be generated for alpha cursors, that is required by VBox. */
773 /* note: (michael) the next struct must be 32bit aligned. */
774 sizeMask = ((w + 7) / 8 * h + 3) & ~3;
775
776 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
777 RETERROR(scrnIndex, ,
778 "Error invalid cursor dimensions %dx%d\n", w, h);
779
780 if ((bitsp->xhot > w) || (bitsp->yhot > h))
781 RETERROR(scrnIndex, ,
782 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
783 bitsp->xhot, bitsp->yhot, w, h);
784
785 pVBox->pointerSize = w * h * 4 + sizeMask;
786 sizeRequest = pVBox->pointerSize + pVBox->pointerHeaderSize;
787 p = xcalloc(1, sizeRequest);
788 if (!p)
789 RETERROR(scrnIndex, ,
790 "Error failed to alloc %lu bytes for cursor\n",
791 (unsigned long)sizeRequest);
792
793 reqp = (VMMDevReqMousePointer *)p;
794 *reqp = *pVBox->reqp;
795 reqp->width = w;
796 reqp->height = h;
797 reqp->xHot = bitsp->xhot;
798 reqp->yHot = bitsp->yhot;
799 reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE
800 | VBOX_MOUSE_POINTER_ALPHA;
801 reqp->header.size = sizeRequest;
802
803 memcpy(p + offsetof(VMMDevReqMousePointer, pointerData) + sizeMask, bitsp->argb, w * h * 4);
804
805 /* Emulate the AND mask. */
806 pm = p + offsetof(VMMDevReqMousePointer, pointerData);
807 pc = bitsp->argb;
808
809 /* Init AND mask to 1 */
810 memset(pm, 0xFF, sizeMask);
811
812 /*
813 * The additions driver must provide the AND mask for alpha cursors. The host frontend
814 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
815 * But if the host does not support ARGB, then it simply uses the AND mask and the color
816 * data to draw a normal color cursor.
817 */
818 for (cy = 0; cy < h; cy++)
819 {
820 unsigned char bitmask = 0x80;
821
822 for (cx = 0; cx < w; cx++, bitmask >>= 1)
823 {
824 if (bitmask == 0)
825 bitmask = 0x80;
826
827 if (pc[cx] >= 0xF0000000)
828 pm[cx / 8] &= ~bitmask;
829 }
830
831 /* Point to next source and dest scans */
832 pc += w;
833 pm += (w + 7) / 8;
834 }
835
836 VbglR3SetPointerShapeReq(reqp);
837 xfree(p);
838}
839#endif
840
841Bool
842vbox_cursor_init(ScreenPtr pScreen)
843{
844 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
845 VBOXPtr pVBox = pScrn->driverPrivate;
846 xf86CursorInfoPtr pCurs = NULL;
847 Bool rc = TRUE;
848
849 TRACE_ENTRY();
850 if (!pVBox->useDevice)
851 return FALSE;
852 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
853 if (!pCurs) {
854 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
855 "Failed to create X Window cursor information structures for virtual mouse.\n");
856 rc = FALSE;
857 }
858 if (rc) {
859 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
860 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
861 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
862 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
863 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
864
865 pCurs->SetCursorColors = vbox_set_cursor_colors;
866 pCurs->SetCursorPosition = vbox_set_cursor_position;
867 pCurs->LoadCursorImage = vbox_load_cursor_image;
868 pCurs->HideCursor = vbox_hide_cursor;
869 pCurs->ShowCursor = vbox_show_cursor;
870 pCurs->UseHWCursor = vbox_use_hw_cursor;
871 pCurs->RealizeCursor = vbox_realize_cursor;
872
873#ifdef ARGB_CURSOR
874 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
875 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
876#endif
877
878 /* Hide the host cursor before we initialise if we wish to use a
879 * software cursor. */
880 if (pVBox->forceSWCursor)
881 vbox_vmm_hide_cursor(pScrn, pVBox);
882 rc = xf86InitCursor(pScreen, pCurs);
883 }
884 if (!rc)
885 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
886 "Failed to enable mouse pointer integration.\n");
887 if (!rc && (pCurs != NULL))
888 xf86DestroyCursorInfoRec(pCurs);
889 return rc;
890}
891
892/**
893 * Inform VBox that we will supply it with dirty rectangle information
894 * and install the dirty rectangle handler.
895 *
896 * @returns TRUE for success, FALSE for failure
897 * @param pScrn Pointer to a structure describing the X screen in use
898 */
899Bool
900vboxEnableVbva(ScrnInfoPtr pScrn)
901{
902 bool rc = TRUE;
903 int scrnIndex = pScrn->scrnIndex;
904 VBOXPtr pVBox = pScrn->driverPrivate;
905
906 TRACE_ENTRY();
907 if (pVBox->useVbva != TRUE)
908 rc = FALSE;
909 if (rc && RT_FAILURE(VbglR3VideoAccelEnable(true)))
910 /* Request not accepted - disable for old hosts. */
911 xf86DrvMsg(scrnIndex, X_ERROR,
912 "Unable to activate VirtualBox graphics acceleration "
913 "- the request to the virtual machine failed. "
914 "You may be running an old version of VirtualBox.\n");
915 pVBox->useVbva = rc;
916 if (!rc)
917 VbglR3VideoAccelEnable(false);
918 return rc;
919}
920
921/**
922 * Inform VBox that we will stop supplying it with dirty rectangle
923 * information. This function is intended to be called when an X
924 * virtual terminal is disabled, or the X server is terminated.
925 *
926 * @returns TRUE for success, FALSE for failure
927 * @param pScrn Pointer to a structure describing the X screen in use
928 */
929Bool
930vboxDisableVbva(ScrnInfoPtr pScrn)
931{
932 int rc;
933 int scrnIndex = pScrn->scrnIndex;
934 VBOXPtr pVBox = pScrn->driverPrivate;
935
936 TRACE_ENTRY();
937 if (pVBox->useVbva != TRUE) /* Ths function should not have been called */
938 return FALSE;
939 rc = VbglR3VideoAccelEnable(false);
940 if (RT_FAILURE(rc))
941 {
942 xf86DrvMsg(scrnIndex, X_ERROR,
943 "Unable to disable VirtualBox graphics acceleration "
944 "- the request to the virtual machine failed.\n");
945 }
946 else
947 memset(pVBox->pVbvaMemory, 0, sizeof(VBVAMEMORY));
948 return TRUE;
949}
950
951/**
952 * Inform VBox that we are aware of advanced graphics functions
953 * (i.e. dynamic resizing, seamless).
954 *
955 * @returns TRUE for success, FALSE for failure
956 */
957Bool
958vboxEnableGraphicsCap(VBOXPtr pVBox)
959{
960 TRACE_ENTRY();
961 if (!pVBox->useDevice)
962 return FALSE;
963 return RT_SUCCESS(VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0));
964}
965
966/**
967 * Inform VBox that we are no longer aware of advanced graphics functions
968 * (i.e. dynamic resizing, seamless).
969 *
970 * @returns TRUE for success, FALSE for failure
971 */
972Bool
973vboxDisableGraphicsCap(VBOXPtr pVBox)
974{
975 TRACE_ENTRY();
976 if (!pVBox->useDevice)
977 return FALSE;
978 return RT_SUCCESS(VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS));
979}
980
981/**
982 * Query the last display change request.
983 *
984 * @returns boolean success indicator.
985 * @param pScrn Pointer to the X screen info structure.
986 * @param pcx Where to store the horizontal pixel resolution (0 = do not change).
987 * @param pcy Where to store the vertical pixel resolution (0 = do not change).
988 * @param pcBits Where to store the bits per pixel (0 = do not change).
989 * @param iDisplay Where to store the display number the request was for - 0 for the
990 * primary display, 1 for the first secondary, etc.
991 */
992Bool
993vboxGetDisplayChangeRequest(ScrnInfoPtr pScrn, uint32_t *pcx, uint32_t *pcy,
994 uint32_t *pcBits, uint32_t *piDisplay)
995{
996 VBOXPtr pVBox = pScrn->driverPrivate;
997 TRACE_ENTRY();
998 if (!pVBox->useDevice)
999 return FALSE;
1000 int rc = VbglR3GetDisplayChangeRequest(pcx, pcy, pcBits, piDisplay, false);
1001 if (RT_SUCCESS(rc))
1002 return TRUE;
1003 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to obtain the last resolution requested by the guest, rc=%d.\n", rc);
1004 return FALSE;
1005}
1006
1007
1008/**
1009 * Query the host as to whether it likes a specific video mode.
1010 *
1011 * @returns the result of the query
1012 * @param cx the width of the mode being queried
1013 * @param cy the height of the mode being queried
1014 * @param cBits the bpp of the mode being queried
1015 */
1016Bool
1017vboxHostLikesVideoMode(ScrnInfoPtr pScrn, uint32_t cx, uint32_t cy, uint32_t cBits)
1018{
1019 VBOXPtr pVBox = pScrn->driverPrivate;
1020 TRACE_ENTRY();
1021 if (!pVBox->useDevice)
1022 return TRUE; /* If we can't ask the host then we like everything. */
1023 return VbglR3HostLikesVideoMode(cx, cy, cBits);
1024}
1025
1026/**
1027 * Save video mode parameters to the registry.
1028 *
1029 * @returns iprt status value
1030 * @param pszName the name to save the mode parameters under
1031 * @param cx mode width
1032 * @param cy mode height
1033 * @param cBits bits per pixel for the mode
1034 */
1035Bool
1036vboxSaveVideoMode(ScrnInfoPtr pScrn, uint32_t cx, uint32_t cy, uint32_t cBits)
1037{
1038 VBOXPtr pVBox = pScrn->driverPrivate;
1039 TRACE_ENTRY();
1040 if (!pVBox->useDevice)
1041 return FALSE;
1042 return RT_SUCCESS(VbglR3SaveVideoMode("SavedMode", cx, cy, cBits));
1043}
1044
1045/**
1046 * Retrieve video mode parameters from the registry.
1047 *
1048 * @returns iprt status value
1049 * @param pszName the name under which the mode parameters are saved
1050 * @param pcx where to store the mode width
1051 * @param pcy where to store the mode height
1052 * @param pcBits where to store the bits per pixel for the mode
1053 */
1054Bool
1055vboxRetrieveVideoMode(ScrnInfoPtr pScrn, uint32_t *pcx, uint32_t *pcy, uint32_t *pcBits)
1056{
1057 VBOXPtr pVBox = pScrn->driverPrivate;
1058 TRACE_ENTRY();
1059 if (!pVBox->useDevice)
1060 return FALSE;
1061 int rc = VbglR3RetrieveVideoMode("SavedMode", pcx, pcy, pcBits);
1062 if (RT_SUCCESS(rc))
1063 TRACE_LOG("Retrieved a video mode of %dx%dx%d\n", *pcx, *pcy, *pcBits);
1064 else
1065 TRACE_LOG("Failed to retrieve video mode, error %d\n", rc);
1066 return (RT_SUCCESS(rc));
1067}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette