VirtualBox

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

Last change on this file since 31890 was 28800, checked in by vboxsync, 14 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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