VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/xgraphics/vboxutils.c@ 430

Last change on this file since 430 was 430, checked in by vboxsync, 18 years ago

The VBVA command record must be marked as partial when it is being created.

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