VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/vboxvideo/vboxutils.c@ 17154

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

Additions/x11/vboxvideo: hide the host mouse pointer at start if we are using a software cursor

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette