VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/xgraphics/vboxutils.c@ 6484

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

Additions (x11): added randr 1.2 support to the graphics driver

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