VirtualBox

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

Last change on this file since 4997 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.3 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 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
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#include <asm/ioctl.h>
30
31#include "vboxvideo.h"
32
33#define VBOX_MAX_CURSOR_WIDTH 64
34#define VBOX_MAX_CURSOR_HEIGHT 64
35
36#if 0
37#define DEBUG_X
38#endif
39#ifdef DEBUG_X
40#define TRACE_ENTRY() for (;;) { \
41 ErrorF ("%s\n", __FUNCTION__); \
42 break; \
43}
44#define PUT_PIXEL(c) ErrorF ("%c", c)
45#define dolog(...) ErrorF (__VA_ARGS__)
46#else
47#define PUT_PIXEL(c) (void) c
48#define TRACE_ENTRY() (void) 0
49#define dolog(...)
50#endif
51
52/** Macro to printf an error message and return from a function */
53#define RETERROR(scrnIndex, RetVal, ...) \
54do \
55{ \
56 xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
57 return RetVal; \
58} \
59while (0)
60
61#ifdef DEBUG_X
62static void vbox_show_shape (unsigned short w, unsigned short h,
63 CARD32 bg, unsigned char *image)
64{
65 size_t x, y;
66 unsigned short pitch;
67 CARD32 *color;
68 unsigned char *mask;
69 size_t size_mask;
70
71 image += offsetof (VMMDevReqMousePointer, pointerData);
72 mask = image;
73 pitch = (w + 7) / 8;
74 size_mask = (pitch * h + 3) & ~3;
75 color = (CARD32 *) (image + size_mask);
76
77 TRACE_ENTRY ();
78 for (y = 0; y < h; ++y, mask += pitch, color += w)
79 {
80 for (x = 0; x < w; ++x)
81 {
82 if (mask[x / 8] & (1 << (7 - (x % 8))))
83 ErrorF (" ");
84
85 else
86 {
87 CARD32 c = color[x];
88 if (c == bg)
89 ErrorF ("Y");
90 else
91 ErrorF ("X");
92 }
93 }
94 ErrorF ("\n");
95 }
96}
97#endif
98
99static Bool vbox_vmmcall (ScrnInfoPtr pScrn, VBOXPtr pVBox,
100 VMMDevRequestHeader *hdrp)
101{
102 int err;
103
104 TRACE_ENTRY ();
105 err = ioctl (pVBox->vbox_fd, IOCTL_VBOXGUEST_VMMREQUEST, hdrp);
106 if (err < 0)
107 RETERROR(pScrn->scrnIndex, FALSE,
108 "Ioctl call failed during a request to the virtual machine: %s\n",
109 strerror (errno));
110 else
111 if (VBOX_FAILURE (hdrp->rc))
112 RETERROR(pScrn->scrnIndex, FALSE,
113 "A request to the virtual machine returned %d\n",
114 hdrp->rc);
115
116 /* success */
117 return TRUE;
118}
119
120static Bool
121vbox_host_can_hwcursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
122{
123 VMMDevReqMouseStatus req;
124 int rc;
125 int scrnIndex = pScrn->scrnIndex;
126
127 rc = vmmdevInitRequest ((VMMDevRequestHeader*)&req, VMMDevReq_GetMouseStatus);
128 if (VBOX_FAILURE (rc))
129 RETERROR(scrnIndex, FALSE,
130 "Unable to determine whether the virtual machine supports mouse pointer integration - request initialization failed with return code %d\n", rc);
131
132 if (ioctl(pVBox->vbox_fd, IOCTL_VBOXGUEST_VMMREQUEST, (void*)&req) < 0)
133 RETERROR(scrnIndex, FALSE,
134 "Unable to determine whether the virtual machine supports mouse pointer integration - request system call failed: %s.\n",
135 strerror(errno));
136
137 return (req.mouseFeatures & VBOXGUEST_MOUSE_HOST_CANNOT_HWPOINTER) ? FALSE : TRUE;
138}
139
140void
141vbox_close(ScrnInfoPtr pScrn, VBOXPtr pVBox)
142{
143 TRACE_ENTRY ();
144
145 xfree (pVBox->reqp);
146 pVBox->reqp = NULL;
147
148 if (close (pVBox->vbox_fd))
149 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
150 "Unable to close the virtual machine device (file %d): %s\n",
151 pVBox->vbox_fd, strerror (errno));
152 pVBox->vbox_fd = -1;
153}
154
155/**
156 * Macro to disable VBVA extensions and return, for use when an
157 * unexplained error occurs.
158 */
159#define DISABLE_VBVA_AND_RETURN(pScrn, ...) \
160do \
161{ \
162 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, __VA_ARGS__); \
163 vboxDisableVbva(pScrn); \
164 pVBox->useVbva = FALSE; \
165 return; \
166} \
167while (0)
168
169/**
170 * Callback function called by the X server to tell us about dirty
171 * rectangles in the video buffer.
172 *
173 * @param pScreen pointer to the information structure for the current
174 * screen
175 * @param iRects Number of dirty rectangles to update
176 * @param aRects Array of structures containing the coordinates of the
177 * rectangles
178 */
179static void
180vboxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects)
181{
182 VBVACMDHDR cmdHdr;
183 VBOXPtr pVBox;
184 VBVARECORD *pRecord;
185 VBVAMEMORY *pMem;
186 CARD32 indexRecordNext;
187 CARD32 off32Data;
188 CARD32 off32Free;
189 INT32 i32Diff;
190 CARD32 cbHwBufferAvail;
191 int scrnIndex;
192 int i;
193
194 pVBox = pScrn->driverPrivate;
195 if (pVBox->useVbva == FALSE)
196 return;
197 pMem = pVBox->pVbvaMemory;
198 /* Just return quietly if VBVA is not currently active. */
199 if ((pMem->fu32ModeFlags & VBVA_F_MODE_ENABLED) == 0)
200 return;
201 scrnIndex = pScrn->scrnIndex;
202
203 for (i = 0; i < iRects; i++)
204 {
205 cmdHdr.x = (int16_t)aRects[i].x1;
206 cmdHdr.y = (int16_t)aRects[i].y1;
207 cmdHdr.w = (uint16_t)(aRects[i].x2 - aRects[i].x1);
208 cmdHdr.h = (uint16_t)(aRects[i].y2 - aRects[i].y1);
209
210 /* Get the active record and move the pointer along */
211 indexRecordNext = (pMem->indexRecordFree + 1)
212 % VBVA_MAX_RECORDS;
213 if (indexRecordNext == pMem->indexRecordFirst)
214 {
215 /* All slots in the records queue are used. */
216 if (vbox_vmmcall(pScrn, pVBox,
217 (VMMDevRequestHeader *) pVBox->reqf) != TRUE)
218 DISABLE_VBVA_AND_RETURN(pScrn,
219 "Unable to clear the VirtualBox graphics acceleration queue "
220 "- the request to the virtual machine failed. Switching to "
221 "unaccelerated mode.\n");
222 }
223 if (indexRecordNext == pMem->indexRecordFirst)
224 DISABLE_VBVA_AND_RETURN(pScrn,
225 "Failed to clear the VirtualBox graphics acceleration queue. "
226 "Switching to unaccelerated mode.\n");
227 pRecord = &pMem->aRecords[pMem->indexRecordFree];
228 /* Mark the record as being updated. */
229 pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
230 pMem->indexRecordFree = indexRecordNext;
231 /* Compute how many bytes we have in the ring buffer. */
232 off32Free = pMem->off32Free;
233 off32Data = pMem->off32Data;
234 /* Free is writing position. Data is reading position.
235 * Data == Free means buffer is free.
236 * There must be always gap between free and data when data
237 * are in the buffer.
238 * Guest only changes free, host only changes data.
239 */
240 i32Diff = off32Data - off32Free;
241 cbHwBufferAvail = i32Diff > 0? i32Diff: VBVA_RING_BUFFER_SIZE
242 + i32Diff;
243 if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD)
244 {
245 if (vbox_vmmcall(pScrn, pVBox,
246 (VMMDevRequestHeader *) pVBox->reqf) != TRUE)
247 DISABLE_VBVA_AND_RETURN(pScrn,
248 "Unable to clear the VirtualBox graphics acceleration queue "
249 "- the request to the virtual machine failed. Switching to "
250 "unaccelerated mode.\n");
251 /* Calculate the free space again. */
252 off32Free = pMem->off32Free;
253 off32Data = pMem->off32Data;
254 i32Diff = off32Data - off32Free;
255 cbHwBufferAvail = i32Diff > 0? i32Diff:
256 VBVA_RING_BUFFER_SIZE + i32Diff;
257 if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD)
258 DISABLE_VBVA_AND_RETURN(pScrn,
259 "No space left in the VirtualBox graphics acceleration command buffer, "
260 "despite clearing the queue. Switching to unaccelerated mode.\n");
261 }
262 /* Now copy the data into the buffer */
263 if (off32Free + sizeof(cmdHdr) < VBVA_RING_BUFFER_SIZE)
264 {
265 memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr,
266 sizeof(cmdHdr));
267 pMem->off32Free = pMem->off32Free + sizeof(cmdHdr);
268 }
269 else
270 {
271 CARD32 u32First = VBVA_RING_BUFFER_SIZE - off32Free;
272 /* The following is impressively ugly! */
273 CARD8 *pu8Second = (CARD8 *)&cmdHdr + u32First;
274 CARD32 u32Second = sizeof(cmdHdr) - u32First;
275 memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr, u32First);
276 if (u32Second)
277 memcpy(&pMem->au8RingBuffer[0], (void *)pu8Second, u32Second);
278 pMem->off32Free = u32Second;
279 }
280 pRecord->cbRecord += sizeof(cmdHdr);
281 /* Mark the record completed. */
282 pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
283 }
284}
285
286
287/**
288 * Initialise VirtualBox's accelerated video extensions.
289 * Note that we assume that the PCI memory is 32bit mapped,
290 * as X doesn't seem to support mapping 64bit memory.
291 *
292 * @returns True on success, false on failure
293 */
294static Bool
295vboxInitVbva(int scrnIndex, ScreenPtr pScreen, VBOXPtr pVBox)
296{
297 PCITAG pciTag;
298 ADDRESS pciAddress;
299 int rc;
300
301 /* Locate the device. It should already have been enabled by
302 the kernel driver. */
303 pciTag = pciFindFirst((unsigned) VMMDEV_DEVICEID << 16 | VMMDEV_VENDORID,
304 (CARD32) ~0);
305 if (pciTag == PCI_NOT_FOUND)
306 {
307 xf86DrvMsg(scrnIndex, X_ERROR,
308 "Could not find the VirtualBox base device on the PCI bus.\n");
309 return FALSE;
310 }
311 /* Read the address and size of the second I/O region. */
312 pciAddress = pciReadLong(pciTag, PCI_MAP_REG_START + 4);
313 if (pciAddress == 0 || pciAddress == (CARD32) ~0)
314 RETERROR(scrnIndex, FALSE,
315 "The VirtualBox base device contains an invalid memory address.\n");
316 if (PCI_MAP_IS64BITMEM(pciAddress))
317 RETERROR(scrnIndex, FALSE,
318 "The VirtualBox base device has a 64bit mapping address. "
319 "This is currently not supported.\n");
320 /* Map it. We hardcode the size as X does not export the
321 function needed to determine it. */
322 pVBox->pVMMDevMemory = xf86MapPciMem(scrnIndex, 0, pciTag, pciAddress,
323 sizeof(VMMDevMemory));
324 if (pVBox->pVMMDevMemory == NULL)
325 {
326 xf86DrvMsg(scrnIndex, X_ERROR,
327 "Failed to map VirtualBox video extension memory.\n");
328 return FALSE;
329 }
330 pVBox->pVbvaMemory = &pVBox->pVMMDevMemory->vbvaMemory;
331 /* Initialise requests */
332 pVBox->reqf = xcalloc(1, vmmdevGetRequestSize(VMMDevReq_VideoAccelFlush));
333 if (!pVBox->reqf)
334 {
335 xf86DrvMsg(scrnIndex, X_ERROR,
336 "Could not allocate memory for VBVA flush request.\n");
337 return FALSE;
338 }
339 rc = vmmdevInitRequest ((VMMDevRequestHeader *) pVBox->reqf,
340 VMMDevReq_VideoAccelFlush);
341 if (VBOX_FAILURE (rc))
342 {
343 xf86DrvMsg(scrnIndex, X_ERROR,
344 "Could not initialise VBVA flush request: return value %d\n", rc);
345 xfree(pVBox->reqf);
346 return FALSE;
347 }
348 pVBox->reqe = xcalloc(1, vmmdevGetRequestSize(VMMDevReq_VideoAccelEnable));
349 if (!pVBox->reqe)
350 {
351 xf86DrvMsg(scrnIndex, X_ERROR,
352 "Could not allocate memory for VBVA enable request.\n");
353 xfree(pVBox->reqf);
354 return FALSE;
355 }
356 rc = vmmdevInitRequest ((VMMDevRequestHeader *) pVBox->reqe,
357 VMMDevReq_VideoAccelEnable);
358 if (VBOX_FAILURE (rc))
359 {
360 xf86DrvMsg(scrnIndex, X_ERROR,
361 "Could not initialise VBVA enable request: return value = %d\n",
362 rc);
363 xfree(pVBox->reqf);
364 xfree(pVBox->reqe);
365 return FALSE;
366 }
367 /* Set up the dirty rectangle handler. Since this seems to be a
368 delicate operation, and removing it doubly so, this will
369 remain in place whether it is needed or not, and will simply
370 return if VBVA is not active. I assume that it will be active
371 most of the time. */
372 if (ShadowFBInit2(pScreen, NULL, vboxHandleDirtyRect) != TRUE)
373 {
374 xf86DrvMsg(scrnIndex, X_ERROR,
375 "Unable to install dirty rectangle handler for VirtualBox graphics acceleration.\n");
376 xfree(pVBox->reqf);
377 xfree(pVBox->reqe);
378 return FALSE;
379 }
380 return TRUE;
381}
382
383Bool
384vbox_open (ScrnInfoPtr pScrn, ScreenPtr pScreen, VBOXPtr pVBox)
385{
386 int fd, rc;
387 void *p;
388 size_t size;
389 int scrnIndex = pScrn->scrnIndex;
390
391 TRACE_ENTRY ();
392
393 pVBox->useVbva = FALSE;
394
395 if (pVBox->vbox_fd != -1 && pVBox->reqp)
396 {
397 /* still open, just re-enable VBVA after CloseScreen was called */
398 pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox);
399 return TRUE;
400 }
401
402 fd = open (VBOXGUEST_DEVICE_NAME, O_RDWR, 0);
403 if (fd < 0)
404 {
405 xf86DrvMsg(scrnIndex, X_ERROR,
406 "Error opening kernel module: %s\n",
407 strerror (errno));
408 return FALSE;
409 }
410
411 size = vmmdevGetRequestSize (VMMDevReq_SetPointerShape);
412
413 p = xcalloc (1, size);
414 if (!p)
415 {
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