VirtualBox

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

Last change on this file since 43369 was 43270, checked in by vboxsync, 12 years ago

Additions/x11/vboxvideo: reduce header file dependencies from xorg-server.h

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