VirtualBox

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

Last change on this file since 15776 was 13835, checked in by vboxsync, 16 years ago

s/VBOX_SUCCESS/RT_SUCCESS/g s/VBOX_FAILURE/RT_FAILURE/g - VBOX_SUCCESS and VBOX_FAILURE have *NOT* been retired (because old habbits die hard) just sligtly deprecated.

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