VirtualBox

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

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

Additions/x11: some refactoring inside the X11 graphics driver

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