VirtualBox

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

Last change on this file since 22683 was 21227, checked in by vboxsync, 16 years ago

VBoxGuest.h/VMMDev.h/VBoxGuestLib.h usage cleanup.

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