VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/xgraphics/vboxutils_68.c@ 8640

Last change on this file since 8640 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.8 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
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#ifndef RT_OS_SOLARIS
34#include <asm/ioctl.h>
35#endif
36
37#include "vboxvideo_68.h"
38
39#define VBOX_MAX_CURSOR_WIDTH 64
40#define VBOX_MAX_CURSOR_HEIGHT 64
41
42#if 0
43#define DEBUG_X
44#endif
45#ifdef DEBUG_X
46#define TRACE_ENTRY() do \
47 { \
48 ErrorF ("%s\n", __FUNCTION__); \
49 } while(0)
50#define TRACE_LINE() do \
51 { \
52 ErrorF ("%s: line %d\n", __FUNCTION__, __LINE__); \
53 } while(0)
54#define PUT_PIXEL(c) ErrorF ("%c", c)
55#define dolog(...) ErrorF (__VA_ARGS__)
56#else
57#define PUT_PIXEL(c) do { } while(0)
58#define TRACE_ENTRY() do { } while(0)
59#define TRACE_LINE() do { } while(0)
60#define dolog(...) do { } while(0)
61#endif
62
63/** Macro to printf an error message and return from a function */
64#define RETERROR(scrnIndex, RetVal, ...) \
65do \
66{ \
67 xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
68 return RetVal; \
69} \
70while (0)
71
72#ifdef DEBUG_X
73static void vbox_show_shape (unsigned short w, unsigned short h,
74 CARD32 bg, unsigned char *image)
75{
76 size_t x, y;
77 unsigned short pitch;
78 CARD32 *color;
79 unsigned char *mask;
80 size_t size_mask;
81
82 image += offsetof (VMMDevReqMousePointer, pointerData);
83 mask = image;
84 pitch = (w + 7) / 8;
85 size_mask = (pitch * h + 3) & ~3;
86 color = (CARD32 *) (image + size_mask);
87
88 TRACE_ENTRY ();
89 for (y = 0; y < h; ++y, mask += pitch, color += w)
90 {
91 for (x = 0; x < w; ++x)
92 {
93 if (mask[x / 8] & (1 << (7 - (x % 8))))
94 ErrorF (" ");
95
96 else
97 {
98 CARD32 c = color[x];
99 if (c == bg)
100 ErrorF ("Y");
101 else
102 ErrorF ("X");
103 }
104 }
105 ErrorF ("\n");
106 }
107}
108#endif
109
110static Bool vbox_vmmcall (ScrnInfoPtr pScrn, VBOXPtr pVBox,
111 VMMDevRequestHeader *hdrp)
112{
113 int err;
114
115 TRACE_ENTRY ();
116#ifdef RT_OS_SOLARIS
117 err = vbglR3GRPerform(hdrp);
118 if (RT_FAILURE(err))
119 {
120 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "VbglR3Perform failed. rc=%d\n", err);
121 err = -1;
122 }
123#else
124 err = ioctl (pVBox->vbox_fd, IOCTL_VBOXGUEST_VMMREQUEST, hdrp);
125#endif
126 if (err < 0)
127 RETERROR(pScrn->scrnIndex, FALSE,
128 "Ioctl call failed during a request to the virtual machine: %s\n",
129 strerror (errno));
130 else
131 if (VBOX_FAILURE (hdrp->rc))
132 RETERROR(pScrn->scrnIndex, FALSE,
133 "A request to the virtual machine returned %d\n",
134 hdrp->rc);
135
136 /* success */
137 return TRUE;
138}
139
140static Bool
141vbox_host_uses_hwcursor(ScrnInfoPtr pScrn)
142{
143 Bool rc = FALSE;
144 VBOXPtr pVBox = pScrn->driverPrivate;
145 VMMDevReqMouseStatus req;
146
147 int vrc = vmmdevInitRequest ((VMMDevRequestHeader*)&req, VMMDevReq_GetMouseStatus);
148 if (RT_FAILURE (vrc))
149 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
150 "Unable to determine whether the virtual machine supports mouse pointer integration - request initialization failed with return code %d\n", rc);
151 if ( RT_SUCCESS(vrc)
152 && (ioctl(pVBox->vbox_fd, IOCTL_VBOXGUEST_VMMREQUEST, (void*)&req) < 0))
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;
237 cmdHdr.y = (int16_t)aRects[i].y1;
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 (VBOX_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 (VBOX_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 (VBOX_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->pointerOffscreen = FALSE;
488 pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox);
489 } else {
490 if (NULL != p) {
491 xfree (p);
492 }
493#ifdef RT_OS_SOLARIS
494 VbglR3Term();
495#else
496 if (close (fd)) {
497 xf86DrvMsg(scrnIndex, X_ERROR,
498 "Error closing kernel module file descriptor(%d): %s\n",
499 fd, strerror (errno));
500 }
501#endif
502 }
503 return rc;
504}
505
506static void vbox_vmm_hide_cursor (ScrnInfoPtr pScrn, VBOXPtr pVBox)
507{
508 pVBox->reqp->fFlags = 0;
509 if (vbox_vmmcall (pScrn, pVBox, &pVBox->reqp->header) != TRUE)
510 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
511 "Could not hide the virtual mouse pointer.\n");
512}
513
514static void vbox_vmm_show_cursor (ScrnInfoPtr pScrn, VBOXPtr pVBox)
515{
516 pVBox->reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE;
517 if (vbox_vmmcall (pScrn, pVBox, &pVBox->reqp->header) != TRUE)
518 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
519 "Could not unhide the virtual mouse pointer.\n");
520}
521
522static void vbox_vmm_load_cursor_image (ScrnInfoPtr pScrn, VBOXPtr pVBox,
523 unsigned char *image)
524{
525 VMMDevReqMousePointer *reqp;
526 reqp = (VMMDevReqMousePointer *) image;
527
528 dolog ("w=%d h=%d size=%d\n",
529 reqp->width,
530 reqp->height,
531 reqp->header.size);
532
533#ifdef DEBUG_X
534 vbox_show_shape (reqp->width, reqp->height, 0, image);
535#endif
536
537 if (vbox_vmmcall (pScrn, pVBox, &reqp->header) != TRUE)
538 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
539 "Unable to set the virtual mouse pointer image.\n");
540}
541
542static void vbox_set_cursor_colors (ScrnInfoPtr pScrn, int bg, int fg)
543{
544 TRACE_ENTRY ();
545
546 (void) pScrn;
547 (void) bg;
548 (void) fg;
549 /* ErrorF ("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
550}
551
552static void
553vbox_set_cursor_position (ScrnInfoPtr pScrn, int x, int y)
554{
555 /* VBOXPtr pVBox = pScrn->driverPrivate; */
556
557 /* TRACE_ENTRY (); */
558
559 /* don't disable the mouse cursor if we go out of our visible area
560 * since the mouse cursor is drawn by the host anyway */
561#if 0
562 if (((x < 0) || (x > pScrn->pScreen->width))
563 || ((y < 0) || (y > pScrn->pScreen->height))) {
564 if (!pVBox->pointerOffscreen) {
565 pVBox->pointerOffscreen = TRUE;
566 vbox_vmm_hide_cursor (pScrn, pVBox);
567 }
568 }
569 else {
570 if (pVBox->pointerOffscreen) {
571 pVBox->pointerOffscreen = FALSE;
572 vbox_vmm_show_cursor (pScrn, pVBox);
573 }
574 }
575#endif
576}
577
578static void vbox_hide_cursor (ScrnInfoPtr pScrn)
579{
580 VBOXPtr pVBox = pScrn->driverPrivate;
581
582 TRACE_ENTRY ();
583
584 vbox_vmm_hide_cursor (pScrn, pVBox);
585}
586
587static void vbox_show_cursor (ScrnInfoPtr pScrn)
588{
589 VBOXPtr pVBox = pScrn->driverPrivate;
590
591 TRACE_ENTRY ();
592
593 vbox_vmm_show_cursor (pScrn, pVBox);
594}
595
596static void vbox_load_cursor_image (ScrnInfoPtr pScrn, unsigned char *image)
597{
598 VBOXPtr pVBox = pScrn->driverPrivate;
599
600 TRACE_ENTRY ();
601
602 vbox_vmm_load_cursor_image (pScrn, pVBox, image);
603}
604
605static Bool
606vbox_use_hw_cursor (ScreenPtr pScreen, CursorPtr pCurs)
607{
608 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
609 return vbox_host_uses_hwcursor(pScrn);
610}
611
612static unsigned char color_to_byte (unsigned c)
613{
614 return (c >> 8) & 0xff;
615}
616
617static unsigned char *
618vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
619{
620 VBOXPtr pVBox;
621 CursorBitsPtr bitsp;
622 unsigned short w, h, x, y;
623 unsigned char *c, *p, *pm, *ps, *m;
624 size_t size, size_rgba, size_mask, src_pitch, dst_pitch;
625 CARD32 fc, bc, *cp;
626 int rc, scrnIndex = infoPtr->pScrn->scrnIndex;
627 VMMDevReqMousePointer *reqp;
628
629 pVBox = infoPtr->pScrn->driverPrivate;
630 bitsp = pCurs->bits;
631 w = bitsp->width;
632 h = bitsp->height;
633
634 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
635 RETERROR(scrnIndex, NULL,
636 "Error invalid cursor dimensions %dx%d\n", w, h);
637
638 if ((bitsp->xhot > w) || (bitsp->yhot > h))
639 RETERROR(scrnIndex, NULL,
640 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
641 bitsp->xhot, bitsp->yhot, w, h);
642
643 src_pitch = PixmapBytePad (bitsp->width, 1);
644 dst_pitch = (w + 7) / 8;
645 size_mask = ((dst_pitch * h) + 3) & (size_t) ~3;
646 size_rgba = w * h * 4;
647 size = size_mask + size_rgba + pVBox->pointerHeaderSize;
648
649 p = c = xcalloc (1, size);
650 if (!c)
651 RETERROR(scrnIndex, NULL,
652 "Error failed to alloc %lu bytes for cursor\n",
653 (unsigned long) size);
654
655 rc = vmmdevInitRequest ((VMMDevRequestHeader *) p,
656 VMMDevReq_SetPointerShape);
657 if (VBOX_FAILURE (rc)) {
658 xfree(p);
659 RETERROR(scrnIndex, NULL,
660 "Could not init VMM request: rc = %d\n", rc);
661 }
662
663 m = p + offsetof (VMMDevReqMousePointer, pointerData);
664 cp = (CARD32 *) (m + size_mask);
665
666 dolog ("w=%d h=%d sm=%d sr=%d p=%d\n",
667 w, h, (int) size_mask, (int) size_rgba, (int) dst_pitch);
668 dolog ("m=%p c=%p cp=%p\n", m, c, (void *) (void *)cp);
669
670 fc = color_to_byte (pCurs->foreBlue)
671 | (color_to_byte (pCurs->foreGreen) << 8)
672 | (color_to_byte (pCurs->foreRed) << 16);
673
674 bc = color_to_byte (pCurs->backBlue)
675 | (color_to_byte (pCurs->backGreen) << 8)
676 | (color_to_byte (pCurs->backRed) << 16);
677
678 /*
679 * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
680 * Xorg:
681 * The mask is a bitmap indicating which parts of the cursor are
682 * transparent and which parts are drawn. The source is a bitmap
683 * indicating which parts of the non-transparent portion of the
684 * the cursor should be painted in the foreground color and which
685 * should be painted in the background color. By default, set bits
686 * indicate the opaque part of the mask bitmap and clear bits
687 * indicate the transparent part.
688 * VBox:
689 * The color data is the XOR mask. The AND mask bits determine
690 * which pixels of the color data (XOR mask) will replace (overwrite)
691 * the screen pixels (AND mask bit = 0) and which ones will be XORed
692 * with existing screen pixels (AND mask bit = 1).
693 * For example when you have the AND mask all 0, then you see the
694 * correct mouse pointer image surrounded by black square.
695 */
696 for (pm = bitsp->mask, ps = bitsp->source, y = 0;
697 y < h;
698 ++y, pm += src_pitch, ps += src_pitch, m += dst_pitch)
699 {
700 for (x = 0; x < w; ++x)
701 {
702 if (pm[x / 8] & (1 << (x % 8)))
703 {
704 /* opaque, leave AND mask bit at 0 */
705 if (ps[x / 8] & (1 << (x % 8)))
706 {
707 *cp++ = fc;
708 PUT_PIXEL ('X');
709 }
710 else
711 {
712 *cp++ = bc;
713 PUT_PIXEL ('*');
714 }
715 }
716 else
717 {
718 /* transparent, set AND mask bit */
719 m[x / 8] |= 1 << (7 - (x % 8));
720 /* don't change the screen pixel */
721 *cp++ = 0;
722 PUT_PIXEL (' ');
723 }
724 }
725 PUT_PIXEL ('\n');
726 }
727
728 reqp = (VMMDevReqMousePointer *) p;
729 reqp->width = w;
730 reqp->height = h;
731 reqp->xHot = bitsp->xhot;
732 reqp->yHot = bitsp->yhot;
733 reqp->fFlags = VBOX_MOUSE_POINTER_SHAPE;
734 reqp->header.size = size;
735
736#ifdef DEBUG_X
737 ErrorF ("shape = %p\n", p);
738 vbox_show_shape (w, h, bc, c);
739#endif
740
741 return p;
742}
743
744#ifdef ARGB_CURSOR
745static Bool vbox_use_hw_cursor_argb (ScreenPtr pScreen, CursorPtr pCurs)
746{
747 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
748 Bool rc = TRUE;
749
750 if (!vbox_host_uses_hwcursor(pScrn))
751 rc = FALSE;
752 if ( rc
753 && ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
754 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
755 || (pScrn->bitsPerPixel <= 8)
756 )
757 )
758 rc = FALSE;
759 return rc;
760}
761
762static void vbox_load_cursor_argb (ScrnInfoPtr pScrn, CursorPtr pCurs)
763{
764 VBOXPtr pVBox;
765 VMMDevReqMousePointer *reqp;
766 CursorBitsPtr bitsp;
767 unsigned short w, h;
768 unsigned short cx, cy;
769 unsigned char *pm;
770 CARD32 *pc;
771 size_t size, mask_size;
772 CARD8 *p;
773 int scrnIndex;
774
775 pVBox = pScrn->driverPrivate;
776 bitsp = pCurs->bits;
777 w = bitsp->width;
778 h = bitsp->height;
779 scrnIndex = pScrn->scrnIndex;
780
781 /* Mask must be generated for alpha cursors, that is required by VBox. */
782 /* @note: (michael) the next struct must be 32bit aligned. */
783 mask_size = ((w + 7) / 8 * h + 3) & ~3;
784
785 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
786 RETERROR(scrnIndex, ,
787 "Error invalid cursor dimensions %dx%d\n", w, h);
788
789 if ((bitsp->xhot > w) || (bitsp->yhot > h))
790 RETERROR(scrnIndex, ,
791 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
792 bitsp->xhot, bitsp->yhot, w, h);
793
794 size = w * h * 4 + pVBox->pointerHeaderSize + mask_size;
795 p = xcalloc (1, size);
796 if (!p)
797 RETERROR(scrnIndex, ,
798 "Error failed to alloc %lu bytes for cursor\n",
799 (unsigned long) size);
800
801 reqp = (VMMDevReqMousePointer *) p;
802 *reqp = *pVBox->reqp;
803 reqp->width = w;
804 reqp->height = h;
805 reqp->xHot = bitsp->xhot;
806 reqp->yHot = bitsp->yhot;
807 reqp->fFlags = VBOX_MOUSE_POINTER_SHAPE | VBOX_MOUSE_POINTER_ALPHA;
808 reqp->header.size = size;
809
810 memcpy (p + offsetof (VMMDevReqMousePointer, pointerData) + mask_size,
811 bitsp->argb, w * h * 4);
812
813 /** @ */
814 /* Emulate the AND mask. */
815 pm = p + offsetof (VMMDevReqMousePointer, pointerData);
816 pc = bitsp->argb;
817
818 /* Init AND mask to 1 */
819 memset (pm, 0xFF, mask_size);
820
821 /**
822 * The additions driver must provide the AND mask for alpha cursors. The host frontend
823 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
824 * But if the host does not support ARGB, then it simply uses the AND mask and the color
825 * data to draw a normal color cursor.
826 */
827 for (cy = 0; cy < h; cy++)
828 {
829 unsigned char bitmask = 0x80;
830
831 for (cx = 0; cx < w; cx++, bitmask >>= 1)
832 {
833 if (bitmask == 0)
834 bitmask = 0x80;
835
836 if (pc[cx] >= 0xF0000000)
837 pm[cx / 8] &= ~bitmask;
838 }
839
840 /* Point to next source and dest scans */
841 pc += w;
842 pm += (w + 7) / 8;
843 }
844
845 if (vbox_vmmcall (pScrn, pVBox, &reqp->header) != TRUE)
846 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
847 "Request to virtual machine failed "
848 "- unable to set the virtual mouse pointer ARGB cursor image.\n");
849
850 xfree (p);
851}
852#endif
853
854Bool vbox_cursor_init (ScreenPtr pScreen)
855{
856 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
857 VBOXPtr pVBox = pScrn->driverPrivate;
858 xf86CursorInfoPtr pCurs;
859 Bool rc;
860
861 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec ();
862 if (!pCurs)
863 RETERROR(pScrn->scrnIndex, FALSE,
864 "Failed to create X Window cursor information structures for virtual mouse.\n");
865
866 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
867 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
868 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
869 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
870 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
871
872 pCurs->SetCursorColors = vbox_set_cursor_colors;
873 pCurs->SetCursorPosition = vbox_set_cursor_position;
874 pCurs->LoadCursorImage = vbox_load_cursor_image;
875 pCurs->HideCursor = vbox_hide_cursor;
876 pCurs->ShowCursor = vbox_show_cursor;
877 pCurs->UseHWCursor = vbox_use_hw_cursor;
878 pCurs->RealizeCursor = vbox_realize_cursor;
879
880#ifdef ARGB_CURSOR
881 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
882 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
883#endif
884
885 rc = xf86InitCursor (pScreen, pCurs);
886 if (rc == TRUE)
887 return TRUE;
888 RETERROR(pScrn->scrnIndex, FALSE, "Failed to enable mouse pointer integration.\n");
889}
890
891/**
892 * Inform VBox that we will supply it with dirty rectangle information
893 * and install the dirty rectangle handler.
894 *
895 * @returns TRUE for success, FALSE for failure
896 * @param pScreen Pointer to a structure describing the X screen in use
897 */
898Bool
899vboxEnableVbva(ScrnInfoPtr pScrn)
900{
901 int scrnIndex = pScrn->scrnIndex;
902 VBOXPtr pVBox = pScrn->driverPrivate;
903
904 if (pVBox->useVbva != TRUE)
905 return FALSE;
906 pVBox->reqe->u32Enable = 1;
907 pVBox->reqe->cbRingBuffer = VBVA_RING_BUFFER_SIZE;
908 pVBox->reqe->fu32Status = 0;
909 if (vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqe)
910 != TRUE)
911 {
912 /* Request not accepted - disable for old hosts. */
913 xf86DrvMsg(scrnIndex, X_ERROR,
914 "Unable to activate VirtualBox graphics acceleration "
915 "- the request to the virtual machine failed. "
916 "You may be running an old version of VirtualBox.\n");
917 pVBox->useVbva = FALSE;
918 pVBox->reqe->u32Enable = 0;
919 pVBox->reqe->cbRingBuffer = VBVA_RING_BUFFER_SIZE;
920 pVBox->reqe->fu32Status = 0;
921 vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqe);
922 return FALSE;
923 }
924 return TRUE;
925}
926
927
928/**
929 * Inform VBox that we will stop supplying it with dirty rectangle
930 * information. This function is intended to be called when an X
931 * virtual terminal is disabled, or the X server is terminated.
932 *
933 * @returns TRUE for success, FALSE for failure
934 * @param pScreen Pointer to a structure describing the X screen in use
935 */
936Bool
937vboxDisableVbva(ScrnInfoPtr pScrn)
938{
939 int scrnIndex = pScrn->scrnIndex;
940 VBOXPtr pVBox = pScrn->driverPrivate;
941
942 if (pVBox->useVbva != TRUE) /* Ths function should not have been called */
943 return FALSE;
944 pVBox->reqe->u32Enable = 0;
945 pVBox->reqe->cbRingBuffer = VBVA_RING_BUFFER_SIZE;
946 pVBox->reqe->fu32Status = 0;
947 if (vbox_vmmcall(pScrn, pVBox, (VMMDevRequestHeader *) pVBox->reqe)
948 != TRUE)
949 xf86DrvMsg(scrnIndex, X_ERROR,
950 "Unable to disable VirtualBox graphics acceleration "
951 "- the request to the virtual machine failed.\n");
952 else
953 memset(pVBox->pVbvaMemory, 0, sizeof(VBVAMEMORY));
954 return TRUE;
955}
956
957
958/**
959 * Query the last display change request.
960 *
961 * @returns iprt status value
962 * @param xres where to store the horizontal pixel resolution requested
963 * (0 = do not change)
964 * @param yres where to store the vertical pixel resolution requested
965 * (0 = do not change)
966 * @param bpp where to store the bits per pixel requeste
967 * (0 = do not change)
968 * @param display Where to store the display number the request was for -
969 * 0 for the primary display, 1 for the first secondary, etc.
970 */
971Bool
972vboxGetDisplayChangeRequest(ScrnInfoPtr pScrn, uint32_t *px, uint32_t *py,
973 uint32_t *pbpp, uint32_t *display)
974{
975 int rc, scrnIndex = pScrn->scrnIndex;
976 VBOXPtr pVBox = pScrn->driverPrivate;
977
978 VMMDevDisplayChangeRequest2 Req = { { 0 } };
979 vmmdevInitRequest(&Req.header, VMMDevReq_GetDisplayChangeRequest2);
980 rc = vbox_vmmcall(pScrn, pVBox, &Req.header);
981 if (RT_SUCCESS(rc))
982 {
983 *px = Req.xres;
984 *py = Req.yres;
985 *pbpp = Req.bpp;
986 *display = Req.display;
987 return TRUE;
988 }
989 xf86DrvMsg(scrnIndex, X_ERROR,
990 "Failed to request the last resolution requested from the guest, rc=%d.\n",
991 rc);
992 return FALSE;
993}
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