VirtualBox

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

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

Additions/x11 and linux: make the Additions work with Fedora 9/X.org server 1.5

  • Property svn:eol-style set to native
File size: 31.7 KB
Line 
1/** @file
2 * VirtualBox X11 Additions graphics driver utility functions
3 */
4
5/*
6 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21#include <VBox/VBoxGuest.h>
22#include <VBox/VBoxDev.h>
23
24#ifndef PCIACCESS
25# include <xf86Pci.h>
26# include <Pci.h>
27#endif
28
29#include "xf86.h"
30#define NEED_XF86_TYPES
31#ifdef NO_ANSIC
32# include <string.h>
33#else
34# include "xf86_ansic.h"
35#endif
36#include "compiler.h"
37#include "cursorstr.h"
38
39#include "vboxvideo.h"
40
41#define VBOX_MAX_CURSOR_WIDTH 64
42#define VBOX_MAX_CURSOR_HEIGHT 64
43
44/**************************************************************************
45* Debugging functions and macros *
46**************************************************************************/
47
48/* #define DEBUG_POINTER */
49
50#ifdef DEBUG_michael
51# define DEBUG_VIDEO
52#endif
53
54#define BOOL_STR(a) ((a) ? "TRUE" : "FALSE")
55
56#ifdef DEBUG_VIDEO
57# define TRACE_ENTRY() \
58do { \
59 xf86Msg(X_INFO, __PRETTY_FUNCTION__); \
60 xf86Msg(X_INFO, ": entering\n"); \
61} while(0)
62# define TRACE_EXIT() \
63do { \
64 xf86Msg(X_INFO, __PRETTY_FUNCTION__); \
65 xf86Msg(X_INFO, ": leaving\n"); \
66} while(0)
67# define TRACE_LOG(...) \
68do { \
69 xf86Msg(X_INFO, __PRETTY_FUNCTION__); \
70 xf86Msg(X_INFO, __VA_ARGS__); \
71} while(0)
72# define TRACE_LINE() do \
73 { \
74 ErrorF ("%s: line %d\n", __FUNCTION__, __LINE__); \
75 } while(0)
76# define PUT_PIXEL(c) ErrorF ("%c", c)
77#else /* DEBUG_VIDEO not defined */
78# define PUT_PIXEL(c) do { } while(0)
79# define TRACE_ENTRY() do { } while(0)
80# define TRACE_EXIT() do { } while(0)
81# define TRACE_LOG(...) do { } while(0)
82# define TRACE_LINE() do { } while(0)
83#endif /* DEBUG_VIDEO not defined */
84
85/** Macro to printf an error message and return from a function */
86#define RETERROR(scrnIndex, RetVal, ...) \
87 do \
88 { \
89 xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
90 return RetVal; \
91 } \
92 while (0)
93
94#ifdef DEBUG_POINTER
95static void
96vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image)
97{
98 size_t x, y;
99 unsigned short pitch;
100 CARD32 *color;
101 unsigned char *mask;
102 size_t sizeMask;
103
104 image += offsetof(VMMDevReqMousePointer, pointerData);
105 mask = image;
106 pitch = (w + 7) / 8;
107 sizeMask = (pitch * h + 3) & ~3;
108 color = (CARD32 *)(image + sizeMask);
109
110 TRACE_ENTRY();
111 for (y = 0; y < h; ++y, mask += pitch, color += w)
112 {
113 for (x = 0; x < w; ++x)
114 {
115 if (mask[x / 8] & (1 << (7 - (x % 8))))
116 ErrorF (" ");
117 else
118 {
119 CARD32 c = color[x];
120 if (c == bg)
121 ErrorF("Y");
122 else
123 ErrorF("X");
124 }
125 }
126 ErrorF("\n");
127 }
128}
129#endif
130
131/**************************************************************************
132* Helper functions and macros *
133**************************************************************************/
134
135static Bool
136vbox_host_uses_hwcursor(ScrnInfoPtr pScrn)
137{
138 Bool rc = FALSE;
139 uint32_t fFeatures = 0;
140
141 TRACE_ENTRY();
142 int vrc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);
143 if (VBOX_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", vrc);
146 else
147 {
148 if ( !(fFeatures & VBOXGUEST_MOUSE_HOST_CANNOT_HWPOINTER)
149 && (fFeatures & VBOXGUEST_MOUSE_GUEST_CAN_ABSOLUTE)
150 && (fFeatures & VBOXGUEST_MOUSE_HOST_CAN_ABSOLUTE)
151 )
152 rc = TRUE;
153 }
154 TRACE_LOG("rc=%s\n", BOOL_STR(rc));
155 return rc;
156}
157
158/**
159 * This function checks whether the X server agrees with us about whether
160 * to use a host or a guest-drawn cursor and gives it a nudge if it doesn't.
161 * Disabling and re-enabling framebuffer access was one of the few
162 * reliable (although not particularly nice) methods I could find to
163 * force the server to recheck whether to use a hardware or a software
164 * cursor.
165 */
166static void
167vboxRecheckHWCursor(ScrnInfoPtr pScrn)
168{
169 int vrc;
170 uint32_t fFeatures;
171 VBOXPtr pVBox = pScrn->driverPrivate;
172
173 TRACE_ENTRY();
174 /* Check whether we are using the hardware cursor or not, and whether this
175 has changed since the last time we checked. */
176 vrc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);
177 if (!!(fFeatures & VBOXGUEST_MOUSE_HOST_CAN_ABSOLUTE) != pVBox->usingHWCursor)
178 {
179 pVBox->usingHWCursor = !!(fFeatures & VBOXGUEST_MOUSE_HOST_CAN_ABSOLUTE);
180 /* This triggers a cursor image reload */
181 if (pVBox->accessEnabled)
182 {
183 pScrn->EnableDisableFBAccess(pScrn->scrnIndex, FALSE);
184 pScrn->EnableDisableFBAccess(pScrn->scrnIndex, TRUE);
185 }
186 }
187 TRACE_EXIT();
188}
189
190/**
191 * Macro to disable VBVA extensions and return, for use when an
192 * unexplained error occurs.
193 */
194#define DISABLE_VBVA_AND_RETURN(pScrn, ...) \
195 do \
196 { \
197 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, __VA_ARGS__); \
198 vboxDisableVbva(pScrn); \
199 pVBox->useVbva = FALSE; \
200 return; \
201 } \
202 while (0)
203
204/**************************************************************************
205* Main functions *
206**************************************************************************/
207
208void
209vbox_close(ScrnInfoPtr pScrn, VBOXPtr pVBox)
210{
211 TRACE_ENTRY();
212
213 xfree (pVBox->reqp);
214 pVBox->reqp = NULL;
215 TRACE_EXIT();
216}
217
218/**
219 * Callback function called by the X server to tell us about dirty
220 * rectangles in the video buffer.
221 *
222 * @param pScreen pointer to the information structure for the current
223 * screen
224 * @param iRects Number of dirty rectangles to update
225 * @param aRects Array of structures containing the coordinates of the
226 * rectangles
227 */
228static void
229vboxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects)
230{
231 VBVACMDHDR cmdHdr;
232 VBOXPtr pVBox;
233 VBVARECORD *pRecord;
234 VBVAMEMORY *pMem;
235 CARD32 indexRecordNext;
236 CARD32 off32Data;
237 CARD32 off32Free;
238 INT32 i32Diff;
239 CARD32 cbHwBufferAvail;
240 int scrnIndex;
241 int i;
242
243 pVBox = pScrn->driverPrivate;
244 TRACE_ENTRY();
245 if (pVBox->useVbva == FALSE)
246 return;
247 pMem = pVBox->pVbvaMemory;
248 /* Just return quietly if VBVA is not currently active. */
249 if ((pMem->fu32ModeFlags & VBVA_F_MODE_ENABLED) == 0)
250 return;
251 scrnIndex = pScrn->scrnIndex;
252
253 for (i = 0; i < iRects; i++)
254 {
255 cmdHdr.x = (int16_t)aRects[i].x1;
256 cmdHdr.y = (int16_t)aRects[i].y1;
257 cmdHdr.w = (uint16_t)(aRects[i].x2 - aRects[i].x1);
258 cmdHdr.h = (uint16_t)(aRects[i].y2 - aRects[i].y1);
259
260 /* Get the active record and move the pointer along */
261 indexRecordNext = (pMem->indexRecordFree + 1) % VBVA_MAX_RECORDS;
262 if (indexRecordNext == pMem->indexRecordFirst)
263 {
264 /* All slots in the records queue are used. */
265 if (VbglR3VideoAccelFlush() < 0)
266 DISABLE_VBVA_AND_RETURN(pScrn,
267 "Unable to clear the VirtualBox graphics acceleration queue "
268 "- the request to the virtual machine failed. Switching to "
269 "unaccelerated mode.\n");
270 }
271 if (indexRecordNext == pMem->indexRecordFirst)
272 DISABLE_VBVA_AND_RETURN(pScrn,
273 "Failed to clear the VirtualBox graphics acceleration queue. "
274 "Switching to unaccelerated mode.\n");
275 pRecord = &pMem->aRecords[pMem->indexRecordFree];
276 /* Mark the record as being updated. */
277 pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
278 pMem->indexRecordFree = indexRecordNext;
279 /* Compute how many bytes we have in the ring buffer. */
280 off32Free = pMem->off32Free;
281 off32Data = pMem->off32Data;
282 /* Free is writing position. Data is reading position.
283 * Data == Free means buffer is free.
284 * There must be always gap between free and data when data
285 * are in the buffer.
286 * Guest only changes free, host only changes data.
287 */
288 i32Diff = off32Data - off32Free;
289 cbHwBufferAvail = i32Diff > 0 ? i32Diff : VBVA_RING_BUFFER_SIZE + i32Diff;
290 if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD)
291 {
292 if (VbglR3VideoAccelFlush() < 0)
293 DISABLE_VBVA_AND_RETURN(pScrn,
294 "Unable to clear the VirtualBox graphics acceleration queue "
295 "- the request to the virtual machine failed. Switching to "
296 "unaccelerated mode.\n");
297 /* Calculate the free space again. */
298 off32Free = pMem->off32Free;
299 off32Data = pMem->off32Data;
300 i32Diff = off32Data - off32Free;
301 cbHwBufferAvail = i32Diff > 0? i32Diff:
302 VBVA_RING_BUFFER_SIZE + i32Diff;
303 if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD)
304 DISABLE_VBVA_AND_RETURN(pScrn,
305 "No space left in the VirtualBox graphics acceleration command buffer, "
306 "despite clearing the queue. Switching to unaccelerated mode.\n");
307 }
308 /* Now copy the data into the buffer */
309 if (off32Free + sizeof(cmdHdr) < VBVA_RING_BUFFER_SIZE)
310 {
311 memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr, sizeof(cmdHdr));
312 pMem->off32Free = pMem->off32Free + sizeof(cmdHdr);
313 }
314 else
315 {
316 CARD32 u32First = VBVA_RING_BUFFER_SIZE - off32Free;
317 /* The following is impressively ugly! */
318 CARD8 *pu8Second = (CARD8 *)&cmdHdr + u32First;
319 CARD32 u32Second = sizeof(cmdHdr) - u32First;
320 memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr, u32First);
321 if (u32Second)
322 memcpy(&pMem->au8RingBuffer[0], pu8Second, u32Second);
323 pMem->off32Free = u32Second;
324 }
325 pRecord->cbRecord += sizeof(cmdHdr);
326 /* Mark the record completed. */
327 pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
328 }
329}
330
331#ifdef PCIACCESS
332/* As of X.org server 1.5, we are using the pciaccess library functions to
333 * access PCI. This structure describes our VMM device. */
334/** Structure describing the VMM device */
335static const struct pci_id_match vboxVMMDevID =
336{ VMMDEV_VENDORID, VMMDEV_DEVICEID, PCI_MATCH_ANY, PCI_MATCH_ANY,
337 PCI_MATCH_ANY, PCI_MATCH_ANY, 0 };
338#endif
339
340/**
341 * Initialise VirtualBox's accelerated video extensions.
342 *
343 * @returns TRUE on success, FALSE on failure
344 */
345static Bool
346vboxInitVbva(int scrnIndex, ScreenPtr pScreen, VBOXPtr pVBox)
347{
348#ifdef PCIACCESS
349 struct pci_device_iterator *devIter = NULL;
350
351 TRACE_ENTRY();
352 pVBox->vmmDevInfo = NULL;
353 devIter = pci_id_match_iterator_create(&vboxVMMDevID);
354 if (devIter)
355 {
356 pVBox->vmmDevInfo = pci_device_next(devIter);
357 pci_iterator_destroy(devIter);
358 }
359 if (pVBox->vmmDevInfo)
360 {
361 pci_device_map_range(pVBox->vmmDevInfo,
362 pVBox->vmmDevInfo->regions[1].base_addr,
363 pVBox->vmmDevInfo->regions[1].size,
364 PCI_DEV_MAP_FLAG_WRITABLE,
365 (void **)&pVBox->pVMMDevMemory);
366 }
367#else
368 PCITAG pciTag;
369 ADDRESS pciAddress;
370
371 TRACE_ENTRY();
372 /* Locate the device. It should already have been enabled by
373 the kernel driver. */
374 pciTag = pciFindFirst((unsigned) VMMDEV_DEVICEID << 16 | VMMDEV_VENDORID,
375 (CARD32) ~0);
376 if (pciTag == PCI_NOT_FOUND)
377 {
378 xf86DrvMsg(scrnIndex, X_ERROR,
379 "Could not find the VirtualBox base device on the PCI bus.\n");
380 return FALSE;
381 }
382 /* Read the address and size of the second I/O region. */
383 pciAddress = pciReadLong(pciTag, PCI_MAP_REG_START + 4);
384 if (pciAddress == 0 || pciAddress == (CARD32) ~0)
385 RETERROR(scrnIndex, FALSE,
386 "The VirtualBox base device contains an invalid memory address.\n");
387 if (PCI_MAP_IS64BITMEM(pciAddress))
388 RETERROR(scrnIndex, FALSE,
389 "The VirtualBox base device has a 64bit mapping address. "
390 "This is currently not supported.\n");
391 /* Map it. We hardcode the size as X does not export the
392 function needed to determine it. */
393 pVBox->pVMMDevMemory = xf86MapPciMem(scrnIndex, 0, pciTag, pciAddress,
394 sizeof(VMMDevMemory));
395#endif
396 if (pVBox->pVMMDevMemory == NULL)
397 {
398 xf86DrvMsg(scrnIndex, X_ERROR,
399 "Failed to map VirtualBox video extension memory.\n");
400 return FALSE;
401 }
402 pVBox->pVbvaMemory = &pVBox->pVMMDevMemory->vbvaMemory;
403 /* Set up the dirty rectangle handler. Since this seems to be a
404 delicate operation, and removing it doubly so, this will
405 remain in place whether it is needed or not, and will simply
406 return if VBVA is not active. I assume that it will be active
407 most of the time. */
408 if (ShadowFBInit2(pScreen, NULL, vboxHandleDirtyRect) != TRUE)
409 {
410 xf86DrvMsg(scrnIndex, X_ERROR,
411 "Unable to install dirty rectangle handler for VirtualBox graphics acceleration.\n");
412 return FALSE;
413 }
414 return TRUE;
415}
416
417Bool
418vbox_init(int scrnIndex, VBOXPtr pVBox)
419{
420 Bool rc = TRUE;
421 int vrc;
422
423 TRACE_ENTRY();
424 pVBox->useVbva = FALSE;
425 vrc = VbglR3Init();
426 if (RT_FAILURE(vrc))
427 {
428 xf86DrvMsg(scrnIndex, X_ERROR,
429 "Failed to initialize the VirtualBox device (rc=%d) - make sure that the VirtualBox guest additions are properly installed. If you are not sure, try reinstalling them. The X Window graphics drivers will run in compatibility mode.\n",
430 vrc);
431 rc = FALSE;
432 }
433 pVBox->useDevice = rc;
434 return rc;
435}
436
437Bool
438vbox_open(ScrnInfoPtr pScrn, ScreenPtr pScreen, VBOXPtr pVBox)
439{
440 int rc;
441 void *p;
442 size_t size;
443 int scrnIndex = pScrn->scrnIndex;
444
445 TRACE_ENTRY();
446
447 if (!pVBox->useDevice)
448 return FALSE;
449
450 if (pVBox->reqp)
451 {
452 /* still open, just re-enable VBVA after CloseScreen was called */
453 pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox);
454 return TRUE;
455 }
456
457 size = vmmdevGetRequestSize(VMMDevReq_SetPointerShape);
458 p = xcalloc(1, size);
459 if (p)
460 {
461 rc = vmmdevInitRequest(p, VMMDevReq_SetPointerShape);
462 if (RT_SUCCESS(rc))
463 {
464 pVBox->reqp = p;
465 pVBox->pCurs = NULL;
466 pVBox->pointerHeaderSize = size;
467 pVBox->pointerOffscreen = FALSE;
468 pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox);
469 return TRUE;
470 }
471 xf86DrvMsg(scrnIndex, X_ERROR, "Could not init VMM request: rc = %d\n", rc);
472 xfree(p);
473 }
474 xf86DrvMsg(scrnIndex, X_ERROR, "Could not allocate %lu bytes for VMM request\n", (unsigned long)size);
475 return FALSE;
476}
477
478Bool
479vbox_device_available(VBOXPtr pVBox)
480{
481 return pVBox->useDevice;
482}
483
484static void
485vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
486{
487 int rc;
488
489 TRACE_ENTRY();
490 pVBox->reqp->fFlags = 0;
491 rc = VbglR3SetPointerShapeReq(pVBox->reqp);
492 if (RT_FAILURE(rc))
493 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not hide the virtual mouse pointer.\n");
494}
495
496static void
497vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
498{
499 int rc;
500
501 TRACE_ENTRY();
502 pVBox->reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE;
503 rc = VbglR3SetPointerShapeReq(pVBox->reqp);
504 if (RT_FAILURE(rc))
505 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not unhide the virtual mouse pointer.\n");
506}
507
508static void
509vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
510 unsigned char *image)
511{
512 int rc;
513 VMMDevReqMousePointer *reqp;
514 reqp = (VMMDevReqMousePointer *)image;
515
516 TRACE_LOG("w=%d h=%d size=%d\n", reqp->width, reqp->height, reqp->header.size);
517#ifdef DEBUG_POINTER
518 vbox_show_shape(reqp->width, reqp->height, 0, image);
519#endif
520
521 rc = VbglR3SetPointerShapeReq(reqp);
522 if (RT_FAILURE(rc))
523 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to set the virtual mouse pointer image.\n");
524}
525
526static void
527vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
528{
529 TRACE_ENTRY();
530
531 NOREF(pScrn);
532 NOREF(bg);
533 NOREF(fg);
534 /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
535}
536
537/**
538 * This function is called to set the position of the hardware cursor.
539 * Since we already know the position (exactly where the host pointer is),
540 * we only use this function to poll for whether we need to switch from a
541 * hardware to a software cursor (that is, whether the user has disabled
542 * pointer integration). Sadly we this doesn't work the other way round,
543 * as the server updates the software cursor itself without calling us.
544 */
545static void
546vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
547{
548 vboxRecheckHWCursor(pScrn);
549 /* don't disable the mouse cursor if we go out of our visible area
550 * since the mouse cursor is drawn by the host anyway */
551#if 0
552 if ( (x < 0 || x > pScrn->pScreen->width)
553 || (y < 0 || y > pScrn->pScreen->height))
554 {
555 if (!pVBox->pointerOffscreen)
556 {
557 pVBox->pointerOffscreen = TRUE;
558 vbox_vmm_hide_cursor(pScrn, pVBox);
559 }
560 }
561 else
562 {
563 if (pVBox->pointerOffscreen)
564 {
565 pVBox->pointerOffscreen = FALSE;
566 vbox_vmm_show_cursor(pScrn, pVBox);
567 }
568 }
569#endif
570}
571
572static void
573vbox_hide_cursor(ScrnInfoPtr pScrn)
574{
575 VBOXPtr pVBox = pScrn->driverPrivate;
576
577 TRACE_ENTRY();
578
579 vbox_vmm_hide_cursor(pScrn, pVBox);
580}
581
582static void
583vbox_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
593vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image)
594{
595 VBOXPtr pVBox = pScrn->driverPrivate;
596
597 TRACE_ENTRY();
598
599 vbox_vmm_load_cursor_image(pScrn, pVBox, image);
600}
601
602static Bool
603vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
604{
605 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
606 return vbox_host_uses_hwcursor(pScrn);
607}
608
609static unsigned char
610color_to_byte(unsigned c)
611{
612 return (c >> 8) & 0xff;
613}
614
615static unsigned char *
616vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
617{
618 VBOXPtr pVBox;
619 CursorBitsPtr bitsp;
620 unsigned short w, h, x, y;
621 unsigned char *c, *p, *pm, *ps, *m;
622 size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
623 CARD32 fc, bc, *cp;
624 int rc, scrnIndex = infoPtr->pScrn->scrnIndex;
625 VMMDevReqMousePointer *reqp;
626
627 TRACE_ENTRY();
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 srcPitch = PixmapBytePad (bitsp->width, 1);
643 dstPitch = (w + 7) / 8;
644 sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
645 sizeRgba = w * h * 4;
646 pVBox->pointerSize = sizeMask + sizeRgba;
647 sizeRequest = pVBox->pointerSize + pVBox->pointerHeaderSize;
648
649 p = c = xcalloc (1, sizeRequest);
650 if (!c)
651 RETERROR(scrnIndex, NULL,
652 "Error failed to alloc %lu bytes for cursor\n",
653 (unsigned long) sizeRequest);
654
655 rc = vmmdevInitRequest((VMMDevRequestHeader *)p, VMMDevReq_SetPointerShape);
656 if (VBOX_FAILURE(rc))
657 {
658 xf86DrvMsg(scrnIndex, X_ERROR, "Could not init VMM request: rc = %d\n", rc);
659 xfree(p);
660 return NULL;
661 }
662
663 m = p + offsetof(VMMDevReqMousePointer, pointerData);
664 cp = (CARD32 *)(m + sizeMask);
665
666 TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
667 w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
668 TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (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 += srcPitch, ps += srcPitch, m += dstPitch)
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 = sizeRequest;
735
736#ifdef DEBUG_POINTER
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
746vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
747{
748 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
749 Bool rc = TRUE;
750
751 TRACE_ENTRY();
752 if (!vbox_host_uses_hwcursor(pScrn))
753 rc = FALSE;
754 if ( rc
755 && ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
756 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
757 || (pScrn->bitsPerPixel <= 8)
758 )
759 )
760 rc = FALSE;
761 TRACE_LOG("rc=%s\n", BOOL_STR(rc));
762 return rc;
763}
764
765
766static void
767vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
768{
769 VBOXPtr pVBox;
770 VMMDevReqMousePointer *reqp;
771 CursorBitsPtr bitsp;
772 unsigned short w, h;
773 unsigned short cx, cy;
774 unsigned char *pm;
775 CARD32 *pc;
776 size_t sizeRequest, sizeMask;
777 CARD8 *p;
778 int scrnIndex;
779
780 TRACE_ENTRY();
781 pVBox = pScrn->driverPrivate;
782 bitsp = pCurs->bits;
783 w = bitsp->width;
784 h = bitsp->height;
785 scrnIndex = pScrn->scrnIndex;
786
787 /* Mask must be generated for alpha cursors, that is required by VBox. */
788 /* note: (michael) the next struct must be 32bit aligned. */
789 sizeMask = ((w + 7) / 8 * h + 3) & ~3;
790
791 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
792 RETERROR(scrnIndex, ,
793 "Error invalid cursor dimensions %dx%d\n", w, h);
794
795 if ((bitsp->xhot > w) || (bitsp->yhot > h))
796 RETERROR(scrnIndex, ,
797 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
798 bitsp->xhot, bitsp->yhot, w, h);
799
800 pVBox->pointerSize = w * h * 4 + sizeMask;
801 sizeRequest = pVBox->pointerSize + pVBox->pointerHeaderSize;
802 p = xcalloc(1, sizeRequest);
803 if (!p)
804 RETERROR(scrnIndex, ,
805 "Error failed to alloc %lu bytes for cursor\n",
806 (unsigned long)sizeRequest);
807
808 reqp = (VMMDevReqMousePointer *)p;
809 *reqp = *pVBox->reqp;
810 reqp->width = w;
811 reqp->height = h;
812 reqp->xHot = bitsp->xhot;
813 reqp->yHot = bitsp->yhot;
814 reqp->fFlags = VBOX_MOUSE_POINTER_SHAPE | VBOX_MOUSE_POINTER_ALPHA;
815 reqp->header.size = sizeRequest;
816
817 memcpy(p + offsetof(VMMDevReqMousePointer, pointerData) + sizeMask, bitsp->argb, w * h * 4);
818
819 /* Emulate the AND mask. */
820 pm = p + offsetof(VMMDevReqMousePointer, pointerData);
821 pc = bitsp->argb;
822
823 /* Init AND mask to 1 */
824 memset(pm, 0xFF, sizeMask);
825
826 /*
827 * The additions driver must provide the AND mask for alpha cursors. The host frontend
828 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
829 * But if the host does not support ARGB, then it simply uses the AND mask and the color
830 * data to draw a normal color cursor.
831 */
832 for (cy = 0; cy < h; cy++)
833 {
834 unsigned char bitmask = 0x80;
835
836 for (cx = 0; cx < w; cx++, bitmask >>= 1)
837 {
838 if (bitmask == 0)
839 bitmask = 0x80;
840
841 if (pc[cx] >= 0xF0000000)
842 pm[cx / 8] &= ~bitmask;
843 }
844
845 /* Point to next source and dest scans */
846 pc += w;
847 pm += (w + 7) / 8;
848 }
849
850 VbglR3SetPointerShapeReq(reqp);
851 xfree(p);
852}
853#endif
854
855Bool
856vbox_cursor_init(ScreenPtr pScreen)
857{
858 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
859 VBOXPtr pVBox = pScrn->driverPrivate;
860 xf86CursorInfoPtr pCurs;
861 Bool rc;
862
863 TRACE_ENTRY();
864 if (!pVBox->useDevice)
865 return FALSE;
866 /* Initially assume we are using a hardware cursor, but this is
867 updated every time the mouse moves anyway. */
868 pVBox->usingHWCursor = TRUE;
869 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
870 if (!pCurs)
871 RETERROR(pScrn->scrnIndex, FALSE,
872 "Failed to create X Window cursor information structures for virtual mouse.\n");
873
874 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
875 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
876 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
877 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
878 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
879
880 pCurs->SetCursorColors = vbox_set_cursor_colors;
881 pCurs->SetCursorPosition = vbox_set_cursor_position;
882 pCurs->LoadCursorImage = vbox_load_cursor_image;
883 pCurs->HideCursor = vbox_hide_cursor;
884 pCurs->ShowCursor = vbox_show_cursor;
885 pCurs->UseHWCursor = vbox_use_hw_cursor;
886 pCurs->RealizeCursor = vbox_realize_cursor;
887
888#ifdef ARGB_CURSOR
889 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
890 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
891#endif
892
893 rc = xf86InitCursor(pScreen, pCurs);
894 if (rc == TRUE)
895 return TRUE;
896 RETERROR(pScrn->scrnIndex, FALSE, "Failed to enable mouse pointer integration.\n");
897}
898
899/**
900 * Inform VBox that we will supply it with dirty rectangle information
901 * and install the dirty rectangle handler.
902 *
903 * @returns TRUE for success, FALSE for failure
904 * @param pScrn Pointer to a structure describing the X screen in use
905 */
906Bool
907vboxEnableVbva(ScrnInfoPtr pScrn)
908{
909 int rc;
910 int scrnIndex = pScrn->scrnIndex;
911 VBOXPtr pVBox = pScrn->driverPrivate;
912
913 TRACE_ENTRY();
914 if (pVBox->useVbva != TRUE)
915 return FALSE;
916 rc = VbglR3VideoAccelEnable(true);
917 if (RT_FAILURE(rc))
918 {
919 /* Request not accepted - disable for old hosts. */
920 xf86DrvMsg(scrnIndex, X_ERROR,
921 "Unable to activate VirtualBox graphics acceleration "
922 "- the request to the virtual machine failed. "
923 "You may be running an old version of VirtualBox.\n");
924 pVBox->useVbva = FALSE;
925 VbglR3VideoAccelEnable(false);
926 return FALSE;
927 }
928 return TRUE;
929}
930
931/**
932 * Inform VBox that we will stop supplying it with dirty rectangle
933 * information. This function is intended to be called when an X
934 * virtual terminal is disabled, or the X server is terminated.
935 *
936 * @returns TRUE for success, FALSE for failure
937 * @param pScrn Pointer to a structure describing the X screen in use
938 */
939Bool
940vboxDisableVbva(ScrnInfoPtr pScrn)
941{
942 int rc;
943 int scrnIndex = pScrn->scrnIndex;
944 VBOXPtr pVBox = pScrn->driverPrivate;
945
946 TRACE_ENTRY();
947 if (pVBox->useVbva != TRUE) /* Ths function should not have been called */
948 return FALSE;
949 rc = VbglR3VideoAccelEnable(false);
950 if (RT_FAILURE(rc))
951 {
952 xf86DrvMsg(scrnIndex, X_ERROR,
953 "Unable to disable VirtualBox graphics acceleration "
954 "- the request to the virtual machine failed.\n");
955 }
956 else
957 memset(pVBox->pVbvaMemory, 0, sizeof(VBVAMEMORY));
958 return TRUE;
959}
960
961/**
962 * Inform VBox that we are aware of advanced graphics functions
963 * (i.e. dynamic resizing, seamless).
964 *
965 * @returns TRUE for success, FALSE for failure
966 */
967Bool
968vboxEnableGraphicsCap(VBOXPtr pVBox)
969{
970 TRACE_ENTRY();
971 if (!pVBox->useDevice)
972 return FALSE;
973 return RT_SUCCESS(VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0));
974}
975
976/**
977 * Inform VBox that we are no longer aware of advanced graphics functions
978 * (i.e. dynamic resizing, seamless).
979 *
980 * @returns TRUE for success, FALSE for failure
981 */
982Bool
983vboxDisableGraphicsCap(VBOXPtr pVBox)
984{
985 TRACE_ENTRY();
986 if (!pVBox->useDevice)
987 return FALSE;
988 return RT_SUCCESS(VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS));
989}
990
991/**
992 * Query the last display change request.
993 *
994 * @returns boolean success indicator.
995 * @param pScrn Pointer to the X screen info structure.
996 * @param pcx Where to store the horizontal pixel resolution (0 = do not change).
997 * @param pcy Where to store the vertical pixel resolution (0 = do not change).
998 * @param pcBits Where to store the bits per pixel (0 = do not change).
999 * @param iDisplay Where to store the display number the request was for - 0 for the
1000 * primary display, 1 for the first secondary, etc.
1001 */
1002Bool
1003vboxGetDisplayChangeRequest(ScrnInfoPtr pScrn, uint32_t *pcx, uint32_t *pcy,
1004 uint32_t *pcBits, uint32_t *piDisplay,
1005 VBOXPtr pVBox)
1006{
1007 TRACE_ENTRY();
1008 if (!pVBox->useDevice)
1009 return FALSE;
1010 int rc = VbglR3GetLastDisplayChangeRequest(pcx, pcy, pcBits, piDisplay);
1011 if (RT_SUCCESS(rc))
1012 return TRUE;
1013 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to obtain the last resolution requested by the guest, rc=%d.\n", rc);
1014 return FALSE;
1015}
1016
1017
1018/**
1019 * Query the host as to whether it likes a specific video mode.
1020 *
1021 * @returns the result of the query
1022 * @param cx the width of the mode being queried
1023 * @param cy the height of the mode being queried
1024 * @param cBits the bpp of the mode being queried
1025 */
1026Bool
1027vboxHostLikesVideoMode(uint32_t cx, uint32_t cy, uint32_t cBits)
1028{
1029 TRACE_ENTRY();
1030 return VbglR3HostLikesVideoMode(cx, cy, cBits);
1031}
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