VirtualBox

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

Last change on this file since 27529 was 27529, checked in by vboxsync, 15 years ago

Additions/x11/vboxvideo: vboxvideo part of dynamic resizing for older X.Org Additions

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