VirtualBox

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

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

Fixed an isse when the Xorg server was not restarted after an X session was terminated

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