VirtualBox

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

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

disabled Xorg guest mouse driver debug logging

  • 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 xf86DrvMsg(scrnIndex, X_ERROR,
402 "Error fd=%d reqp=%p\n", pVBox->vbox_fd,
403 (void *) pVBox->reqp);
404 return FALSE;
405 }
406
407 fd = open (VBOXGUEST_DEVICE_NAME, O_RDWR, 0);
408 if (fd < 0) {
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 xf86DrvMsg(scrnIndex, X_ERROR,
420 "Could not allocate %lu bytes for VMM request\n",
421 (unsigned long) size);
422 goto fail0;
423 }
424
425 rc = vmmdevInitRequest (p, VMMDevReq_SetPointerShape);
426 if (VBOX_FAILURE (rc))
427 {
428 xf86DrvMsg(scrnIndex, X_ERROR,
429 "Could not init VMM request: rc = %d\n", rc);
430 goto fail1;
431 }
432
433 pVBox->vbox_fd = fd;
434 pVBox->reqp = p;
435 pVBox->pCurs = NULL;
436 pVBox->use_hw_cursor = vbox_host_can_hwcursor(pScrn, pVBox);
437 pVBox->set_pointer_shape_size = size;
438 pVBox->pointer_offscreen = FALSE;
439 pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox);
440 return TRUE;
441
442 fail1:
443 xfree (p);
444
445 fail0:
446 if (close (fd))
447 {
448 xf86DrvMsg(scrnIndex, X_ERROR,
449 "Error closing kernel module file descriptor(%d): %s\n",
450 fd, strerror (errno));
451 }
452 return FALSE;
453}
454
455static void vbox_vmm_hide_cursor (ScrnInfoPtr pScrn, VBOXPtr pVBox)
456{
457 pVBox->reqp->fFlags = 0;
458 if (vbox_vmmcall (pScrn, pVBox, &pVBox->reqp->header) != TRUE)
459 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
460 "Could not hide the virtual mouse pointer.\n");
461}
462
463static void vbox_vmm_show_cursor (ScrnInfoPtr pScrn, VBOXPtr pVBox)
464{
465 pVBox->reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE;
466 if (vbox_vmmcall (pScrn, pVBox, &pVBox->reqp->header) != TRUE)
467 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
468 "Could not unhide the virtual mouse pointer.\n");
469}
470
471static void vbox_vmm_load_cursor_image (ScrnInfoPtr pScrn, VBOXPtr pVBox,
472 unsigned char *image)
473{
474 VMMDevReqMousePointer *reqp;
475 reqp = (VMMDevReqMousePointer *) image;
476
477 dolog ("w=%d h=%d size=%d\n",
478 reqp->width,
479 reqp->height,
480 reqp->header.size);
481
482#ifdef DEBUG_X
483 vbox_show_shape (reqp->width, reqp->height, 0, image);
484#endif
485
486 if (vbox_vmmcall (pScrn, pVBox, &reqp->header) != TRUE)
487 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
488 "Unable to set the virtual mouse pointer image.\n");
489}
490
491static void vbox_set_cursor_colors (ScrnInfoPtr pScrn, int bg, int fg)
492{
493 TRACE_ENTRY ();
494
495 (void) pScrn;
496 (void) bg;
497 (void) fg;
498 /* ErrorF ("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
499}
500
501static void
502vbox_set_cursor_position (ScrnInfoPtr pScrn, int x, int y)
503{
504 /* VBOXPtr pVBox = pScrn->driverPrivate; */
505
506 /* TRACE_ENTRY (); */
507
508 /* don't disable the mouse cursor if we go out of our visible area
509 * since the mouse cursor is drawn by the host anyway */
510#if 0
511 if (((x < 0) || (x > pScrn->pScreen->width))
512 || ((y < 0) || (y > pScrn->pScreen->height))) {
513 if (!pVBox->pointer_offscreen) {
514 pVBox->pointer_offscreen = TRUE;
515 vbox_vmm_hide_cursor (pScrn, pVBox);
516 }
517 }
518 else {
519 if (pVBox->pointer_offscreen) {
520 pVBox->pointer_offscreen = FALSE;
521 vbox_vmm_show_cursor (pScrn, pVBox);
522 }
523 }
524#endif
525}
526
527static void vbox_hide_cursor (ScrnInfoPtr pScrn)
528{
529 VBOXPtr pVBox = pScrn->driverPrivate;
530
531 TRACE_ENTRY ();
532
533 vbox_vmm_hide_cursor (pScrn, pVBox);
534}
535
536static void vbox_show_cursor (ScrnInfoPtr pScrn)
537{
538 VBOXPtr pVBox = pScrn->driverPrivate;
539
540 TRACE_ENTRY ();
541
542 vbox_vmm_show_cursor (pScrn, pVBox);
543}
544
545static void vbox_load_cursor_image (ScrnInfoPtr pScrn, unsigned char *image)
546{
547 VBOXPtr pVBox = pScrn->driverPrivate;
548
549 TRACE_ENTRY ();
550
551 vbox_vmm_load_cursor_image (pScrn, pVBox, image);
552}
553
554static Bool
555vbox_use_hw_cursor (ScreenPtr pScreen, CursorPtr pCurs)
556{
557 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
558 VBOXPtr pVBox = pScrn->driverPrivate;
559 return pVBox->use_hw_cursor;
560}
561
562static unsigned char color_to_byte (unsigned c)
563{
564 return (c >> 8) & 0xff;
565}
566
567static unsigned char *
568vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
569{
570 VBOXPtr pVBox;
571 CursorBitsPtr bitsp;
572 unsigned short w, h, x, y;
573 unsigned char *c, *p, *pm, *ps, *m;
574 size_t size, size_rgba, size_mask, src_pitch, dst_pitch;
575 CARD32 fc, bc, *cp;
576 int rc, scrnIndex = infoPtr->pScrn->scrnIndex;
577 VMMDevReqMousePointer *reqp;
578
579 pVBox = infoPtr->pScrn->driverPrivate;
580 bitsp = pCurs->bits;
581 w = bitsp->width;
582 h = bitsp->height;
583
584 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
585 RETERROR(scrnIndex, NULL,
586 "Error invalid cursor dimensions %dx%d\n", w, h);
587
588 if ((bitsp->xhot > w) || (bitsp->yhot > h))
589 RETERROR(scrnIndex, NULL,
590 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
591 bitsp->xhot, bitsp->yhot, w, h);
592
593 src_pitch = PixmapBytePad (bitsp->width, 1);
594 dst_pitch = (w + 7) / 8;
595 size_mask = ((dst_pitch * h) + 3) & (size_t) ~3;
596 size_rgba = w * h * 4;
597 size = size_mask + size_rgba + pVBox->set_pointer_shape_size;
598
599 p = c = xcalloc (1, size);
600 if (!c)
601 RETERROR(scrnIndex, NULL,
602 "Error failed to alloc %lu bytes for cursor\n",
603 (unsigned long) size);
604
605 rc = vmmdevInitRequest ((VMMDevRequestHeader *) p,
606 VMMDevReq_SetPointerShape);
607 if (VBOX_FAILURE (rc))
608 {
609 xf86DrvMsg(scrnIndex, X_ERROR,
610 "Could not init VMM request: rc = %d\n", rc);
611 goto fail0;
612 }
613
614 m = p + offsetof (VMMDevReqMousePointer, pointerData);
615 cp = (CARD32 *) (m + size_mask);
616
617 dolog ("w=%d h=%d sm=%d sr=%d p=%d\n",
618 w, h, (int) size_mask, (int) size_rgba, (int) dst_pitch);
619 dolog ("m=%p c=%p cp=%p\n", m, c, (void *) cp);
620
621 fc = color_to_byte (pCurs->foreBlue)
622 | (color_to_byte (pCurs->foreGreen) << 8)
623 | (color_to_byte (pCurs->foreRed) << 16);
624
625 bc = color_to_byte (pCurs->backBlue)
626 | (color_to_byte (pCurs->backGreen) << 8)
627 | (color_to_byte (pCurs->backRed) << 16);
628
629 /*
630 * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
631 * Xorg:
632 * The mask is a bitmap indicating which parts of the cursor are
633 * transparent and which parts are drawn. The source is a bitmap
634 * indicating which parts of the non-transparent portion of the
635 * the cursor should be painted in the foreground color and which
636 * should be painted in the background color. By default, set bits
637 * indicate the opaque part of the mask bitmap and clear bits
638 * indicate the transparent part.
639 * VBox:
640 * The color data is the XOR mask. The AND mask bits determine
641 * which pixels of the color data (XOR mask) will replace (overwrite)
642 * the screen pixels (AND mask bit = 0) and which ones will be XORed
643 * with existing screen pixels (AND mask bit = 1).
644 * For example when you have the AND mask all 0, then you see the
645 * correct mouse pointer image surrounded by black square.
646 */
647 for (pm = bitsp->mask, ps = bitsp->source, y = 0;
648 y < h;
649 ++y, pm += src_pitch, ps += src_pitch, m += dst_pitch)
650 {
651 for (x = 0; x < w; ++x)
652 {
653 if (pm[x / 8] & (1 << (x % 8)))
654 {
655 /* opaque, leave AND mask bit at 0 */
656 if (ps[x / 8] & (1 << (x % 8)))
657 {
658 *cp++ = fc;
659 PUT_PIXEL ('X');
660 }
661 else
662 {
663 *cp++ = bc;
664 PUT_PIXEL ('*');
665 }
666 }
667 else
668 {
669 /* transparent, set AND mask bit */
670 m[x / 8] |= 1 << (7 - (x % 8));
671 /* don't change the screen pixel */
672 *cp++ = 0;
673 PUT_PIXEL (' ');
674 }
675 }
676 PUT_PIXEL ('\n');
677 }
678
679 reqp = (VMMDevReqMousePointer *) p;
680 reqp->width = w;
681 reqp->height = h;
682 reqp->xHot = bitsp->xhot;
683 reqp->yHot = bitsp->yhot;
684 reqp->fFlags = VBOX_MOUSE_POINTER_SHAPE;
685 reqp->header.size = size;
686
687#ifdef DEBUG_X
688 ErrorF ("shape = %p\n", p);
689 vbox_show_shape (w, h, bc, c);
690#endif
691
692 return p;
693
694 fail0:
695 xfree (p);
696 return NULL;
697}
698
699#ifdef ARGB_CURSOR
700static Bool vbox_use_hw_cursor_argb (ScreenPtr pScreen, CursorPtr pCurs)
701{
702 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
703 return pCurs->bits->height <= VBOX_MAX_CURSOR_HEIGHT
704 && pCurs->bits->width <= VBOX_MAX_CURSOR_WIDTH
705 && pScrn->bitsPerPixel > 8;
706}
707
708static void vbox_load_cursor_argb (ScrnInfoPtr pScrn, CursorPtr pCurs)
709{
710 VBOXPtr pVBox;
711 VMMDevReqMousePointer *reqp;
712 CursorBitsPtr bitsp;
713 unsigned short w, h;
714 unsigned short cx, cy;
715 unsigned char *pm;
716 CARD32 *pc;
717 size_t size, mask_size;
718 CARD8 *p;
719 int scrnIndex;
720
721 pVBox = pScrn->driverPrivate;
722 bitsp = pCurs->bits;
723 w = bitsp->width;
724 h = bitsp->height;
725 scrnIndex = pScrn->scrnIndex;
726
727 /* Mask must be generated for alpha cursors, that is required by VBox. */
728 mask_size = (w + 7) / 8 * h;
729
730 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
731 RETERROR(scrnIndex, ,
732 "Error invalid cursor dimensions %dx%d\n", w, h);
733
734 if ((bitsp->xhot > w) || (bitsp->yhot > h))
735 RETERROR(scrnIndex, ,
736 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
737 bitsp->xhot, bitsp->yhot, w, h);
738
739 size = w * h * 4 + pVBox->set_pointer_shape_size + mask_size;
740 p = xcalloc (1, size);
741 if (!p)
742 RETERROR(scrnIndex, ,
743 "Error failed to alloc %lu bytes for cursor\n",
744 (unsigned long) size);
745
746 reqp = (VMMDevReqMousePointer *) p;
747 *reqp = *pVBox->reqp;
748 reqp->width = w;
749 reqp->height = h;
750 reqp->xHot = bitsp->xhot;
751 reqp->yHot = bitsp->yhot;
752 reqp->fFlags = VBOX_MOUSE_POINTER_SHAPE | VBOX_MOUSE_POINTER_ALPHA;
753 reqp->header.size = size;
754
755 memcpy (p + offsetof (VMMDevReqMousePointer, pointerData) + mask_size,
756 bitsp->argb, w * h * 4);
757
758 /** @ */
759 /* Emulate the AND mask. */
760 pm = p + offsetof (VMMDevReqMousePointer, pointerData);
761 pc = bitsp->argb;
762
763 /* Init AND mask to 1 */
764 memset (pm, 0xFF, mask_size);
765
766 /**
767 * The additions driver must provide the AND mask for alpha cursors. The host frontend
768 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
769 * But if the host does not support ARGB, then it simply uses the AND mask and the color
770 * data to draw a normal color cursor.
771 */
772 for (cy = 0; cy < h; cy++)
773 {
774 unsigned char bitmask = 0x80;
775
776 for (cx = 0; cx < w; cx++, bitmask >>= 1)
777 {
778 if (bitmask == 0)
779 bitmask = 0x80;
780
781 if (pc[cx] >= 0xF0000000)
782 pm[cx / 8] &= ~bitmask;
783 }
784
785 /* Point to next source and dest scans */
786 pc += w;
787 pm += (w + 7) / 8;
788 }
789
790 if (vbox_vmmcall (pScrn, pVBox, &reqp->header) != TRUE)
791 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
792 "Request to virtual machine failed "
793 "- unable to set the virtual mouse pointer ARGB cursor image.\n");
794
795 xfree (p);
796}
797#endif
798
799Bool vbox_cursor_init (ScreenPtr pScreen)
800{
801 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
802 VBOXPtr pVBox = pScrn->driverPrivate;
803 xf86CursorInfoPtr pCurs;
804 Bool rc;
805
806 if (pVBox->use_hw_cursor)
807 {
808 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
809 "The host system is drawing the mouse cursor.\n");
810 }
811 else
812 {
813 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
814 "The guest system is drawing the mouse cursor.\n");
815 return TRUE;
816 }
817
818 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec ();
819 if (!pCurs)
820 RETERROR(pScrn->scrnIndex, FALSE,
821 "Failed to create X Window cursor information structures for virtual mouse.\n");
822
823 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
824 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
825 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
826 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
827 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
828
829 pCurs->SetCursorColors = vbox_set_cursor_colors;
830 pCurs->SetCursorPosition = vbox_set_cursor_position;
831 pCurs->LoadCursorImage = vbox_load_cursor_image;
832 pCurs->HideCursor = vbox_hide_cursor;
833 pCurs->ShowCursor = vbox_show_cursor;
834 pCurs->UseHWCursor = vbox_use_hw_cursor;
835 pCurs->RealizeCursor = vbox_realize_cursor;
836
837#ifdef ARGB_CURSOR
838 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
839 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
840#endif
841
842 rc = xf86InitCursor (pScreen, pCurs);
843 if (rc == TRUE)
844 return TRUE;
845 RETERROR(pScrn->scrnIndex, FALSE, "Failed to enable mouse pointer integration.\n");
846}
847
848/**
849 * Inform VBox that we will supply it with dirty rectangle information
850 * and install the dirty rectangle handler.
851 *
852 * @returns TRUE for success, FALSE for failure
853 * @param pScreen Pointer to a structure describing the X screen in use
854 */
855Bool
856vboxEnableVbva(ScrnInfoPtr pScrn)
857{
858 int scrnIndex = pScrn->scrnIndex;
859 VBOXPtr pVBox = pScrn->driverPrivate;
860
861 if (pVBox->useVbva != TRUE)
862 return FALSE;
863 pVBox->reqe->u32Enable = 1;
864 pVBox->reqe->cbRingBuffer = VBVA_RING_BUFFER_SIZE;
865 pVBox->reqe->fu32Status = 0;
866 if (vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqe)
867 != TRUE)
868 {
869 /* Request not accepted - disable for old hosts. */
870 xf86DrvMsg(scrnIndex, X_ERROR,
871 "Unable to activate VirtualBox graphics acceleration "
872 "- the request to the virtual machine failed. "
873 "You may be running an old version of VirtualBox.\n");
874 pVBox->useVbva = FALSE;
875 pVBox->reqe->u32Enable = 0;
876 pVBox->reqe->cbRingBuffer = VBVA_RING_BUFFER_SIZE;
877 pVBox->reqe->fu32Status = 0;
878 vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqe);
879 return FALSE;
880 }
881 return TRUE;
882}
883
884
885/**
886 * Inform VBox that we will stop supplying it with dirty rectangle
887 * information. This function is intended to be called when an X
888 * virtual terminal is disabled, or the X server is terminated.
889 *
890 * @returns TRUE for success, FALSE for failure
891 * @param pScreen Pointer to a structure describing the X screen in use
892 */
893Bool
894vboxDisableVbva(ScrnInfoPtr pScrn)
895{
896 int scrnIndex = pScrn->scrnIndex;
897 VBOXPtr pVBox = pScrn->driverPrivate;
898
899 if (pVBox->useVbva != TRUE) /* Ths function should not have been called */
900 return FALSE;
901 pVBox->reqe->u32Enable = 0;
902 pVBox->reqe->cbRingBuffer = VBVA_RING_BUFFER_SIZE;
903 pVBox->reqe->fu32Status = 0;
904 if (vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqe)
905 != TRUE)
906 xf86DrvMsg(scrnIndex, X_ERROR,
907 "Unable to disable VirtualBox graphics acceleration "
908 "- the request to the virtual machine failed.\n");
909 else
910 memset(pVBox->pVbvaMemory, 0, sizeof(VBVAMEMORY));
911 return TRUE;
912}
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