VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/xgraphics/vboxutils.c@ 6356

Last change on this file since 6356 was 6301, checked in by vboxsync, 17 years ago

Solaris guest additions: Got x11 vboxvideo working. Changed guest kernel driver ioctl, verified with previously working additions.

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