VirtualBox

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

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

Additions/x11: * fix for the case in which the video driver is loaded but the mouse driver not - in this case the mouse pointer will be drawn in software. * More logging clean-ups

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