VirtualBox

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

Last change on this file since 49764 was 46042, checked in by vboxsync, 12 years ago

All source files should have $Id:$ keywords at the top.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.6 KB
Line 
1/* $Id: vboxutils.c 46042 2013-05-13 19:45:02Z vboxsync $ */
2/** @file
3 * VirtualBox X11 Additions graphics driver utility functions
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <VBox/VMMDev.h>
19#include <VBox/VBoxGuestLib.h>
20
21#ifndef PCIACCESS
22# include <xf86Pci.h>
23# include <Pci.h>
24#endif
25
26#include "xf86.h"
27#define NEED_XF86_TYPES
28#include <iprt/string.h>
29#include "compiler.h"
30
31#include "vboxvideo.h"
32
33#ifdef XORG_7X
34# include <stdio.h>
35# include <stdlib.h>
36#endif
37
38/**************************************************************************
39* Main functions *
40**************************************************************************/
41
42/**
43 * Inform VBox that we are aware of advanced graphics functions
44 * (i.e. dynamic resizing, seamless).
45 *
46 * @returns TRUE for success, FALSE for failure
47 */
48Bool
49vboxEnableGraphicsCap(VBOXPtr pVBox)
50{
51 TRACE_ENTRY();
52 if (!pVBox->useDevice)
53 return FALSE;
54 return RT_SUCCESS(VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0));
55}
56
57/**
58 * Inform VBox that we are no longer aware of advanced graphics functions
59 * (i.e. dynamic resizing, seamless).
60 *
61 * @returns TRUE for success, FALSE for failure
62 */
63Bool
64vboxDisableGraphicsCap(VBOXPtr pVBox)
65{
66 TRACE_ENTRY();
67 if (!pVBox->useDevice)
68 return FALSE;
69 return RT_SUCCESS(VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS));
70}
71
72/**
73 * Query the last display change request.
74 *
75 * @returns boolean success indicator.
76 * @param pScrn Pointer to the X screen info structure.
77 * @param pcx Where to store the horizontal pixel resolution (0 = do not change).
78 * @param pcy Where to store the vertical pixel resolution (0 = do not change).
79 * @param pcBits Where to store the bits per pixel (0 = do not change).
80 * @param iDisplay Where to store the display number the request was for - 0 for the
81 * primary display, 1 for the first secondary, etc.
82 */
83Bool
84vboxGetDisplayChangeRequest(ScrnInfoPtr pScrn, uint32_t *pcx, uint32_t *pcy,
85 uint32_t *pcBits, uint32_t *piDisplay)
86{
87 VBOXPtr pVBox = pScrn->driverPrivate;
88 TRACE_ENTRY();
89 if (!pVBox->useDevice)
90 return FALSE;
91 int rc = VbglR3GetDisplayChangeRequest(pcx, pcy, pcBits, piDisplay, false);
92 if (RT_SUCCESS(rc))
93 return TRUE;
94 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to obtain the last resolution requested by the guest, rc=%d.\n", rc);
95 return FALSE;
96}
97
98
99/**
100 * Query the host as to whether it likes a specific video mode.
101 *
102 * @returns the result of the query
103 * @param cx the width of the mode being queried
104 * @param cy the height of the mode being queried
105 * @param cBits the bpp of the mode being queried
106 */
107Bool
108vboxHostLikesVideoMode(ScrnInfoPtr pScrn, uint32_t cx, uint32_t cy, uint32_t cBits)
109{
110 VBOXPtr pVBox = pScrn->driverPrivate;
111 TRACE_ENTRY();
112 if (!pVBox->useDevice)
113 return TRUE; /* If we can't ask the host then we like everything. */
114 return VbglR3HostLikesVideoMode(cx, cy, cBits);
115}
116
117/**
118 * Check if any seamless mode is enabled.
119 * Seamless is only relevant for the newer Xorg modules.
120 *
121 * @returns the result of the query
122 * (true = seamless enabled, false = seamless not enabled)
123 * @param pScrn Screen info pointer.
124 */
125Bool
126vboxGuestIsSeamless(ScrnInfoPtr pScrn)
127{
128 VMMDevSeamlessMode mode;
129 VBOXPtr pVBox = pScrn->driverPrivate;
130 TRACE_ENTRY();
131 if (!pVBox->useDevice)
132 return FALSE;
133 if (RT_FAILURE(VbglR3SeamlessGetLastEvent(&mode)))
134 return FALSE;
135 return (mode != VMMDev_Seamless_Disabled);
136}
137
138/**
139 * Save video mode parameters to the registry.
140 *
141 * @returns iprt status value
142 * @param pszName the name to save the mode parameters under
143 * @param cx mode width
144 * @param cy mode height
145 * @param cBits bits per pixel for the mode
146 */
147Bool
148vboxSaveVideoMode(ScrnInfoPtr pScrn, uint32_t cx, uint32_t cy, uint32_t cBits)
149{
150 VBOXPtr pVBox = pScrn->driverPrivate;
151 TRACE_ENTRY();
152 if (!pVBox->useDevice)
153 return FALSE;
154 return RT_SUCCESS(VbglR3SaveVideoMode("SavedMode", cx, cy, cBits));
155}
156
157/**
158 * Retrieve video mode parameters from the registry.
159 *
160 * @returns iprt status value
161 * @param pszName the name under which the mode parameters are saved
162 * @param pcx where to store the mode width
163 * @param pcy where to store the mode height
164 * @param pcBits where to store the bits per pixel for the mode
165 */
166Bool
167vboxRetrieveVideoMode(ScrnInfoPtr pScrn, uint32_t *pcx, uint32_t *pcy, uint32_t *pcBits)
168{
169 VBOXPtr pVBox = pScrn->driverPrivate;
170 int rc;
171 TRACE_ENTRY();
172 if (!pVBox->useDevice)
173 rc = VERR_NOT_AVAILABLE;
174 else
175 rc = VbglR3RetrieveVideoMode("SavedMode", pcx, pcy, pcBits);
176 if (RT_SUCCESS(rc))
177 TRACE_LOG("Retrieved a video mode of %dx%dx%d\n", *pcx, *pcy, *pcBits);
178 else
179 TRACE_LOG("Failed to retrieve video mode, error %d\n", rc);
180 return (RT_SUCCESS(rc));
181}
182
183/**
184 * Fills a display mode M with a built-in mode of name pszName and dimensions
185 * cx and cy.
186 */
187static void vboxFillDisplayMode(ScrnInfoPtr pScrn, DisplayModePtr m,
188 const char *pszName, unsigned cx, unsigned cy)
189{
190 VBOXPtr pVBox = pScrn->driverPrivate;
191 TRACE_LOG("pszName=%s, cx=%u, cy=%u\n", pszName, cx, cy);
192 m->status = MODE_OK;
193 m->type = M_T_BUILTIN;
194 /* Older versions of VBox only support screen widths which are a multiple
195 * of 8 */
196 if (pVBox->fAnyX)
197 m->HDisplay = cx;
198 else
199 m->HDisplay = cx & ~7;
200 m->HSyncStart = m->HDisplay + 2;
201 m->HSyncEnd = m->HDisplay + 4;
202 m->HTotal = m->HDisplay + 6;
203 m->VDisplay = cy;
204 m->VSyncStart = m->VDisplay + 2;
205 m->VSyncEnd = m->VDisplay + 4;
206 m->VTotal = m->VDisplay + 6;
207 m->Clock = m->HTotal * m->VTotal * 60 / 1000; /* kHz */
208 if (pszName)
209 {
210 if (m->name)
211 free(m->name);
212 m->name = xnfstrdup(pszName);
213 }
214}
215
216/** vboxvideo's list of standard video modes */
217struct
218{
219 /** mode width */
220 uint32_t cx;
221 /** mode height */
222 uint32_t cy;
223} vboxStandardModes[] =
224{
225 { 1600, 1200 },
226 { 1440, 1050 },
227 { 1280, 960 },
228 { 1024, 768 },
229 { 800, 600 },
230 { 640, 480 },
231 { 0, 0 }
232};
233enum
234{
235 vboxNumStdModes = sizeof(vboxStandardModes) / sizeof(vboxStandardModes[0])
236};
237
238/**
239 * Returns a standard mode which the host likes. Can be called multiple
240 * times with the index returned by the previous call to get a list of modes.
241 * @returns the index of the mode in the list, or 0 if no more modes are
242 * available
243 * @param pScrn the screen information structure
244 * @param pScrn->bitsPerPixel
245 * if this is non-null, only modes with this BPP will be
246 * returned
247 * @param cIndex the index of the last mode queried, or 0 to query the
248 * first mode available. Note: the first index is 1
249 * @param pcx where to store the mode's width
250 * @param pcy where to store the mode's height
251 * @param pcBits where to store the mode's BPP
252 */
253unsigned vboxNextStandardMode(ScrnInfoPtr pScrn, unsigned cIndex,
254 uint32_t *pcx, uint32_t *pcy,
255 uint32_t *pcBits)
256{
257 unsigned i;
258
259 XF86ASSERT(cIndex < vboxNumStdModes,
260 ("cIndex = %d, vboxNumStdModes = %d\n", cIndex,
261 vboxNumStdModes));
262 for (i = cIndex; i < vboxNumStdModes - 1; ++i)
263 {
264 uint32_t cBits = pScrn->bitsPerPixel;
265 uint32_t cx = vboxStandardModes[i].cx;
266 uint32_t cy = vboxStandardModes[i].cy;
267
268 if (cBits != 0 && !vboxHostLikesVideoMode(pScrn, cx, cy, cBits))
269 continue;
270 if (vboxHostLikesVideoMode(pScrn, cx, cy, 32))
271 cBits = 32;
272 else if (vboxHostLikesVideoMode(pScrn, cx, cy, 16))
273 cBits = 16;
274 else
275 continue;
276 if (pcx)
277 *pcx = cx;
278 if (pcy)
279 *pcy = cy;
280 if (pcBits)
281 *pcBits = cBits;
282 return i + 1;
283 }
284 return 0;
285}
286
287/**
288 * Returns the preferred video mode. The current order of preference is
289 * (from highest to least preferred):
290 * - The mode corresponding to the last size hint from the host
291 * - The video mode saved from the last session
292 * - The largest standard mode which the host likes, falling back to
293 * 640x480x32 as a worst case
294 * - If the host can't be contacted at all, we return 1024x768x32
295 *
296 * The return type is void as we guarantee we will return some mode.
297 */
298void vboxGetPreferredMode(ScrnInfoPtr pScrn, uint32_t iScreen, uint32_t *pcx,
299 uint32_t *pcy, uint32_t *pcBits)
300{
301 /* Query the host for the preferred resolution and colour depth */
302 uint32_t cx = 0, cy = 0, iScreenIn = iScreen, cBits = 32;
303 VBOXPtr pVBox = pScrn->driverPrivate;
304
305 TRACE_LOG("iScreen=%u\n", iScreen);
306 bool found = false;
307 if ( pVBox->aPreferredSize[iScreen].cx
308 && pVBox->aPreferredSize[iScreen].cy)
309 {
310 cx = pVBox->aPreferredSize[iScreen].cx;
311 cy = pVBox->aPreferredSize[iScreen].cy;
312 found = true;
313 }
314 if (pVBox->useDevice)
315 {
316 if (!found)
317 found = vboxGetDisplayChangeRequest(pScrn, &cx, &cy, &cBits,
318 &iScreenIn);
319 if ((cx == 0) || (cy == 0) || iScreenIn != iScreen)
320 found = false;
321 if (!found)
322 found = vboxRetrieveVideoMode(pScrn, &cx, &cy, &cBits);
323 if ((cx == 0) || (cy == 0))
324 found = false;
325 if (!found)
326 found = (vboxNextStandardMode(pScrn, 0, &cx, &cy, &cBits) != 0);
327 if (!found)
328 {
329 /* Last resort */
330 cx = 640;
331 cy = 480;
332 cBits = 32;
333 }
334 }
335 else
336 {
337 cx = 1024;
338 cy = 768;
339 }
340 if (pcx)
341 *pcx = cx;
342 if (pcy)
343 *pcy = cy;
344 if (pcBits)
345 *pcBits = cBits;
346 TRACE_LOG("cx=%u, cy=%u, cBits=%u\n", cx, cy, cBits);
347}
348
349/* Move a screen mode found to the end of the list, so that RandR will give
350 * it the highest priority when a mode switch is requested. Returns the mode
351 * that was previously before the mode in the list in order to allow the
352 * caller to continue walking the list. */
353static DisplayModePtr vboxMoveModeToFront(ScrnInfoPtr pScrn,
354 DisplayModePtr pMode)
355{
356 DisplayModePtr pPrev = pMode->prev;
357 if (pMode != pScrn->modes)
358 {
359 pMode->prev->next = pMode->next;
360 pMode->next->prev = pMode->prev;
361 pMode->next = pScrn->modes;
362 pMode->prev = pScrn->modes->prev;
363 pMode->next->prev = pMode;
364 pMode->prev->next = pMode;
365 pScrn->modes = pMode;
366 }
367 return pPrev;
368}
369
370/**
371 * Rewrites the first dynamic mode found which is not the current screen mode
372 * to contain the host's currently preferred screen size, then moves that
373 * mode to the front of the screen information structure's mode list.
374 * Additionally, if the current mode is not dynamic, the second dynamic mode
375 * will be set to match the current mode and also added to the front. This
376 * ensures that the user can always reset the current size to kick the driver
377 * to update its mode list.
378 */
379void vboxWriteHostModes(ScrnInfoPtr pScrn, DisplayModePtr pCurrent)
380{
381 uint32_t cx = 0, cy = 0, iDisplay = 0, cBits = 0;
382 DisplayModePtr pMode;
383 bool found = false;
384
385 TRACE_ENTRY();
386 vboxGetPreferredMode(pScrn, 0, &cx, &cy, &cBits);
387#ifdef DEBUG
388 /* Count the number of modes for sanity */
389 unsigned cModes = 1, cMode = 0;
390 DisplayModePtr pCount;
391 for (pCount = pScrn->modes; ; pCount = pCount->next, ++cModes)
392 if (pCount->next == pScrn->modes)
393 break;
394#endif
395 for (pMode = pScrn->modes; ; pMode = pMode->next)
396 {
397#ifdef DEBUG
398 XF86ASSERT (cMode++ < cModes, (NULL));
399#endif
400 if ( pMode != pCurrent
401 && !strcmp(pMode->name, "VBoxDynamicMode"))
402 {
403 if (!found)
404 vboxFillDisplayMode(pScrn, pMode, NULL, cx, cy);
405 else if (pCurrent)
406 vboxFillDisplayMode(pScrn, pMode, NULL, pCurrent->HDisplay,
407 pCurrent->VDisplay);
408 found = true;
409 pMode = vboxMoveModeToFront(pScrn, pMode);
410 }
411 if (pMode->next == pScrn->modes)
412 break;
413 }
414 XF86ASSERT (found,
415 ("vboxvideo: no free dynamic mode found. Exiting.\n"));
416 XF86ASSERT ( (pScrn->modes->HDisplay == (long) cx)
417 || ( (pScrn->modes->HDisplay == pCurrent->HDisplay)
418 && (pScrn->modes->next->HDisplay == (long) cx)),
419 ("pScrn->modes->HDisplay=%u, pScrn->modes->next->HDisplay=%u\n",
420 pScrn->modes->HDisplay, pScrn->modes->next->HDisplay));
421 XF86ASSERT ( (pScrn->modes->VDisplay == (long) cy)
422 || ( (pScrn->modes->VDisplay == pCurrent->VDisplay)
423 && (pScrn->modes->next->VDisplay == (long) cy)),
424 ("pScrn->modes->VDisplay=%u, pScrn->modes->next->VDisplay=%u\n",
425 pScrn->modes->VDisplay, pScrn->modes->next->VDisplay));
426}
427
428/**
429 * Allocates an empty display mode and links it into the doubly linked list of
430 * modes pointed to by pScrn->modes. Returns a pointer to the newly allocated
431 * memory.
432 */
433static DisplayModePtr vboxAddEmptyScreenMode(ScrnInfoPtr pScrn)
434{
435 DisplayModePtr pMode = xnfcalloc(sizeof(DisplayModeRec), 1);
436
437 TRACE_ENTRY();
438 if (!pScrn->modes)
439 {
440 pScrn->modes = pMode;
441 pMode->next = pMode;
442 pMode->prev = pMode;
443 }
444 else
445 {
446 pMode->next = pScrn->modes;
447 pMode->prev = pScrn->modes->prev;
448 pMode->next->prev = pMode;
449 pMode->prev->next = pMode;
450 }
451 return pMode;
452}
453
454/**
455 * Create display mode entries in the screen information structure for each
456 * of the initial graphics modes that we wish to support. This includes:
457 * - An initial mode, of the size requested by the caller
458 * - Two dynamic modes, one of which will be updated to match the last size
459 * hint from the host on each mode switch, but initially also of the
460 * requested size
461 * - Several standard modes, if possible ones that the host likes
462 * - Any modes that the user requested in xorg.conf/XFree86Config
463 */
464void vboxAddModes(ScrnInfoPtr pScrn, uint32_t cxInit, uint32_t cyInit)
465{
466 unsigned cx = 0, cy = 0, cIndex = 0;
467 unsigned i;
468 /* For reasons related to the way RandR 1.1 is implemented, we need to
469 * make sure that the initial mode (more precisely, a mode equal to the
470 * initial virtual resolution) is always present in the mode list. RandR
471 * has the assumption build in that there will either be a mode of that
472 * size present at all times, or that the first mode in the list will
473 * always be smaller than the initial virtual resolution. Since our
474 * approach to dynamic resizing isn't quite the way RandR was intended to
475 * be, and breaks the second assumption, we guarantee the first. */
476 DisplayModePtr pMode = vboxAddEmptyScreenMode(pScrn);
477 vboxFillDisplayMode(pScrn, pMode, "VBoxInitialMode", cxInit, cyInit);
478 /* Create our two dynamic modes. */
479 pMode = vboxAddEmptyScreenMode(pScrn);
480 vboxFillDisplayMode(pScrn, pMode, "VBoxDynamicMode", cxInit, cyInit);
481 pMode = vboxAddEmptyScreenMode(pScrn);
482 vboxFillDisplayMode(pScrn, pMode, "VBoxDynamicMode", cxInit, cyInit);
483 /* Add standard modes supported by the host */
484 for ( ; ; )
485 {
486 char szName[256];
487 cIndex = vboxNextStandardMode(pScrn, cIndex, &cx, &cy, NULL);
488 if (cIndex == 0)
489 break;
490 sprintf(szName, "VBox-%ux%u", cx, cy);
491 pMode = vboxAddEmptyScreenMode(pScrn);
492 vboxFillDisplayMode(pScrn, pMode, szName, cx, cy);
493 }
494 /* And finally any modes specified by the user. We assume here that
495 * the mode names reflect the mode sizes. */
496 for (i = 0; pScrn->display->modes && pScrn->display->modes[i]; i++)
497 {
498 if (sscanf(pScrn->display->modes[i], "%ux%u", &cx, &cy) == 2)
499 {
500 pMode = vboxAddEmptyScreenMode(pScrn);
501 vboxFillDisplayMode(pScrn, pMode, pScrn->display->modes[i], cx, cy);
502 }
503 }
504}
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