VirtualBox

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

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

Additions/x11/vboxvideo: some more simplification

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 43.3 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 xf86DestroyCursorInfoRec(pVBox->pCurs);
176 pVBox->pCurs = 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 TRACE_ENTRY();
422
423 if (!pVBox->useDevice)
424 return FALSE;
425 pVBox->useVbva = vboxInitVbva(pScrn->scrnIndex, pScreen, pVBox);
426 return TRUE;
427}
428
429Bool
430vbox_device_available(VBOXPtr pVBox)
431{
432 return pVBox->useDevice;
433}
434
435static void
436vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
437{
438 int rc;
439
440 rc = VbglR3SetPointerShape(0, 0, 0, 0, 0, NULL, 0);
441 if (RT_FAILURE(rc))
442 {
443 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not hide the virtual mouse pointer, VBox error %d.\n", rc);
444 /* Play safe, and disable the hardware cursor until the next mode
445 * switch, since obviously something happened that we didn't
446 * anticipate. */
447 pVBox->forceSWCursor = TRUE;
448 }
449}
450
451static void
452vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
453{
454 int rc;
455
456 if (!vbox_host_uses_hwcursor(pScrn))
457 return;
458 rc = VbglR3SetPointerShape(VBOX_MOUSE_POINTER_VISIBLE, 0, 0, 0, 0, NULL, 0);
459 if (RT_FAILURE(rc)) {
460 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not unhide the virtual mouse pointer.\n");
461 /* Play safe, and disable the hardware cursor until the next mode
462 * switch, since obviously something happened that we didn't
463 * anticipate. */
464 pVBox->forceSWCursor = TRUE;
465 }
466}
467
468static void
469vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
470 unsigned char *image)
471{
472 int rc;
473 VMMDevReqMousePointer *reqp;
474 reqp = (VMMDevReqMousePointer *)image;
475
476#ifdef DEBUG_POINTER
477 vbox_show_shape(reqp->width, reqp->height, 0, image);
478#endif
479
480 rc = VbglR3SetPointerShapeReq(reqp);
481 if (RT_FAILURE(rc)) {
482 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to set the virtual mouse pointer image.\n");
483 /* Play safe, and disable the hardware cursor until the next mode
484 * switch, since obviously something happened that we didn't
485 * anticipate. */
486 pVBox->forceSWCursor = TRUE;
487 }
488}
489
490static void
491vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
492{
493 NOREF(pScrn);
494 NOREF(bg);
495 NOREF(fg);
496 /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
497}
498
499
500static void
501vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
502{
503 /* Nothing to do here, as we are telling the guest where the mouse is,
504 * not vice versa. */
505 NOREF(pScrn);
506 NOREF(x);
507 NOREF(y);
508}
509
510static void
511vbox_hide_cursor(ScrnInfoPtr pScrn)
512{
513 VBOXPtr pVBox = pScrn->driverPrivate;
514
515 vbox_vmm_hide_cursor(pScrn, pVBox);
516}
517
518static void
519vbox_show_cursor(ScrnInfoPtr pScrn)
520{
521 VBOXPtr pVBox = pScrn->driverPrivate;
522
523 vbox_vmm_show_cursor(pScrn, pVBox);
524}
525
526static void
527vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image)
528{
529 VBOXPtr pVBox = pScrn->driverPrivate;
530
531 vbox_vmm_load_cursor_image(pScrn, pVBox, image);
532}
533
534static Bool
535vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
536{
537 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
538 return vbox_host_uses_hwcursor(pScrn);
539}
540
541static unsigned char
542color_to_byte(unsigned c)
543{
544 return (c >> 8) & 0xff;
545}
546
547static unsigned char *
548vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
549{
550 VBOXPtr pVBox;
551 CursorBitsPtr bitsp;
552 unsigned short w, h, x, y;
553 unsigned char *c, *p, *pm, *ps, *m;
554 size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
555 CARD32 fc, bc, *cp;
556 int rc, scrnIndex = infoPtr->pScrn->scrnIndex;
557 VMMDevReqMousePointer *reqp;
558
559 pVBox = infoPtr->pScrn->driverPrivate;
560 bitsp = pCurs->bits;
561 w = bitsp->width;
562 h = bitsp->height;
563
564 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
565 RETERROR(scrnIndex, NULL,
566 "Error invalid cursor dimensions %dx%d\n", w, h);
567
568 if ((bitsp->xhot > w) || (bitsp->yhot > h))
569 RETERROR(scrnIndex, NULL,
570 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
571 bitsp->xhot, bitsp->yhot, w, h);
572
573 srcPitch = PixmapBytePad (bitsp->width, 1);
574 dstPitch = (w + 7) / 8;
575 sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
576 sizeRgba = w * h * 4;
577 sizeRequest = sizeMask + sizeRgba + sizeof(VMMDevReqMousePointer);
578
579 p = c = calloc (1, sizeRequest);
580 if (!c)
581 RETERROR(scrnIndex, NULL,
582 "Error failed to alloc %lu bytes for cursor\n",
583 (unsigned long) sizeRequest);
584
585 rc = vmmdevInitRequest((VMMDevRequestHeader *)p, VMMDevReq_SetPointerShape);
586 if (RT_FAILURE(rc))
587 {
588 xf86DrvMsg(scrnIndex, X_ERROR, "Could not init VMM request: rc = %d\n", rc);
589 free(p);
590 return NULL;
591 }
592
593 m = p + offsetof(VMMDevReqMousePointer, pointerData);
594 cp = (CARD32 *)(m + sizeMask);
595
596 TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
597 w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
598 TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp);
599
600 fc = color_to_byte (pCurs->foreBlue)
601 | (color_to_byte (pCurs->foreGreen) << 8)
602 | (color_to_byte (pCurs->foreRed) << 16);
603
604 bc = color_to_byte (pCurs->backBlue)
605 | (color_to_byte (pCurs->backGreen) << 8)
606 | (color_to_byte (pCurs->backRed) << 16);
607
608 /*
609 * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
610 * Xorg:
611 * The mask is a bitmap indicating which parts of the cursor are
612 * transparent and which parts are drawn. The source is a bitmap
613 * indicating which parts of the non-transparent portion of the
614 * the cursor should be painted in the foreground color and which
615 * should be painted in the background color. By default, set bits
616 * indicate the opaque part of the mask bitmap and clear bits
617 * indicate the transparent part.
618 * VBox:
619 * The color data is the XOR mask. The AND mask bits determine
620 * which pixels of the color data (XOR mask) will replace (overwrite)
621 * the screen pixels (AND mask bit = 0) and which ones will be XORed
622 * with existing screen pixels (AND mask bit = 1).
623 * For example when you have the AND mask all 0, then you see the
624 * correct mouse pointer image surrounded by black square.
625 */
626 for (pm = bitsp->mask, ps = bitsp->source, y = 0;
627 y < h;
628 ++y, pm += srcPitch, ps += srcPitch, m += dstPitch)
629 {
630 for (x = 0; x < w; ++x)
631 {
632 if (pm[x / 8] & (1 << (x % 8)))
633 {
634 /* opaque, leave AND mask bit at 0 */
635 if (ps[x / 8] & (1 << (x % 8)))
636 {
637 *cp++ = fc;
638 PUT_PIXEL('X');
639 }
640 else
641 {
642 *cp++ = bc;
643 PUT_PIXEL('*');
644 }
645 }
646 else
647 {
648 /* transparent, set AND mask bit */
649 m[x / 8] |= 1 << (7 - (x % 8));
650 /* don't change the screen pixel */
651 *cp++ = 0;
652 PUT_PIXEL(' ');
653 }
654 }
655 PUT_PIXEL('\n');
656 }
657
658 reqp = (VMMDevReqMousePointer *)p;
659 reqp->width = w;
660 reqp->height = h;
661 reqp->xHot = bitsp->xhot;
662 reqp->yHot = bitsp->yhot;
663 reqp->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
664 reqp->header.size = sizeRequest;
665
666#ifdef DEBUG_POINTER
667 ErrorF("shape = %p\n", p);
668 vbox_show_shape(w, h, bc, c);
669#endif
670
671 return p;
672}
673
674#ifdef ARGB_CURSOR
675static Bool
676vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
677{
678 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
679 Bool rc = TRUE;
680
681 if (!vbox_host_uses_hwcursor(pScrn))
682 rc = FALSE;
683 if ( rc
684 && ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
685 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
686 || (pScrn->bitsPerPixel <= 8)
687 )
688 )
689 rc = FALSE;
690#ifndef VBOXVIDEO_13
691 /* Evil hack - we use this as another way of poking the driver to update
692 * our list of video modes. */
693 vboxWriteHostModes(pScrn, pScrn->currentMode);
694#endif
695 TRACE_LOG("rc=%s\n", BOOL_STR(rc));
696 return rc;
697}
698
699
700static void
701vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
702{
703 VBOXPtr pVBox;
704 VMMDevReqMousePointer *reqp;
705 CursorBitsPtr bitsp;
706 unsigned short w, h;
707 unsigned short cx, cy;
708 unsigned char *pm;
709 CARD32 *pc;
710 size_t sizeData, sizeMask;
711 CARD8 *p;
712 int scrnIndex;
713 uint32_t fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE
714 | VBOX_MOUSE_POINTER_ALPHA;
715 int rc;
716
717 TRACE_ENTRY();
718 pVBox = pScrn->driverPrivate;
719 bitsp = pCurs->bits;
720 w = bitsp->width;
721 h = bitsp->height;
722 scrnIndex = pScrn->scrnIndex;
723
724 /* Mask must be generated for alpha cursors, that is required by VBox. */
725 /* note: (michael) the next struct must be 32bit aligned. */
726 sizeMask = ((w + 7) / 8 * h + 3) & ~3;
727
728 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
729 RETERROR(scrnIndex, ,
730 "Error invalid cursor dimensions %dx%d\n", w, h);
731
732 if ((bitsp->xhot > w) || (bitsp->yhot > h))
733 RETERROR(scrnIndex, ,
734 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
735 bitsp->xhot, bitsp->yhot, w, h);
736
737 sizeData = w * h * 4 + sizeMask;
738 p = calloc(1, sizeData);
739 if (!p)
740 RETERROR(scrnIndex, ,
741 "Error failed to alloc %lu bytes for cursor\n",
742 (unsigned long)sizeData);
743
744 memcpy(p + sizeMask, bitsp->argb, w * h * 4);
745
746 /* Emulate the AND mask. */
747 pm = p;
748 pc = bitsp->argb;
749
750 /* Init AND mask to 1 */
751 memset(pm, 0xFF, sizeMask);
752
753 /*
754 * The additions driver must provide the AND mask for alpha cursors. The host frontend
755 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
756 * But if the host does not support ARGB, then it simply uses the AND mask and the color
757 * data to draw a normal color cursor.
758 */
759 for (cy = 0; cy < h; cy++)
760 {
761 unsigned char bitmask = 0x80;
762
763 for (cx = 0; cx < w; cx++, bitmask >>= 1)
764 {
765 if (bitmask == 0)
766 bitmask = 0x80;
767
768 if (pc[cx] >= 0xF0000000)
769 pm[cx / 8] &= ~bitmask;
770 }
771
772 /* Point to next source and dest scans */
773 pc += w;
774 pm += (w + 7) / 8;
775 }
776
777 rc = VbglR3SetPointerShape(fFlags, bitsp->xhot, bitsp->yhot, w, h, p, sizeData);
778 TRACE_LOG(": leaving, returning %d\n", rc);
779 free(p);
780}
781#endif
782
783Bool
784vbox_cursor_init(ScreenPtr pScreen)
785{
786 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
787 VBOXPtr pVBox = pScrn->driverPrivate;
788 xf86CursorInfoPtr pCurs = NULL;
789 Bool rc = TRUE;
790
791 TRACE_ENTRY();
792 if (!pVBox->useDevice)
793 return FALSE;
794 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
795 if (!pCurs) {
796 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
797 "Failed to create X Window cursor information structures for virtual mouse.\n");
798 rc = FALSE;
799 }
800 if (rc) {
801 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
802 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
803 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
804 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
805 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
806
807 pCurs->SetCursorColors = vbox_set_cursor_colors;
808 pCurs->SetCursorPosition = vbox_set_cursor_position;
809 pCurs->LoadCursorImage = vbox_load_cursor_image;
810 pCurs->HideCursor = vbox_hide_cursor;
811 pCurs->ShowCursor = vbox_show_cursor;
812 pCurs->UseHWCursor = vbox_use_hw_cursor;
813 pCurs->RealizeCursor = vbox_realize_cursor;
814
815#ifdef ARGB_CURSOR
816 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
817 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
818#endif
819
820 /* Hide the host cursor before we initialise if we wish to use a
821 * software cursor. */
822 if (pVBox->forceSWCursor)
823 vbox_vmm_hide_cursor(pScrn, pVBox);
824 rc = xf86InitCursor(pScreen, pCurs);
825 }
826 if (!rc)
827 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
828 "Failed to enable mouse pointer integration.\n");
829 if (!rc && (pCurs != NULL))
830 xf86DestroyCursorInfoRec(pCurs);
831 return rc;
832}
833
834/**
835 * Inform VBox that we will supply it with dirty rectangle information
836 * and install the dirty rectangle handler.
837 *
838 * @returns TRUE for success, FALSE for failure
839 * @param pScrn Pointer to a structure describing the X screen in use
840 */
841Bool
842vboxEnableVbva(ScrnInfoPtr pScrn)
843{
844 bool rc = TRUE;
845 int scrnIndex = pScrn->scrnIndex;
846 VBOXPtr pVBox = pScrn->driverPrivate;
847
848 TRACE_ENTRY();
849 if (pVBox->useVbva != TRUE)
850 rc = FALSE;
851 if (rc && RT_FAILURE(VbglR3VideoAccelEnable(true)))
852 /* Request not accepted - disable for old hosts. */
853 xf86DrvMsg(scrnIndex, X_ERROR,
854 "Unable to activate VirtualBox graphics acceleration "
855 "- the request to the virtual machine failed. "
856 "You may be running an old version of VirtualBox.\n");
857 pVBox->useVbva = rc;
858 if (!rc)
859 VbglR3VideoAccelEnable(false);
860 return rc;
861}
862
863/**
864 * Inform VBox that we will stop supplying it with dirty rectangle
865 * information. This function is intended to be called when an X
866 * virtual terminal is disabled, or the X server is terminated.
867 *
868 * @returns TRUE for success, FALSE for failure
869 * @param pScrn Pointer to a structure describing the X screen in use
870 */
871Bool
872vboxDisableVbva(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) /* Ths function should not have been called */
880 return FALSE;
881 rc = VbglR3VideoAccelEnable(false);
882 if (RT_FAILURE(rc))
883 {
884 xf86DrvMsg(scrnIndex, X_ERROR,
885 "Unable to disable VirtualBox graphics acceleration "
886 "- the request to the virtual machine failed.\n");
887 }
888 else
889 memset(pVBox->pVbvaMemory, 0, sizeof(VBVAMEMORY));
890 return TRUE;
891}
892
893/**
894 * Inform VBox that we are aware of advanced graphics functions
895 * (i.e. dynamic resizing, seamless).
896 *
897 * @returns TRUE for success, FALSE for failure
898 */
899Bool
900vboxEnableGraphicsCap(VBOXPtr pVBox)
901{
902 TRACE_ENTRY();
903 if (!pVBox->useDevice)
904 return FALSE;
905 return RT_SUCCESS(VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0));
906}
907
908/**
909 * Inform VBox that we are no longer aware of advanced graphics functions
910 * (i.e. dynamic resizing, seamless).
911 *
912 * @returns TRUE for success, FALSE for failure
913 */
914Bool
915vboxDisableGraphicsCap(VBOXPtr pVBox)
916{
917 TRACE_ENTRY();
918 if (!pVBox->useDevice)
919 return FALSE;
920 return RT_SUCCESS(VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS));
921}
922
923/**
924 * Query the last display change request.
925 *
926 * @returns boolean success indicator.
927 * @param pScrn Pointer to the X screen info structure.
928 * @param pcx Where to store the horizontal pixel resolution (0 = do not change).
929 * @param pcy Where to store the vertical pixel resolution (0 = do not change).
930 * @param pcBits Where to store the bits per pixel (0 = do not change).
931 * @param iDisplay Where to store the display number the request was for - 0 for the
932 * primary display, 1 for the first secondary, etc.
933 */
934Bool
935vboxGetDisplayChangeRequest(ScrnInfoPtr pScrn, uint32_t *pcx, uint32_t *pcy,
936 uint32_t *pcBits, uint32_t *piDisplay)
937{
938 VBOXPtr pVBox = pScrn->driverPrivate;
939 TRACE_ENTRY();
940 if (!pVBox->useDevice)
941 return FALSE;
942 int rc = VbglR3GetDisplayChangeRequest(pcx, pcy, pcBits, piDisplay, true);
943 if (RT_SUCCESS(rc))
944 return TRUE;
945 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to obtain the last resolution requested by the guest, rc=%d.\n", rc);
946 return FALSE;
947}
948
949
950/**
951 * Query the host as to whether it likes a specific video mode.
952 *
953 * @returns the result of the query
954 * @param cx the width of the mode being queried
955 * @param cy the height of the mode being queried
956 * @param cBits the bpp of the mode being queried
957 */
958Bool
959vboxHostLikesVideoMode(ScrnInfoPtr pScrn, uint32_t cx, uint32_t cy, uint32_t cBits)
960{
961 VBOXPtr pVBox = pScrn->driverPrivate;
962 TRACE_ENTRY();
963 if (!pVBox->useDevice)
964 return TRUE; /* If we can't ask the host then we like everything. */
965 return VbglR3HostLikesVideoMode(cx, cy, cBits);
966}
967
968/**
969 * Check if any seamless mode is enabled.
970 * Seamless is only relevant for the newer Xorg modules.
971 *
972 * @returns the result of the query
973 * (true = seamless enabled, false = seamless not enabled)
974 * @param pScrn Screen info pointer.
975 */
976Bool
977vboxGuestIsSeamless(ScrnInfoPtr pScrn)
978{
979 VMMDevSeamlessMode mode;
980 VBOXPtr pVBox = pScrn->driverPrivate;
981 TRACE_ENTRY();
982 if (!pVBox->useDevice)
983 return FALSE;
984 if (RT_FAILURE(VbglR3SeamlessGetLastEvent(&mode)))
985 return FALSE;
986 return (mode != VMMDev_Seamless_Disabled);
987}
988
989/**
990 * Save video mode parameters to the registry.
991 *
992 * @returns iprt status value
993 * @param pszName the name to save the mode parameters under
994 * @param cx mode width
995 * @param cy mode height
996 * @param cBits bits per pixel for the mode
997 */
998Bool
999vboxSaveVideoMode(ScrnInfoPtr pScrn, uint32_t cx, uint32_t cy, uint32_t cBits)
1000{
1001 VBOXPtr pVBox = pScrn->driverPrivate;
1002 TRACE_ENTRY();
1003 if (!pVBox->useDevice)
1004 return FALSE;
1005 return RT_SUCCESS(VbglR3SaveVideoMode("SavedMode", cx, cy, cBits));
1006}
1007
1008/**
1009 * Retrieve video mode parameters from the registry.
1010 *
1011 * @returns iprt status value
1012 * @param pszName the name under which the mode parameters are saved
1013 * @param pcx where to store the mode width
1014 * @param pcy where to store the mode height
1015 * @param pcBits where to store the bits per pixel for the mode
1016 */
1017Bool
1018vboxRetrieveVideoMode(ScrnInfoPtr pScrn, uint32_t *pcx, uint32_t *pcy, uint32_t *pcBits)
1019{
1020 VBOXPtr pVBox = pScrn->driverPrivate;
1021 TRACE_ENTRY();
1022 if (!pVBox->useDevice)
1023 return FALSE;
1024 int rc = VbglR3RetrieveVideoMode("SavedMode", pcx, pcy, pcBits);
1025 if (RT_SUCCESS(rc))
1026 TRACE_LOG("Retrieved a video mode of %dx%dx%d\n", *pcx, *pcy, *pcBits);
1027 else
1028 TRACE_LOG("Failed to retrieve video mode, error %d\n", rc);
1029 return (RT_SUCCESS(rc));
1030}
1031
1032/**
1033 * Fills a display mode M with a built-in mode of name pszName and dimensions
1034 * cx and cy.
1035 */
1036static void vboxFillDisplayMode(DisplayModePtr m, const char *pszName,
1037 unsigned cx, unsigned cy)
1038{
1039 TRACE_LOG("pszName=%s, cx=%u, cy=%u\n", pszName, cx, cy);
1040 m->status = MODE_OK;
1041 m->type = M_T_BUILTIN;
1042 /* VBox only supports screen widths which are a multiple of 8 */
1043 m->HDisplay = cx & ~7;
1044 m->HSyncStart = m->HDisplay + 2;
1045 m->HSyncEnd = m->HDisplay + 4;
1046 m->HTotal = m->HDisplay + 6;
1047 m->VDisplay = cy;
1048 m->VSyncStart = m->VDisplay + 2;
1049 m->VSyncEnd = m->VDisplay + 4;
1050 m->VTotal = m->VDisplay + 6;
1051 m->Clock = m->HTotal * m->VTotal * 60 / 1000; /* kHz */
1052 if (pszName)
1053 {
1054 if (m->name)
1055 free(m->name);
1056 m->name = xnfstrdup(pszName);
1057 }
1058}
1059
1060/** vboxvideo's list of standard video modes */
1061struct
1062{
1063 /** mode width */
1064 uint32_t cx;
1065 /** mode height */
1066 uint32_t cy;
1067} vboxStandardModes[] =
1068{
1069 { 1600, 1200 },
1070 { 1440, 1050 },
1071 { 1280, 960 },
1072 { 1024, 768 },
1073 { 800, 600 },
1074 { 640, 480 },
1075 { 0, 0 }
1076};
1077enum
1078{
1079 vboxNumStdModes = sizeof(vboxStandardModes) / sizeof(vboxStandardModes[0])
1080};
1081
1082/**
1083 * Returns a standard mode which the host likes. Can be called multiple
1084 * times with the index returned by the previous call to get a list of modes.
1085 * @returns the index of the mode in the list, or 0 if no more modes are
1086 * available
1087 * @param pScrn the screen information structure
1088 * @param pScrn->bitsPerPixel
1089 * if this is non-null, only modes with this BPP will be
1090 * returned
1091 * @param cIndex the index of the last mode queried, or 0 to query the
1092 * first mode available. Note: the first index is 1
1093 * @param pcx where to store the mode's width
1094 * @param pcy where to store the mode's height
1095 * @param pcBits where to store the mode's BPP
1096 */
1097static unsigned vboxNextStandardMode(ScrnInfoPtr pScrn, unsigned cIndex,
1098 uint32_t *pcx, uint32_t *pcy,
1099 uint32_t *pcBits)
1100{
1101 XF86ASSERT(cIndex < vboxNumStdModes,
1102 ("cIndex = %d, vboxNumStdModes = %d\n", cIndex,
1103 vboxNumStdModes));
1104 for (unsigned i = cIndex; i < vboxNumStdModes - 1; ++i)
1105 {
1106 uint32_t cBits = pScrn->bitsPerPixel;
1107 uint32_t cx = vboxStandardModes[i].cx;
1108 uint32_t cy = vboxStandardModes[i].cy;
1109
1110 if (cBits != 0 && !vboxHostLikesVideoMode(pScrn, cx, cy, cBits))
1111 continue;
1112 if (vboxHostLikesVideoMode(pScrn, cx, cy, 32))
1113 cBits = 32;
1114 else if (vboxHostLikesVideoMode(pScrn, cx, cy, 16))
1115 cBits = 16;
1116 else
1117 continue;
1118 if (pcx)
1119 *pcx = cx;
1120 if (pcy)
1121 *pcy = cy;
1122 if (pcBits)
1123 *pcBits = cBits;
1124 return i + 1;
1125 }
1126 return 0;
1127}
1128
1129/**
1130 * Returns the preferred video mode. The current order of preference is
1131 * (from highest to least preferred):
1132 * - The mode corresponding to the last size hint from the host
1133 * - The video mode saved from the last session
1134 * - The largest standard mode which the host likes, falling back to
1135 * 640x480x32 as a worst case
1136 * - If the host can't be contacted at all, we return 1024x768x32
1137 *
1138 * The return type is void as we guarantee we will return some mode.
1139 */
1140void vboxGetPreferredMode(ScrnInfoPtr pScrn, uint32_t *pcx,
1141 uint32_t *pcy, uint32_t *pcBits)
1142{
1143 /* Query the host for the preferred resolution and colour depth */
1144 uint32_t cx = 0, cy = 0, iDisplay = 0, cBits = 32;
1145 VBOXPtr pVBox = pScrn->driverPrivate;
1146
1147 TRACE_ENTRY();
1148 if (pVBox->useDevice)
1149 {
1150 bool found = vboxGetDisplayChangeRequest(pScrn, &cx, &cy, &cBits, &iDisplay);
1151 if ((cx == 0) || (cy == 0))
1152 found = false;
1153 if (!found)
1154 found = vboxRetrieveVideoMode(pScrn, &cx, &cy, &cBits);
1155 if ((cx == 0) || (cy == 0))
1156 found = false;
1157 if (found)
1158 /* Adjust to a multiple of eight */
1159 cx &= ~7;
1160 if (!found)
1161 found = (vboxNextStandardMode(pScrn, 0, &cx, &cy, &cBits) != 0);
1162 if (!found)
1163 {
1164 /* Last resort */
1165 cx = 640;
1166 cy = 480;
1167 cBits = 32;
1168 }
1169 }
1170 else
1171 {
1172 cx = 1024;
1173 cy = 768;
1174 }
1175 if (pcx)
1176 *pcx = cx;
1177 if (pcy)
1178 *pcy = cy;
1179 if (pcx)
1180 *pcBits = cBits;
1181}
1182
1183/* Move a screen mode found to the end of the list, so that RandR will give
1184 * it the highest priority when a mode switch is requested. Returns the mode
1185 * that was previously before the mode in the list in order to allow the
1186 * caller to continue walking the list. */
1187static DisplayModePtr vboxMoveModeToFront(ScrnInfoPtr pScrn,
1188 DisplayModePtr pMode)
1189{
1190 DisplayModePtr pPrev = pMode->prev;
1191 if (pMode != pScrn->modes)
1192 {
1193 pMode->prev->next = pMode->next;
1194 pMode->next->prev = pMode->prev;
1195 pMode->next = pScrn->modes;
1196 pMode->prev = pScrn->modes->prev;
1197 pMode->next->prev = pMode;
1198 pMode->prev->next = pMode;
1199 pScrn->modes = pMode;
1200 }
1201 return pPrev;
1202}
1203
1204/**
1205 * Rewrites the first dynamic mode found which is not the current screen mode
1206 * to contain the host's currently preferred screen size, then moves that
1207 * mode to the front of the screen information structure's mode list.
1208 * Additionally, if the current mode is not dynamic, the second dynamic mode
1209 * will be set to match the current mode and also added to the front. This
1210 * ensures that the user can always reset the current size to kick the driver
1211 * to update its mode list.
1212 */
1213void vboxWriteHostModes(ScrnInfoPtr pScrn, DisplayModePtr pCurrent)
1214{
1215 uint32_t cx = 0, cy = 0, iDisplay = 0, cBits = 0;
1216 DisplayModePtr pMode;
1217 bool found = false;
1218
1219 TRACE_ENTRY();
1220 vboxGetPreferredMode(pScrn, &cx, &cy, &cBits);
1221#ifdef DEBUG
1222 /* Count the number of modes for sanity */
1223 unsigned cModes = 1, cMode = 0;
1224 DisplayModePtr pCount;
1225 for (pCount = pScrn->modes; ; pCount = pCount->next, ++cModes)
1226 if (pCount->next == pScrn->modes)
1227 break;
1228#endif
1229 for (pMode = pScrn->modes; ; pMode = pMode->next)
1230 {
1231#ifdef DEBUG
1232 XF86ASSERT (cMode++ < cModes, (NULL));
1233#endif
1234 if ( pMode != pCurrent
1235 && !strcmp(pMode->name, "VBoxDynamicMode"))
1236 {
1237 if (!found)
1238 vboxFillDisplayMode(pMode, NULL, cx, cy);
1239 else if (pCurrent)
1240 vboxFillDisplayMode(pMode, NULL, pCurrent->HDisplay,
1241 pCurrent->VDisplay);
1242 found = true;
1243 pMode = vboxMoveModeToFront(pScrn, pMode);
1244 }
1245 if (pMode->next == pScrn->modes)
1246 break;
1247 }
1248 XF86ASSERT (found,
1249 ("vboxvideo: no free dynamic mode found. Exiting.\n"));
1250 XF86ASSERT ( (pScrn->modes->HDisplay == (long) cx)
1251 || ( (pScrn->modes->HDisplay == pCurrent->HDisplay)
1252 && (pScrn->modes->next->HDisplay == (long) cx)),
1253 ("pScrn->modes->HDisplay=%u, pScrn->modes->next->HDisplay=%u\n",
1254 pScrn->modes->HDisplay, pScrn->modes->next->HDisplay));
1255 XF86ASSERT ( (pScrn->modes->VDisplay == (long) cy)
1256 || ( (pScrn->modes->VDisplay == pCurrent->VDisplay)
1257 && (pScrn->modes->next->VDisplay == (long) cy)),
1258 ("pScrn->modes->VDisplay=%u, pScrn->modes->next->VDisplay=%u\n",
1259 pScrn->modes->VDisplay, pScrn->modes->next->VDisplay));
1260}
1261
1262/**
1263 * Allocates an empty display mode and links it into the doubly linked list of
1264 * modes pointed to by pScrn->modes. Returns a pointer to the newly allocated
1265 * memory.
1266 */
1267static DisplayModePtr vboxAddEmptyScreenMode(ScrnInfoPtr pScrn)
1268{
1269 DisplayModePtr pMode = xnfcalloc(sizeof(DisplayModeRec), 1);
1270
1271 TRACE_ENTRY();
1272 if (!pScrn->modes)
1273 {
1274 pScrn->modes = pMode;
1275 pMode->next = pMode;
1276 pMode->prev = pMode;
1277 }
1278 else
1279 {
1280 pMode->next = pScrn->modes;
1281 pMode->prev = pScrn->modes->prev;
1282 pMode->next->prev = pMode;
1283 pMode->prev->next = pMode;
1284 }
1285 return pMode;
1286}
1287
1288/**
1289 * Create display mode entries in the screen information structure for each
1290 * of the initial graphics modes that we wish to support. This includes:
1291 * - An initial mode, of the size requested by the caller
1292 * - Two dynamic modes, one of which will be updated to match the last size
1293 * hint from the host on each mode switch, but initially also of the
1294 * requested size
1295 * - Several standard modes, if possible ones that the host likes
1296 * - Any modes that the user requested in xorg.conf/XFree86Config
1297 */
1298void vboxAddModes(ScrnInfoPtr pScrn, uint32_t cxInit, uint32_t cyInit)
1299{
1300 unsigned cx = 0, cy = 0, cIndex = 0;
1301 /* For reasons related to the way RandR 1.1 is implemented, we need to
1302 * make sure that the initial mode (more precisely, a mode equal to the
1303 * initial virtual resolution) is always present in the mode list. RandR
1304 * has the assumption build in that there will either be a mode of that
1305 * size present at all times, or that the first mode in the list will
1306 * always be smaller than the initial virtual resolution. Since our
1307 * approach to dynamic resizing isn't quite the way RandR was intended to
1308 * be, and breaks the second assumption, we guarantee the first. */
1309 DisplayModePtr pMode = vboxAddEmptyScreenMode(pScrn);
1310 vboxFillDisplayMode(pMode, "VBoxInitialMode", cxInit, cyInit);
1311 /* Create our two dynamic modes. */
1312 pMode = vboxAddEmptyScreenMode(pScrn);
1313 vboxFillDisplayMode(pMode, "VBoxDynamicMode", cxInit, cyInit);
1314 pMode = vboxAddEmptyScreenMode(pScrn);
1315 vboxFillDisplayMode(pMode, "VBoxDynamicMode", cxInit, cyInit);
1316 /* Add standard modes supported by the host */
1317 for ( ; ; )
1318 {
1319 char szName[256];
1320 cIndex = vboxNextStandardMode(pScrn, cIndex, &cx, &cy, NULL);
1321 if (cIndex == 0)
1322 break;
1323 sprintf(szName, "VBox-%ux%u", cx, cy);
1324 pMode = vboxAddEmptyScreenMode(pScrn);
1325 vboxFillDisplayMode(pMode, szName, cx, cy);
1326 }
1327 /* And finally any modes specified by the user. We assume here that
1328 * the mode names reflect the mode sizes. */
1329 for (unsigned i = 0; pScrn->display->modes != NULL
1330 && pScrn->display->modes[i] != NULL; i++)
1331 {
1332 if (sscanf(pScrn->display->modes[i], "%ux%u", &cx, &cy) == 2)
1333 {
1334 pMode = vboxAddEmptyScreenMode(pScrn);
1335 vboxFillDisplayMode(pMode, pScrn->display->modes[i], cx, cy);
1336 }
1337 }
1338}
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