VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/vboxvideo/vboxutils_68.c@ 26033

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

warnings

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

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