VirtualBox

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

Last change on this file since 31890 was 28800, checked in by vboxsync, 14 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 44.5 KB
Line 
1/** @file
2 * VirtualBox X11 Additions graphics driver utility functions
3 */
4
5/*
6 * Copyright (C) 2006-2007 Oracle Corporation
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
17#include <VBox/VMMDev.h>
18#include <VBox/VBoxGuestLib.h>
19
20#ifndef PCIACCESS
21# include <xf86Pci.h>
22# include <Pci.h>
23#endif
24
25#include "xf86.h"
26#define NEED_XF86_TYPES
27#ifdef NO_ANSIC
28# include <string.h>
29#else
30# include "xf86_ansic.h"
31#endif
32#include "compiler.h"
33#include "cursorstr.h"
34
35#include "vboxvideo.h"
36
37#define VBOX_MAX_CURSOR_WIDTH 64
38#define VBOX_MAX_CURSOR_HEIGHT 64
39
40/**************************************************************************
41* Debugging functions and macros *
42**************************************************************************/
43
44/* #define DEBUG_POINTER */
45
46#ifdef DEBUG
47# define PUT_PIXEL(c) ErrorF ("%c", c)
48#else /* DEBUG_VIDEO not defined */
49# define PUT_PIXEL(c) do { } while(0)
50#endif /* DEBUG_VIDEO not defined */
51
52/** Macro to printf an error message and return from a function */
53#define RETERROR(scrnIndex, RetVal, ...) \
54 do \
55 { \
56 xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
57 return RetVal; \
58 } \
59 while (0)
60
61#ifdef DEBUG_POINTER
62static void
63vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image)
64{
65 size_t x, y;
66 unsigned short pitch;
67 CARD32 *color;
68 unsigned char *mask;
69 size_t sizeMask;
70
71 image += offsetof(VMMDevReqMousePointer, pointerData);
72 mask = image;
73 pitch = (w + 7) / 8;
74 sizeMask = (pitch * h + 3) & ~3;
75 color = (CARD32 *)(image + sizeMask);
76
77 TRACE_ENTRY();
78 for (y = 0; y < h; ++y, mask += pitch, color += w)
79 {
80 for (x = 0; x < w; ++x)
81 {
82 if (mask[x / 8] & (1 << (7 - (x % 8))))
83 ErrorF (" ");
84 else
85 {
86 CARD32 c = color[x];
87 if (c == bg)
88 ErrorF("Y");
89 else
90 ErrorF("X");
91 }
92 }
93 ErrorF("\n");
94 }
95}
96#endif
97
98/**************************************************************************
99* Helper functions and macros *
100**************************************************************************/
101
102/* This is called by the X server every time it loads a new cursor to see
103 * whether our "cursor hardware" can handle the cursor. This provides us with
104 * a mechanism (the only one!) to switch back from a software to a hardware
105 * cursor. */
106static Bool
107vbox_host_uses_hwcursor(ScrnInfoPtr pScrn)
108{
109 Bool rc = TRUE;
110 uint32_t fFeatures = 0;
111 VBOXPtr pVBox = pScrn->driverPrivate;
112
113 TRACE_ENTRY();
114 /* We may want to force the use of a software cursor. Currently this is
115 * needed if the guest uses a large virtual resolution, as in this case
116 * the host and guest tend to disagree about the pointer location. */
117 if (pVBox->forceSWCursor)
118 rc = FALSE;
119 /* Query information about mouse integration from the host. */
120 if (rc) {
121 int vrc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);
122 if (RT_FAILURE(vrc)) {
123 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
124 "Unable to determine whether the virtual machine supports mouse pointer integration - request initialization failed with return code %d\n", vrc);
125 rc = FALSE;
126 }
127 }
128 /* If we got the information from the host then make sure the host wants
129 * to draw the pointer. */
130 if (rc)
131 {
132 if ( (fFeatures & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE)
133#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) >= 5
134 /* As of this version (server 1.6) all major Linux releases
135 * are known to handle USB tablets correctly. */
136 || (fFeatures & VMMDEV_MOUSE_HOST_HAS_ABS_DEV)
137#endif
138 )
139 /* Assume this will never be unloaded as long as the X session is
140 * running. */
141 pVBox->guestCanAbsolute = TRUE;
142 if ( (fFeatures & VMMDEV_MOUSE_HOST_CANNOT_HWPOINTER)
143 || !pVBox->guestCanAbsolute
144 || !(fFeatures & VMMDEV_MOUSE_HOST_CAN_ABSOLUTE)
145 )
146 rc = FALSE;
147 }
148 TRACE_LOG("rc=%s\n", BOOL_STR(rc));
149 return rc;
150}
151
152/**
153 * Macro to disable VBVA extensions and return, for use when an
154 * unexplained error occurs.
155 */
156#define DISABLE_VBVA_AND_RETURN(pScrn, ...) \
157 do \
158 { \
159 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, __VA_ARGS__); \
160 vboxDisableVbva(pScrn); \
161 pVBox->useVbva = FALSE; \
162 return; \
163 } \
164 while (0)
165
166/**************************************************************************
167* Main functions *
168**************************************************************************/
169
170void
171vbox_close(ScrnInfoPtr pScrn, VBOXPtr pVBox)
172{
173 TRACE_ENTRY();
174
175 xfree (pVBox->reqp);
176 pVBox->reqp = NULL;
177 TRACE_EXIT();
178}
179
180/**
181 * Callback function called by the X server to tell us about dirty
182 * rectangles in the video buffer.
183 *
184 * @param pScreen pointer to the information structure for the current
185 * screen
186 * @param iRects Number of dirty rectangles to update
187 * @param aRects Array of structures containing the coordinates of the
188 * rectangles
189 */
190static void
191vboxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects)
192{
193 VBVACMDHDR cmdHdr;
194 VBOXPtr pVBox;
195 VBVARECORD *pRecord;
196 VBVAMEMORY *pMem;
197 CARD32 indexRecordNext;
198 CARD32 off32Data;
199 CARD32 off32Free;
200 INT32 i32Diff;
201 CARD32 cbHwBufferAvail;
202 int scrnIndex;
203 int i;
204
205 pVBox = pScrn->driverPrivate;
206 if (pVBox->useVbva == FALSE)
207 return;
208 pMem = pVBox->pVbvaMemory;
209 /* Just return quietly if VBVA is not currently active. */
210 if ((pMem->fu32ModeFlags & VBVA_F_MODE_ENABLED) == 0)
211 return;
212 scrnIndex = pScrn->scrnIndex;
213
214 for (i = 0; i < iRects; i++)
215 {
216 cmdHdr.x = (int16_t)aRects[i].x1 - pVBox->viewportX;
217 cmdHdr.y = (int16_t)aRects[i].y1 - pVBox->viewportY;
218 cmdHdr.w = (uint16_t)(aRects[i].x2 - aRects[i].x1);
219 cmdHdr.h = (uint16_t)(aRects[i].y2 - aRects[i].y1);
220
221 /* Get the active record and move the pointer along */
222 indexRecordNext = (pMem->indexRecordFree + 1) % VBVA_MAX_RECORDS;
223 if (indexRecordNext == pMem->indexRecordFirst)
224 {
225 /* All slots in the records queue are used. */
226 if (VbglR3VideoAccelFlush() < 0)
227 DISABLE_VBVA_AND_RETURN(pScrn,
228 "Unable to clear the VirtualBox graphics acceleration queue "
229 "- the request to the virtual machine failed. Switching to "
230 "unaccelerated mode.\n");
231 }
232 if (indexRecordNext == pMem->indexRecordFirst)
233 DISABLE_VBVA_AND_RETURN(pScrn,
234 "Failed to clear the VirtualBox graphics acceleration queue. "
235 "Switching to unaccelerated mode.\n");
236 pRecord = &pMem->aRecords[pMem->indexRecordFree];
237 /* Mark the record as being updated. */
238 pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
239 pMem->indexRecordFree = indexRecordNext;
240 /* Compute how many bytes we have in the ring buffer. */
241 off32Free = pMem->off32Free;
242 off32Data = pMem->off32Data;
243 /* Free is writing position. Data is reading position.
244 * Data == Free means buffer is free.
245 * There must be always gap between free and data when data
246 * are in the buffer.
247 * Guest only changes free, host only changes data.
248 */
249 i32Diff = off32Data - off32Free;
250 cbHwBufferAvail = i32Diff > 0 ? i32Diff : VBVA_RING_BUFFER_SIZE + i32Diff;
251 if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD)
252 {
253 if (VbglR3VideoAccelFlush() < 0)
254 DISABLE_VBVA_AND_RETURN(pScrn,
255 "Unable to clear the VirtualBox graphics acceleration queue "
256 "- the request to the virtual machine failed. Switching to "
257 "unaccelerated mode.\n");
258 /* Calculate the free space again. */
259 off32Free = pMem->off32Free;
260 off32Data = pMem->off32Data;
261 i32Diff = off32Data - off32Free;
262 cbHwBufferAvail = i32Diff > 0? i32Diff:
263 VBVA_RING_BUFFER_SIZE + i32Diff;
264 if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD)
265 DISABLE_VBVA_AND_RETURN(pScrn,
266 "No space left in the VirtualBox graphics acceleration command buffer, "
267 "despite clearing the queue. Switching to unaccelerated mode.\n");
268 }
269 /* Now copy the data into the buffer */
270 if (off32Free + sizeof(cmdHdr) < VBVA_RING_BUFFER_SIZE)
271 {
272 memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr, sizeof(cmdHdr));
273 pMem->off32Free = pMem->off32Free + sizeof(cmdHdr);
274 }
275 else
276 {
277 CARD32 u32First = VBVA_RING_BUFFER_SIZE - off32Free;
278 /* The following is impressively ugly! */
279 CARD8 *pu8Second = (CARD8 *)&cmdHdr + u32First;
280 CARD32 u32Second = sizeof(cmdHdr) - u32First;
281 memcpy(&pMem->au8RingBuffer[off32Free], &cmdHdr, u32First);
282 if (u32Second)
283 memcpy(&pMem->au8RingBuffer[0], pu8Second, u32Second);
284 pMem->off32Free = u32Second;
285 }
286 pRecord->cbRecord += sizeof(cmdHdr);
287 /* Mark the record completed. */
288 pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
289 }
290}
291
292#ifdef PCIACCESS
293/* As of X.org server 1.5, we are using the pciaccess library functions to
294 * access PCI. This structure describes our VMM device. */
295/** Structure describing the VMM device */
296static const struct pci_id_match vboxVMMDevID =
297{ VMMDEV_VENDORID, VMMDEV_DEVICEID, PCI_MATCH_ANY, PCI_MATCH_ANY,
298 0, 0, 0 };
299#endif
300
301/**
302 * Initialise VirtualBox's accelerated video extensions.
303 *
304 * @returns TRUE on success, FALSE on failure
305 */
306static Bool
307vboxInitVbva(int scrnIndex, ScreenPtr pScreen, VBOXPtr pVBox)
308{
309#ifdef PCIACCESS
310 struct pci_device_iterator *devIter = NULL;
311
312 TRACE_ENTRY();
313 pVBox->vmmDevInfo = NULL;
314 devIter = pci_id_match_iterator_create(&vboxVMMDevID);
315 if (devIter)
316 {
317 pVBox->vmmDevInfo = pci_device_next(devIter);
318 pci_iterator_destroy(devIter);
319 }
320 if (pVBox->vmmDevInfo)
321 {
322 if (pci_device_probe(pVBox->vmmDevInfo) != 0)
323 {
324 xf86DrvMsg (scrnIndex, X_ERROR,
325 "Failed to probe VMM device (vendor=%04x, devid=%04x)\n",
326 pVBox->vmmDevInfo->vendor_id,
327 pVBox->vmmDevInfo->device_id);
328 }
329 else
330 {
331 if (pci_device_map_range(pVBox->vmmDevInfo,
332 pVBox->vmmDevInfo->regions[1].base_addr,
333 pVBox->vmmDevInfo->regions[1].size,
334 PCI_DEV_MAP_FLAG_WRITABLE,
335 (void **)&pVBox->pVMMDevMemory) != 0)
336 xf86DrvMsg (scrnIndex, X_ERROR,
337 "Failed to map VMM device range\n");
338 }
339 }
340#else
341 PCITAG pciTagDev;
342 ADDRESS pciAddrDev;
343
344 TRACE_ENTRY();
345 /* Locate the device. It should already have been enabled by
346 the kernel driver. */
347 pciTagDev = pciFindFirst((unsigned) VMMDEV_DEVICEID << 16 | VMMDEV_VENDORID,
348 (CARD32) ~0);
349 if (pciTagDev == PCI_NOT_FOUND)
350 {
351 xf86DrvMsg(scrnIndex, X_ERROR,
352 "Could not find the VirtualBox base device on the PCI bus.\n");
353 return FALSE;
354 }
355 /* Read the address and size of the second I/O region. */
356 pciAddrDev = pciReadLong(pciTagDev, PCI_MAP_REG_START + 4);
357 if (pciAddrDev == 0 || pciAddrDev == (CARD32) ~0)
358 RETERROR(scrnIndex, FALSE,
359 "The VirtualBox base device contains an invalid memory address.\n");
360 if (PCI_MAP_IS64BITMEM(pciAddrDev))
361 RETERROR(scrnIndex, FALSE,
362 "The VirtualBox base device has a 64bit mapping address. "
363 "This is currently not supported.\n");
364 /* Map it. We hardcode the size as X does not export the
365 function needed to determine it. */
366 pVBox->pVMMDevMemory = xf86MapPciMem(scrnIndex, 0, pciTagDev, pciAddrDev,
367 sizeof(VMMDevMemory));
368#endif
369 if (pVBox->pVMMDevMemory == NULL)
370 {
371 xf86DrvMsg(scrnIndex, X_ERROR,
372 "Failed to map VirtualBox video extension memory.\n");
373 return FALSE;
374 }
375 pVBox->pVbvaMemory = &pVBox->pVMMDevMemory->vbvaMemory;
376 /* Set up the dirty rectangle handler. Since this seems to be a
377 delicate operation, and removing it doubly so, this will
378 remain in place whether it is needed or not, and will simply
379 return if VBVA is not active. I assume that it will be active
380 most of the time. */
381 if (ShadowFBInit2(pScreen, NULL, vboxHandleDirtyRect) != TRUE)
382 {
383 xf86DrvMsg(scrnIndex, X_ERROR,
384 "Unable to install dirty rectangle handler for VirtualBox graphics acceleration.\n");
385 return FALSE;
386 }
387 return TRUE;
388}
389
390Bool
391vbox_init(int scrnIndex, VBOXPtr pVBox)
392{
393 Bool rc = TRUE;
394 int vrc;
395 uint32_t fMouseFeatures = 0;
396
397 TRACE_ENTRY();
398 pVBox->useVbva = FALSE;
399 vrc = VbglR3Init();
400 if (RT_FAILURE(vrc))
401 {
402 xf86DrvMsg(scrnIndex, X_ERROR,
403 "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",
404 vrc);
405 rc = FALSE;
406 }
407 pVBox->useDevice = rc;
408 /* We can't switch to a software cursor at will without help from
409 * VBoxClient. So tell that to the host and wait for VBoxClient to
410 * change this. */
411 vrc = VbglR3GetMouseStatus(&fMouseFeatures, NULL, NULL);
412 if (RT_SUCCESS(vrc))
413 VbglR3SetMouseStatus( fMouseFeatures
414 | VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
415 return rc;
416}
417
418Bool
419vbox_open(ScrnInfoPtr pScrn, ScreenPtr pScreen, VBOXPtr pVBox)
420{
421 int rc;
422 void *p;
423 size_t size;
424 int scrnIndex = pScrn->scrnIndex;
425
426 TRACE_ENTRY();
427
428 if (!pVBox->useDevice)
429 return FALSE;
430
431 if (pVBox->reqp)
432 {
433 /* still open, just re-enable VBVA after CloseScreen was called */
434 pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox);
435 return TRUE;
436 }
437
438 size = vmmdevGetRequestSize(VMMDevReq_SetPointerShape);
439 p = xcalloc(1, size);
440 if (p)
441 {
442 rc = vmmdevInitRequest(p, VMMDevReq_SetPointerShape);
443 if (RT_SUCCESS(rc))
444 {
445 pVBox->reqp = p;
446 pVBox->pCurs = NULL;
447 pVBox->pointerHeaderSize = size;
448 pVBox->useVbva = vboxInitVbva(scrnIndex, pScreen, pVBox);
449 return TRUE;
450 }
451 xf86DrvMsg(scrnIndex, X_ERROR, "Could not init VMM request: rc = %d\n", rc);
452 xfree(p);
453 }
454 xf86DrvMsg(scrnIndex, X_ERROR, "Could not allocate %lu bytes for VMM request\n", (unsigned long)size);
455 return FALSE;
456}
457
458Bool
459vbox_device_available(VBOXPtr pVBox)
460{
461 return pVBox->useDevice;
462}
463
464static void
465vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
466{
467 int rc;
468
469 pVBox->reqp->fFlags = 0;
470 rc = VbglR3SetPointerShapeReq(pVBox->reqp);
471 if (RT_FAILURE(rc))
472 {
473 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not hide the virtual mouse pointer.\n");
474 /* Play safe, and disable the hardware cursor until the next mode
475 * switch, since obviously something happened that we didn't
476 * anticipate. */
477 pVBox->forceSWCursor = TRUE;
478 }
479}
480
481static void
482vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
483{
484 int rc;
485
486 if (vbox_host_uses_hwcursor(pScrn)) {
487 pVBox->reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE;
488 rc = VbglR3SetPointerShapeReq(pVBox->reqp);
489 if (RT_FAILURE(rc)) {
490 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not unhide the virtual mouse pointer.\n");
491 /* Play safe, and disable the hardware cursor until the next mode
492 * switch, since obviously something happened that we didn't
493 * anticipate. */
494 pVBox->forceSWCursor = TRUE;
495 }
496 }
497}
498
499static void
500vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
501 unsigned char *image)
502{
503 int rc;
504 VMMDevReqMousePointer *reqp;
505 reqp = (VMMDevReqMousePointer *)image;
506
507#ifdef DEBUG_POINTER
508 vbox_show_shape(reqp->width, reqp->height, 0, image);
509#endif
510
511 rc = VbglR3SetPointerShapeReq(reqp);
512 if (RT_FAILURE(rc)) {
513 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to set the virtual mouse pointer image.\n");
514 /* Play safe, and disable the hardware cursor until the next mode
515 * switch, since obviously something happened that we didn't
516 * anticipate. */
517 pVBox->forceSWCursor = TRUE;
518 }
519}
520
521static void
522vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
523{
524 NOREF(pScrn);
525 NOREF(bg);
526 NOREF(fg);
527 /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
528}
529
530
531static void
532vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
533{
534 /* Nothing to do here, as we are telling the guest where the mouse is,
535 * not vice versa. */
536 NOREF(pScrn);
537 NOREF(x);
538 NOREF(y);
539}
540
541static void
542vbox_hide_cursor(ScrnInfoPtr pScrn)
543{
544 VBOXPtr pVBox = pScrn->driverPrivate;
545
546 vbox_vmm_hide_cursor(pScrn, pVBox);
547}
548
549static void
550vbox_show_cursor(ScrnInfoPtr pScrn)
551{
552 VBOXPtr pVBox = pScrn->driverPrivate;
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 vbox_vmm_load_cursor_image(pScrn, pVBox, image);
563}
564
565static Bool
566vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
567{
568 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
569 return vbox_host_uses_hwcursor(pScrn);
570}
571
572static unsigned char
573color_to_byte(unsigned c)
574{
575 return (c >> 8) & 0xff;
576}
577
578static unsigned char *
579vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
580{
581 VBOXPtr pVBox;
582 CursorBitsPtr bitsp;
583 unsigned short w, h, x, y;
584 unsigned char *c, *p, *pm, *ps, *m;
585 size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
586 CARD32 fc, bc, *cp;
587 int rc, scrnIndex = infoPtr->pScrn->scrnIndex;
588 VMMDevReqMousePointer *reqp;
589
590 pVBox = infoPtr->pScrn->driverPrivate;
591 bitsp = pCurs->bits;
592 w = bitsp->width;
593 h = bitsp->height;
594
595 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
596 RETERROR(scrnIndex, NULL,
597 "Error invalid cursor dimensions %dx%d\n", w, h);
598
599 if ((bitsp->xhot > w) || (bitsp->yhot > h))
600 RETERROR(scrnIndex, NULL,
601 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
602 bitsp->xhot, bitsp->yhot, w, h);
603
604 srcPitch = PixmapBytePad (bitsp->width, 1);
605 dstPitch = (w + 7) / 8;
606 sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
607 sizeRgba = w * h * 4;
608 pVBox->pointerSize = sizeMask + sizeRgba;
609 sizeRequest = pVBox->pointerSize + pVBox->pointerHeaderSize;
610
611 p = c = xcalloc (1, sizeRequest);
612 if (!c)
613 RETERROR(scrnIndex, NULL,
614 "Error failed to alloc %lu bytes for cursor\n",
615 (unsigned long) sizeRequest);
616
617 rc = vmmdevInitRequest((VMMDevRequestHeader *)p, VMMDevReq_SetPointerShape);
618 if (RT_FAILURE(rc))
619 {
620 xf86DrvMsg(scrnIndex, X_ERROR, "Could not init VMM request: rc = %d\n", rc);
621 xfree(p);
622 return NULL;
623 }
624
625 m = p + offsetof(VMMDevReqMousePointer, pointerData);
626 cp = (CARD32 *)(m + sizeMask);
627
628 TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
629 w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
630 TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp);
631
632 fc = color_to_byte (pCurs->foreBlue)
633 | (color_to_byte (pCurs->foreGreen) << 8)
634 | (color_to_byte (pCurs->foreRed) << 16);
635
636 bc = color_to_byte (pCurs->backBlue)
637 | (color_to_byte (pCurs->backGreen) << 8)
638 | (color_to_byte (pCurs->backRed) << 16);
639
640 /*
641 * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
642 * Xorg:
643 * The mask is a bitmap indicating which parts of the cursor are
644 * transparent and which parts are drawn. The source is a bitmap
645 * indicating which parts of the non-transparent portion of the
646 * the cursor should be painted in the foreground color and which
647 * should be painted in the background color. By default, set bits
648 * indicate the opaque part of the mask bitmap and clear bits
649 * indicate the transparent part.
650 * VBox:
651 * The color data is the XOR mask. The AND mask bits determine
652 * which pixels of the color data (XOR mask) will replace (overwrite)
653 * the screen pixels (AND mask bit = 0) and which ones will be XORed
654 * with existing screen pixels (AND mask bit = 1).
655 * For example when you have the AND mask all 0, then you see the
656 * correct mouse pointer image surrounded by black square.
657 */
658 for (pm = bitsp->mask, ps = bitsp->source, y = 0;
659 y < h;
660 ++y, pm += srcPitch, ps += srcPitch, m += dstPitch)
661 {
662 for (x = 0; x < w; ++x)
663 {
664 if (pm[x / 8] & (1 << (x % 8)))
665 {
666 /* opaque, leave AND mask bit at 0 */
667 if (ps[x / 8] & (1 << (x % 8)))
668 {
669 *cp++ = fc;
670 PUT_PIXEL('X');
671 }
672 else
673 {
674 *cp++ = bc;
675 PUT_PIXEL('*');
676 }
677 }
678 else
679 {
680 /* transparent, set AND mask bit */
681 m[x / 8] |= 1 << (7 - (x % 8));
682 /* don't change the screen pixel */
683 *cp++ = 0;
684 PUT_PIXEL(' ');
685 }
686 }
687 PUT_PIXEL('\n');
688 }
689
690 reqp = (VMMDevReqMousePointer *)p;
691 reqp->width = w;
692 reqp->height = h;
693 reqp->xHot = bitsp->xhot;
694 reqp->yHot = bitsp->yhot;
695 reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
696 reqp->header.size = sizeRequest;
697
698#ifdef DEBUG_POINTER
699 ErrorF("shape = %p\n", p);
700 vbox_show_shape(w, h, bc, c);
701#endif
702
703 return p;
704}
705
706#ifdef ARGB_CURSOR
707static Bool
708vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
709{
710 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
711 Bool rc = TRUE;
712
713 if (!vbox_host_uses_hwcursor(pScrn))
714 rc = FALSE;
715 if ( rc
716 && ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
717 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
718 || (pScrn->bitsPerPixel <= 8)
719 )
720 )
721 rc = FALSE;
722#ifndef VBOXVIDEO_13
723 /* Evil hack - we use this as another way of poking the driver to update
724 * our list of video modes. */
725 vboxWriteHostModes(pScrn, pScrn->currentMode);
726#endif
727 TRACE_LOG("rc=%s\n", BOOL_STR(rc));
728 return rc;
729}
730
731
732static void
733vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
734{
735 VBOXPtr pVBox;
736 VMMDevReqMousePointer *reqp;
737 CursorBitsPtr bitsp;
738 unsigned short w, h;
739 unsigned short cx, cy;
740 unsigned char *pm;
741 CARD32 *pc;
742 size_t sizeRequest, sizeMask;
743 CARD8 *p;
744 int scrnIndex;
745
746 TRACE_ENTRY();
747 pVBox = pScrn->driverPrivate;
748 bitsp = pCurs->bits;
749 w = bitsp->width;
750 h = bitsp->height;
751 scrnIndex = pScrn->scrnIndex;
752
753 /* Mask must be generated for alpha cursors, that is required by VBox. */
754 /* note: (michael) the next struct must be 32bit aligned. */
755 sizeMask = ((w + 7) / 8 * h + 3) & ~3;
756
757 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
758 RETERROR(scrnIndex, ,
759 "Error invalid cursor dimensions %dx%d\n", w, h);
760
761 if ((bitsp->xhot > w) || (bitsp->yhot > h))
762 RETERROR(scrnIndex, ,
763 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
764 bitsp->xhot, bitsp->yhot, w, h);
765
766 pVBox->pointerSize = w * h * 4 + sizeMask;
767 sizeRequest = pVBox->pointerSize + pVBox->pointerHeaderSize;
768 p = xcalloc(1, sizeRequest);
769 if (!p)
770 RETERROR(scrnIndex, ,
771 "Error failed to alloc %lu bytes for cursor\n",
772 (unsigned long)sizeRequest);
773
774 reqp = (VMMDevReqMousePointer *)p;
775 *reqp = *pVBox->reqp;
776 reqp->width = w;
777 reqp->height = h;
778 reqp->xHot = bitsp->xhot;
779 reqp->yHot = bitsp->yhot;
780 reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE
781 | VBOX_MOUSE_POINTER_ALPHA;
782 reqp->header.size = sizeRequest;
783
784 memcpy(p + offsetof(VMMDevReqMousePointer, pointerData) + sizeMask, bitsp->argb, w * h * 4);
785
786 /* Emulate the AND mask. */
787 pm = p + offsetof(VMMDevReqMousePointer, pointerData);
788 pc = bitsp->argb;
789
790 /* Init AND mask to 1 */
791 memset(pm, 0xFF, sizeMask);
792
793 /*
794 * The additions driver must provide the AND mask for alpha cursors. The host frontend
795 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
796 * But if the host does not support ARGB, then it simply uses the AND mask and the color
797 * data to draw a normal color cursor.
798 */
799 for (cy = 0; cy < h; cy++)
800 {
801 unsigned char bitmask = 0x80;
802
803 for (cx = 0; cx < w; cx++, bitmask >>= 1)
804 {
805 if (bitmask == 0)
806 bitmask = 0x80;
807
808 if (pc[cx] >= 0xF0000000)
809 pm[cx / 8] &= ~bitmask;
810 }
811
812 /* Point to next source and dest scans */
813 pc += w;
814 pm += (w + 7) / 8;
815 }
816
817 VbglR3SetPointerShapeReq(reqp);
818 xfree(p);
819}
820#endif
821
822Bool
823vbox_cursor_init(ScreenPtr pScreen)
824{
825 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
826 VBOXPtr pVBox = pScrn->driverPrivate;
827 xf86CursorInfoPtr pCurs = NULL;
828 Bool rc = TRUE;
829
830 TRACE_ENTRY();
831 if (!pVBox->useDevice)
832 return FALSE;
833 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
834 if (!pCurs) {
835 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
836 "Failed to create X Window cursor information structures for virtual mouse.\n");
837 rc = FALSE;
838 }
839 if (rc) {
840 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
841 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
842 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
843 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
844 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
845
846 pCurs->SetCursorColors = vbox_set_cursor_colors;
847 pCurs->SetCursorPosition = vbox_set_cursor_position;
848 pCurs->LoadCursorImage = vbox_load_cursor_image;
849 pCurs->HideCursor = vbox_hide_cursor;
850 pCurs->ShowCursor = vbox_show_cursor;
851 pCurs->UseHWCursor = vbox_use_hw_cursor;
852 pCurs->RealizeCursor = vbox_realize_cursor;
853
854#ifdef ARGB_CURSOR
855 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
856 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
857#endif
858
859 /* Hide the host cursor before we initialise if we wish to use a
860 * software cursor. */
861 if (pVBox->forceSWCursor)
862 vbox_vmm_hide_cursor(pScrn, pVBox);
863 rc = xf86InitCursor(pScreen, pCurs);
864 }
865 if (!rc)
866 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
867 "Failed to enable mouse pointer integration.\n");
868 if (!rc && (pCurs != NULL))
869 xf86DestroyCursorInfoRec(pCurs);
870 return rc;
871}
872
873/**
874 * Inform VBox that we will supply it with dirty rectangle information
875 * and install the dirty rectangle handler.
876 *
877 * @returns TRUE for success, FALSE for failure
878 * @param pScrn Pointer to a structure describing the X screen in use
879 */
880Bool
881vboxEnableVbva(ScrnInfoPtr pScrn)
882{
883 bool rc = TRUE;
884 int scrnIndex = pScrn->scrnIndex;
885 VBOXPtr pVBox = pScrn->driverPrivate;
886
887 TRACE_ENTRY();
888 if (pVBox->useVbva != TRUE)
889 rc = FALSE;
890 if (rc && RT_FAILURE(VbglR3VideoAccelEnable(true)))
891 /* Request not accepted - disable for old hosts. */
892 xf86DrvMsg(scrnIndex, X_ERROR,
893 "Unable to activate VirtualBox graphics acceleration "
894 "- the request to the virtual machine failed. "
895 "You may be running an old version of VirtualBox.\n");
896 pVBox->useVbva = rc;
897 if (!rc)
898 VbglR3VideoAccelEnable(false);
899 return rc;
900}
901
902/**
903 * Inform VBox that we will stop supplying it with dirty rectangle
904 * information. This function is intended to be called when an X
905 * virtual terminal is disabled, or the X server is terminated.
906 *
907 * @returns TRUE for success, FALSE for failure
908 * @param pScrn Pointer to a structure describing the X screen in use
909 */
910Bool
911vboxDisableVbva(ScrnInfoPtr pScrn)
912{
913 int rc;
914 int scrnIndex = pScrn->scrnIndex;
915 VBOXPtr pVBox = pScrn->driverPrivate;
916
917 TRACE_ENTRY();
918 if (pVBox->useVbva != TRUE) /* Ths function should not have been called */
919 return FALSE;
920 rc = VbglR3VideoAccelEnable(false);
921 if (RT_FAILURE(rc))
922 {
923 xf86DrvMsg(scrnIndex, X_ERROR,
924 "Unable to disable VirtualBox graphics acceleration "
925 "- the request to the virtual machine failed.\n");
926 }
927 else
928 memset(pVBox->pVbvaMemory, 0, sizeof(VBVAMEMORY));
929 return TRUE;
930}
931
932/**
933 * Inform VBox that we are aware of advanced graphics functions
934 * (i.e. dynamic resizing, seamless).
935 *
936 * @returns TRUE for success, FALSE for failure
937 */
938Bool
939vboxEnableGraphicsCap(VBOXPtr pVBox)
940{
941 TRACE_ENTRY();
942 if (!pVBox->useDevice)
943 return FALSE;
944 return RT_SUCCESS(VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0));
945}
946
947/**
948 * Inform VBox that we are no longer aware of advanced graphics functions
949 * (i.e. dynamic resizing, seamless).
950 *
951 * @returns TRUE for success, FALSE for failure
952 */
953Bool
954vboxDisableGraphicsCap(VBOXPtr pVBox)
955{
956 TRACE_ENTRY();
957 if (!pVBox->useDevice)
958 return FALSE;
959 return RT_SUCCESS(VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS));
960}
961
962/**
963 * Query the last display change request.
964 *
965 * @returns boolean success indicator.
966 * @param pScrn Pointer to the X screen info structure.
967 * @param pcx Where to store the horizontal pixel resolution (0 = do not change).
968 * @param pcy Where to store the vertical pixel resolution (0 = do not change).
969 * @param pcBits Where to store the bits per pixel (0 = do not change).
970 * @param iDisplay Where to store the display number the request was for - 0 for the
971 * primary display, 1 for the first secondary, etc.
972 */
973Bool
974vboxGetDisplayChangeRequest(ScrnInfoPtr pScrn, uint32_t *pcx, uint32_t *pcy,
975 uint32_t *pcBits, uint32_t *piDisplay)
976{
977 VBOXPtr pVBox = pScrn->driverPrivate;
978 TRACE_ENTRY();
979 if (!pVBox->useDevice)
980 return FALSE;
981 int rc = VbglR3GetDisplayChangeRequest(pcx, pcy, pcBits, piDisplay, true);
982 if (RT_SUCCESS(rc))
983 return TRUE;
984 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to obtain the last resolution requested by the guest, rc=%d.\n", rc);
985 return FALSE;
986}
987
988
989/**
990 * Query the host as to whether it likes a specific video mode.
991 *
992 * @returns the result of the query
993 * @param cx the width of the mode being queried
994 * @param cy the height of the mode being queried
995 * @param cBits the bpp of the mode being queried
996 */
997Bool
998vboxHostLikesVideoMode(ScrnInfoPtr pScrn, uint32_t cx, uint32_t cy, uint32_t cBits)
999{
1000 VBOXPtr pVBox = pScrn->driverPrivate;
1001 TRACE_ENTRY();
1002 if (!pVBox->useDevice)
1003 return TRUE; /* If we can't ask the host then we like everything. */
1004 return VbglR3HostLikesVideoMode(cx, cy, cBits);
1005}
1006
1007/**
1008 * Check if any seamless mode is enabled.
1009 * Seamless is only relevant for the newer Xorg modules.
1010 *
1011 * @returns the result of the query
1012 * (true = seamless enabled, false = seamless not enabled)
1013 * @param pScrn Screen info pointer.
1014 */
1015Bool
1016vboxGuestIsSeamless(ScrnInfoPtr pScrn)
1017{
1018 VMMDevSeamlessMode mode;
1019 VBOXPtr pVBox = pScrn->driverPrivate;
1020 TRACE_ENTRY();
1021 if (!pVBox->useDevice)
1022 return FALSE;
1023 if (RT_FAILURE(VbglR3SeamlessGetLastEvent(&mode)))
1024 return FALSE;
1025 return (mode != VMMDev_Seamless_Disabled);
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}
1070
1071/**
1072 * Fills a display mode M with a built-in mode of name pszName and dimensions
1073 * cx and cy.
1074 */
1075static void vboxFillDisplayMode(DisplayModePtr m, const char *pszName,
1076 unsigned cx, unsigned cy)
1077{
1078 TRACE_LOG("pszName=%s, cx=%u, cy=%u\n", pszName, cx, cy);
1079 m->status = MODE_OK;
1080 m->type = M_T_BUILTIN;
1081 /* VBox only supports screen widths which are a multiple of 8 */
1082 m->HDisplay = cx & ~7;
1083 m->HSyncStart = m->HDisplay + 2;
1084 m->HSyncEnd = m->HDisplay + 4;
1085 m->HTotal = m->HDisplay + 6;
1086 m->VDisplay = cy;
1087 m->VSyncStart = m->VDisplay + 2;
1088 m->VSyncEnd = m->VDisplay + 4;
1089 m->VTotal = m->VDisplay + 6;
1090 m->Clock = m->HTotal * m->VTotal * 60 / 1000; /* kHz */
1091 if (pszName)
1092 {
1093 if (m->name)
1094 xfree(m->name);
1095 m->name = xnfstrdup(pszName);
1096 }
1097}
1098
1099/** vboxvideo's list of standard video modes */
1100struct
1101{
1102 /** mode width */
1103 uint32_t cx;
1104 /** mode height */
1105 uint32_t cy;
1106} vboxStandardModes[] =
1107{
1108 { 1600, 1200 },
1109 { 1440, 1050 },
1110 { 1280, 960 },
1111 { 1024, 768 },
1112 { 800, 600 },
1113 { 640, 480 },
1114 { 0, 0 }
1115};
1116enum
1117{
1118 vboxNumStdModes = sizeof(vboxStandardModes) / sizeof(vboxStandardModes[0])
1119};
1120
1121/**
1122 * Returns a standard mode which the host likes. Can be called multiple
1123 * times with the index returned by the previous call to get a list of modes.
1124 * @returns the index of the mode in the list, or 0 if no more modes are
1125 * available
1126 * @param pScrn the screen information structure
1127 * @param pScrn->bitsPerPixel
1128 * if this is non-null, only modes with this BPP will be
1129 * returned
1130 * @param cIndex the index of the last mode queried, or 0 to query the
1131 * first mode available. Note: the first index is 1
1132 * @param pcx where to store the mode's width
1133 * @param pcy where to store the mode's height
1134 * @param pcBits where to store the mode's BPP
1135 */
1136static unsigned vboxNextStandardMode(ScrnInfoPtr pScrn, unsigned cIndex,
1137 uint32_t *pcx, uint32_t *pcy,
1138 uint32_t *pcBits)
1139{
1140 XF86ASSERT(cIndex < vboxNumStdModes,
1141 ("cIndex = %d, vboxNumStdModes = %d\n", cIndex,
1142 vboxNumStdModes));
1143 for (unsigned i = cIndex; i < vboxNumStdModes - 1; ++i)
1144 {
1145 uint32_t cBits = pScrn->bitsPerPixel;
1146 uint32_t cx = vboxStandardModes[i].cx;
1147 uint32_t cy = vboxStandardModes[i].cy;
1148
1149 if (cBits != 0 && !vboxHostLikesVideoMode(pScrn, cx, cy, cBits))
1150 continue;
1151 if (vboxHostLikesVideoMode(pScrn, cx, cy, 32))
1152 cBits = 32;
1153 else if (vboxHostLikesVideoMode(pScrn, cx, cy, 16))
1154 cBits = 16;
1155 else
1156 continue;
1157 if (pcx)
1158 *pcx = cx;
1159 if (pcy)
1160 *pcy = cy;
1161 if (pcBits)
1162 *pcBits = cBits;
1163 return i + 1;
1164 }
1165 return 0;
1166}
1167
1168/**
1169 * Returns the preferred video mode. The current order of preference is
1170 * (from highest to least preferred):
1171 * - The mode corresponding to the last size hint from the host
1172 * - The video mode saved from the last session
1173 * - The largest standard mode which the host likes, falling back to
1174 * 640x480x32 as a worst case
1175 * - If the host can't be contacted at all, we return 1024x768x32
1176 *
1177 * The return type is void as we guarantee we will return some mode.
1178 */
1179void vboxGetPreferredMode(ScrnInfoPtr pScrn, uint32_t *pcx,
1180 uint32_t *pcy, uint32_t *pcBits)
1181{
1182 /* Query the host for the preferred resolution and colour depth */
1183 uint32_t cx = 0, cy = 0, iDisplay = 0, cBits = 32;
1184 VBOXPtr pVBox = pScrn->driverPrivate;
1185
1186 TRACE_ENTRY();
1187 if (pVBox->useDevice)
1188 {
1189 bool found = vboxGetDisplayChangeRequest(pScrn, &cx, &cy, &cBits, &iDisplay);
1190 if ((cx == 0) || (cy == 0))
1191 found = false;
1192 if (!found)
1193 found = vboxRetrieveVideoMode(pScrn, &cx, &cy, &cBits);
1194 if ((cx == 0) || (cy == 0))
1195 found = false;
1196 if (found)
1197 /* Adjust to a multiple of eight */
1198 cx &= ~7;
1199 if (!found)
1200 found = (vboxNextStandardMode(pScrn, 0, &cx, &cy, &cBits) != 0);
1201 if (!found)
1202 {
1203 /* Last resort */
1204 cx = 640;
1205 cy = 480;
1206 cBits = 32;
1207 }
1208 }
1209 else
1210 {
1211 cx = 1024;
1212 cy = 768;
1213 }
1214 if (pcx)
1215 *pcx = cx;
1216 if (pcy)
1217 *pcy = cy;
1218 if (pcx)
1219 *pcBits = cBits;
1220}
1221
1222/* Move a screen mode found to the end of the list, so that RandR will give
1223 * it the highest priority when a mode switch is requested. Returns the mode
1224 * that was previously before the mode in the list in order to allow the
1225 * caller to continue walking the list. */
1226static DisplayModePtr vboxMoveModeToFront(ScrnInfoPtr pScrn,
1227 DisplayModePtr pMode)
1228{
1229 DisplayModePtr pPrev = pMode->prev;
1230 if (pMode != pScrn->modes)
1231 {
1232 pMode->prev->next = pMode->next;
1233 pMode->next->prev = pMode->prev;
1234 pMode->next = pScrn->modes;
1235 pMode->prev = pScrn->modes->prev;
1236 pMode->next->prev = pMode;
1237 pMode->prev->next = pMode;
1238 pScrn->modes = pMode;
1239 }
1240 return pPrev;
1241}
1242
1243/**
1244 * Rewrites the first dynamic mode found which is not the current screen mode
1245 * to contain the host's currently preferred screen size, then moves that
1246 * mode to the front of the screen information structure's mode list.
1247 * Additionally, if the current mode is not dynamic, the second dynamic mode
1248 * will be set to match the current mode and also added to the front. This
1249 * ensures that the user can always reset the current size to kick the driver
1250 * to update its mode list.
1251 */
1252void vboxWriteHostModes(ScrnInfoPtr pScrn, DisplayModePtr pCurrent)
1253{
1254 uint32_t cx = 0, cy = 0, iDisplay = 0, cBits = 0;
1255 DisplayModePtr pMode;
1256 bool found = false;
1257
1258 TRACE_ENTRY();
1259 vboxGetPreferredMode(pScrn, &cx, &cy, &cBits);
1260#ifdef DEBUG
1261 /* Count the number of modes for sanity */
1262 unsigned cModes = 1, cMode = 0;
1263 DisplayModePtr pCount;
1264 for (pCount = pScrn->modes; ; pCount = pCount->next, ++cModes)
1265 if (pCount->next == pScrn->modes)
1266 break;
1267#endif
1268 for (pMode = pScrn->modes; ; pMode = pMode->next)
1269 {
1270#ifdef DEBUG
1271 XF86ASSERT (cMode++ < cModes, (NULL));
1272#endif
1273 if ( pMode != pCurrent
1274 && !strcmp(pMode->name, "VBoxDynamicMode"))
1275 {
1276 if (!found)
1277 vboxFillDisplayMode(pMode, NULL, cx, cy);
1278 else if (pCurrent)
1279 vboxFillDisplayMode(pMode, NULL, pCurrent->HDisplay,
1280 pCurrent->VDisplay);
1281 found = true;
1282 pMode = vboxMoveModeToFront(pScrn, pMode);
1283 }
1284 if (pMode->next == pScrn->modes)
1285 break;
1286 }
1287 XF86ASSERT (found,
1288 ("vboxvideo: no free dynamic mode found. Exiting.\n"));
1289 XF86ASSERT ( (pScrn->modes->HDisplay == (long) cx)
1290 || ( (pScrn->modes->HDisplay == pCurrent->HDisplay)
1291 && (pScrn->modes->next->HDisplay == (long) cx)),
1292 ("pScrn->modes->HDisplay=%u, pScrn->modes->next->HDisplay=%u\n",
1293 pScrn->modes->HDisplay, pScrn->modes->next->HDisplay));
1294 XF86ASSERT ( (pScrn->modes->VDisplay == (long) cy)
1295 || ( (pScrn->modes->VDisplay == pCurrent->VDisplay)
1296 && (pScrn->modes->next->VDisplay == (long) cy)),
1297 ("pScrn->modes->VDisplay=%u, pScrn->modes->next->VDisplay=%u\n",
1298 pScrn->modes->VDisplay, pScrn->modes->next->VDisplay));
1299}
1300
1301/**
1302 * Allocates an empty display mode and links it into the doubly linked list of
1303 * modes pointed to by pScrn->modes. Returns a pointer to the newly allocated
1304 * memory.
1305 */
1306static DisplayModePtr vboxAddEmptyScreenMode(ScrnInfoPtr pScrn)
1307{
1308 DisplayModePtr pMode = xnfcalloc(sizeof(DisplayModeRec), 1);
1309
1310 TRACE_ENTRY();
1311 if (!pScrn->modes)
1312 {
1313 pScrn->modes = pMode;
1314 pMode->next = pMode;
1315 pMode->prev = pMode;
1316 }
1317 else
1318 {
1319 pMode->next = pScrn->modes;
1320 pMode->prev = pScrn->modes->prev;
1321 pMode->next->prev = pMode;
1322 pMode->prev->next = pMode;
1323 }
1324 return pMode;
1325}
1326
1327/**
1328 * Create display mode entries in the screen information structure for each
1329 * of the initial graphics modes that we wish to support. This includes:
1330 * - An initial mode, of the size requested by the caller
1331 * - Two dynamic modes, one of which will be updated to match the last size
1332 * hint from the host on each mode switch, but initially also of the
1333 * requested size
1334 * - Several standard modes, if possible ones that the host likes
1335 * - Any modes that the user requested in xorg.conf/XFree86Config
1336 */
1337void vboxAddModes(ScrnInfoPtr pScrn, uint32_t cxInit, uint32_t cyInit)
1338{
1339 unsigned cx = 0, cy = 0, cIndex = 0;
1340 /* For reasons related to the way RandR 1.1 is implemented, we need to
1341 * make sure that the initial mode (more precisely, a mode equal to the
1342 * initial virtual resolution) is always present in the mode list. RandR
1343 * has the assumption build in that there will either be a mode of that
1344 * size present at all times, or that the first mode in the list will
1345 * always be smaller than the initial virtual resolution. Since our
1346 * approach to dynamic resizing isn't quite the way RandR was intended to
1347 * be, and breaks the second assumption, we guarantee the first. */
1348 DisplayModePtr pMode = vboxAddEmptyScreenMode(pScrn);
1349 vboxFillDisplayMode(pMode, "VBoxInitialMode", cxInit, cyInit);
1350 /* Create our two dynamic modes. */
1351 pMode = vboxAddEmptyScreenMode(pScrn);
1352 vboxFillDisplayMode(pMode, "VBoxDynamicMode", cxInit, cyInit);
1353 pMode = vboxAddEmptyScreenMode(pScrn);
1354 vboxFillDisplayMode(pMode, "VBoxDynamicMode", cxInit, cyInit);
1355 /* Add standard modes supported by the host */
1356 for ( ; ; )
1357 {
1358 char szName[256];
1359 cIndex = vboxNextStandardMode(pScrn, cIndex, &cx, &cy, NULL);
1360 if (cIndex == 0)
1361 break;
1362 sprintf(szName, "VBox-%ux%u", cx, cy);
1363 pMode = vboxAddEmptyScreenMode(pScrn);
1364 vboxFillDisplayMode(pMode, szName, cx, cy);
1365 }
1366 /* And finally any modes specified by the user. We assume here that
1367 * the mode names reflect the mode sizes. */
1368 for (unsigned i = 0; pScrn->display->modes != NULL
1369 && pScrn->display->modes[i] != NULL; i++)
1370 {
1371 if (sscanf(pScrn->display->modes[i], "%ux%u", &cx, &cy) == 2)
1372 {
1373 pMode = vboxAddEmptyScreenMode(pScrn);
1374 vboxFillDisplayMode(pMode, pScrn->display->modes[i], cx, cy);
1375 }
1376 }
1377}
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