VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/vboxvideo/vboxutils_68.c@ 21219

Last change on this file since 21219 was 21218, checked in by vboxsync, 16 years ago

Additions: Use VBoxGuestLib.h instead of VBoxGuest.h where applicable.

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