VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/vboxvideo/getmode.c@ 53770

Last change on this file since 53770 was 53770, checked in by vboxsync, 10 years ago

Additions/x11/vboxvideo: better separate VBox-specific and X server-specific code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.7 KB
Line 
1/* $Id: getmode.c 53770 2015-01-12 09:10:36Z vboxsync $ */
2/** @file
3 * VirtualBox X11 Additions graphics driver dynamic video mode functions.
4 */
5
6/*
7 * Copyright (C) 2006-2014 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 "vboxvideo.h"
19
20#define NEED_XF86_TYPES
21#include <iprt/string.h>
22
23#include "xf86.h"
24#include "dixstruct.h"
25#ifdef VBOX_GUESTR3XF86MOD
26# define EXTENSION_PROC_ARGS char *name, GCPtr pGC
27#endif
28#include "extnsionst.h"
29#include "windowstr.h"
30#include <X11/extensions/randrproto.h>
31#include <X11/Xatom.h>
32
33#ifdef XORG_7X
34# include <stdio.h>
35# include <stdlib.h>
36#endif
37
38#ifdef VBOXVIDEO_13
39# ifdef RT_OS_LINUX
40# include "randrstr.h"
41# include "xf86_OSproc.h"
42# include <linux/input.h>
43# ifndef EVIOCGRAB
44# define EVIOCGRAB _IOW('E', 0x90, int)
45# endif
46# ifndef KEY_SWITCHVIDEOMODE
47# define KEY_SWITCHVIDEOMODE 227
48# endif
49# include <dirent.h>
50# include <errno.h>
51# include <fcntl.h>
52# include <unistd.h>
53# endif /* RT_OS_LINUX */
54#endif /* VBOXVIDEO_13 */
55/**************************************************************************
56* Main functions *
57**************************************************************************/
58
59/**
60 * Fills a display mode M with a built-in mode of name pszName and dimensions
61 * cx and cy.
62 */
63static void vboxFillDisplayMode(ScrnInfoPtr pScrn, DisplayModePtr m,
64 const char *pszName, unsigned cx, unsigned cy)
65{
66 VBOXPtr pVBox = pScrn->driverPrivate;
67 char szName[256];
68 DisplayModePtr pPrev = m->prev;
69 DisplayModePtr pNext = m->next;
70
71 if (!pszName)
72 {
73 sprintf(szName, "%ux%u", cx, cy);
74 pszName = szName;
75 }
76 TRACE_LOG("pszName=%s, cx=%u, cy=%u\n", pszName, cx, cy);
77 if (m->name)
78 free((void*)m->name);
79 memset(m, '\0', sizeof(*m));
80 m->prev = pPrev;
81 m->next = pNext;
82 m->status = MODE_OK;
83 m->type = M_T_BUILTIN;
84 /* Older versions of VBox only support screen widths which are a multiple
85 * of 8 */
86 if (pVBox->fAnyX)
87 m->HDisplay = cx;
88 else
89 m->HDisplay = cx & ~7;
90 m->HSyncStart = m->HDisplay + 2;
91 m->HSyncEnd = m->HDisplay + 4;
92 m->HTotal = m->HDisplay + 6;
93 m->VDisplay = cy;
94 m->VSyncStart = m->VDisplay + 2;
95 m->VSyncEnd = m->VDisplay + 4;
96 m->VTotal = m->VDisplay + 6;
97 m->Clock = m->HTotal * m->VTotal * 60 / 1000; /* kHz */
98 m->name = xnfstrdup(pszName);
99}
100
101/** vboxvideo's list of standard video modes */
102struct
103{
104 /** mode width */
105 uint32_t cx;
106 /** mode height */
107 uint32_t cy;
108} vboxStandardModes[] =
109{
110 { 1600, 1200 },
111 { 1440, 1050 },
112 { 1280, 960 },
113 { 1024, 768 },
114 { 800, 600 },
115 { 640, 480 },
116 { 0, 0 }
117};
118enum
119{
120 vboxNumStdModes = sizeof(vboxStandardModes) / sizeof(vboxStandardModes[0])
121};
122
123/**
124 * Returns a standard mode which the host likes. Can be called multiple
125 * times with the index returned by the previous call to get a list of modes.
126 * @returns the index of the mode in the list, or 0 if no more modes are
127 * available
128 * @param pScrn the screen information structure
129 * @param pScrn->bitsPerPixel
130 * if this is non-null, only modes with this BPP will be
131 * returned
132 * @param cIndex the index of the last mode queried, or 0 to query the
133 * first mode available. Note: the first index is 1
134 * @param pcx where to store the mode's width
135 * @param pcy where to store the mode's height
136 * @param pcBits where to store the mode's BPP
137 */
138unsigned vboxNextStandardMode(ScrnInfoPtr pScrn, unsigned cIndex,
139 uint32_t *pcx, uint32_t *pcy)
140{
141 unsigned i;
142
143 VBVXASSERT(cIndex < vboxNumStdModes,
144 ("cIndex = %d, vboxNumStdModes = %d\n", cIndex,
145 vboxNumStdModes));
146 for (i = cIndex; i < vboxNumStdModes - 1; ++i)
147 {
148 uint32_t cx = vboxStandardModes[i].cx;
149 uint32_t cy = vboxStandardModes[i].cy;
150
151 if (pcx)
152 *pcx = cx;
153 if (pcy)
154 *pcy = cy;
155 return i + 1;
156 }
157 return 0;
158}
159
160/**
161 * Allocates an empty display mode and links it into the doubly linked list of
162 * modes pointed to by pScrn->modes. Returns a pointer to the newly allocated
163 * memory.
164 */
165static DisplayModePtr vboxAddEmptyScreenMode(ScrnInfoPtr pScrn)
166{
167 DisplayModePtr pMode = xnfcalloc(sizeof(DisplayModeRec), 1);
168
169 TRACE_ENTRY();
170 if (!pScrn->modes)
171 {
172 pScrn->modes = pMode;
173 pMode->next = pMode;
174 pMode->prev = pMode;
175 }
176 else
177 {
178 pMode->next = pScrn->modes;
179 pMode->prev = pScrn->modes->prev;
180 pMode->next->prev = pMode;
181 pMode->prev->next = pMode;
182 }
183 return pMode;
184}
185
186/**
187 * Create display mode entries in the screen information structure for each
188 * of the graphics modes that we wish to support, that is:
189 * - A dynamic mode in first place which will be updated by the RandR code.
190 * - Several standard modes.
191 * - Any modes that the user requested in xorg.conf/XFree86Config.
192 */
193void vboxAddModes(ScrnInfoPtr pScrn)
194{
195 unsigned cx = 0, cy = 0, cIndex = 0;
196 unsigned i;
197 DisplayModePtr pMode;
198
199 /* Add two dynamic mode entries. When we receive a new size hint we will
200 * update whichever of these is not current. */
201 pMode = vboxAddEmptyScreenMode(pScrn);
202 vboxFillDisplayMode(pScrn, pMode, NULL, 1024, 768);
203 pMode = vboxAddEmptyScreenMode(pScrn);
204 vboxFillDisplayMode(pScrn, pMode, NULL, 1024, 768);
205 /* Add standard modes supported by the host */
206 for ( ; ; )
207 {
208 cIndex = vboxNextStandardMode(pScrn, cIndex, &cx, &cy);
209 if (cIndex == 0)
210 break;
211 pMode = vboxAddEmptyScreenMode(pScrn);
212 vboxFillDisplayMode(pScrn, pMode, NULL, cx, cy);
213 }
214 /* And finally any modes specified by the user. We assume here that
215 * the mode names reflect the mode sizes. */
216 for (i = 0; pScrn->display->modes && pScrn->display->modes[i]; i++)
217 {
218 if (sscanf(pScrn->display->modes[i], "%ux%u", &cx, &cy) == 2)
219 {
220 pMode = vboxAddEmptyScreenMode(pScrn);
221 vboxFillDisplayMode(pScrn, pMode, pScrn->display->modes[i], cx, cy);
222 }
223 }
224}
225
226/** Set the initial values for the guest screen size hints by reading saved
227 * values from files. */
228/** @todo Actually read the files instead of setting dummies. */
229void VBoxInitialiseSizeHints(ScrnInfoPtr pScrn)
230{
231 VBOXPtr pVBox = VBOXGetRec(pScrn);
232 DisplayModePtr pMode;
233 unsigned i;
234
235 for (i = 0; i < pVBox->cScreens; ++i)
236 {
237 pVBox->pScreens[i].aPreferredSize.cx = 1024;
238 pVBox->pScreens[i].aPreferredSize.cy = 768;
239 pVBox->pScreens[i].afConnected = true;
240 }
241 /* Set up the first mode correctly to match the requested initial mode. */
242 pScrn->modes->HDisplay = pVBox->pScreens[0].aPreferredSize.cx;
243 pScrn->modes->VDisplay = pVBox->pScreens[0].aPreferredSize.cy;
244 /* RandR 1.1 quirk: make sure that the initial resolution is always present
245 * in the mode list as RandR will always advertise a mode of the initial
246 * virtual resolution via GetScreenInfo. */
247 pMode = vboxAddEmptyScreenMode(pScrn);
248 vboxFillDisplayMode(pScrn, pMode, NULL, pVBox->pScreens[0].aPreferredSize.cx,
249 pVBox->pScreens[0].aPreferredSize.cy);
250}
251
252# define SIZE_HINTS_PROPERTY "VBOX_SIZE_HINTS"
253
254/** Read in information about the most recent size hints requested for the
255 * guest screens. A client application sets the hint information as a root
256 * window property. */
257void VBoxUpdateSizeHints(ScrnInfoPtr pScrn)
258{
259 VBOXPtr pVBox = VBOXGetRec(pScrn);
260 Atom atom = MakeAtom(SIZE_HINTS_PROPERTY, sizeof(SIZE_HINTS_PROPERTY) - 1,
261 FALSE);
262 PropertyPtr prop = NULL;
263 unsigned i;
264
265#ifdef VBOXVIDEO_13
266 if (RT_SUCCESS(VBoxHGSMIGetModeHints(&pVBox->guestCtx, pVBox->cScreens,
267 pVBox->paVBVAModeHints)))
268 {
269 for (i = 0; i < pVBox->cScreens; ++i)
270 {
271 if (pVBox->paVBVAModeHints[i].magic == VBVAMODEHINT_MAGIC)
272 {
273 pVBox->pScreens[i].aPreferredSize.cx =
274 pVBox->paVBVAModeHints[i].cx;
275 pVBox->pScreens[i].aPreferredSize.cy =
276 pVBox->paVBVAModeHints[i].cy;
277 pVBox->pScreens[i].afConnected =
278 pVBox->paVBVAModeHints[i].fEnabled;
279 }
280 }
281 return;
282 }
283#endif
284 /* We can get called early, before the root window is created. */
285 if (!ROOT_WINDOW(pScrn))
286 return;
287 if (atom != BAD_RESOURCE)
288 {
289 for (prop = wUserProps(ROOT_WINDOW(pScrn));
290 prop != NULL && prop->propertyName != atom; prop = prop->next);
291 }
292 if (prop && prop->type == XA_INTEGER && prop->format == 32)
293 for (i = 0; i < prop->size && i < pVBox->cScreens; ++i)
294 {
295 if (((int32_t *)prop->data)[i] != 0)
296 {
297 pVBox->pScreens[i].aPreferredSize.cx =
298 ((int32_t *)prop->data)[i] >> 16;
299 pVBox->pScreens[i].aPreferredSize.cy =
300 ((int32_t *)prop->data)[i] & 0x8fff;
301 }
302 }
303}
304
305#ifndef VBOXVIDEO_13
306
307/** The RandR "proc" vector, which we wrap with our own in order to notice
308 * when a client sends a GetScreenInfo request. */
309static int (*g_pfnVBoxRandRProc)(ClientPtr) = NULL;
310/** The swapped RandR "proc" vector. */
311static int (*g_pfnVBoxRandRSwappedProc)(ClientPtr) = NULL;
312
313static void vboxRandRDispatchCore(ClientPtr pClient)
314{
315 xRRGetScreenInfoReq *pReq = (xRRGetScreenInfoReq *)pClient->requestBuffer;
316 WindowPtr pWin;
317 ScrnInfoPtr pScrn;
318 VBOXPtr pVBox;
319 DisplayModePtr pMode;
320
321 if (pClient->req_len != sizeof(xRRGetScreenInfoReq) >> 2)
322 return;
323 pWin = (WindowPtr)SecurityLookupWindow(pReq->window, pClient,
324 SecurityReadAccess);
325 if (!pWin)
326 return;
327 pScrn = xf86Screens[pWin->drawable.pScreen->myNum];
328 pVBox = VBOXGetRec(pScrn);
329 VBoxUpdateSizeHints(pScrn);
330 pMode = pScrn->modes;
331 if (pScrn->currentMode == pMode)
332 pMode = pMode->next;
333 pMode->HDisplay = pVBox->pScreens[0].aPreferredSize.cx;
334 pMode->VDisplay = pVBox->pScreens[0].aPreferredSize.cy;
335}
336
337static int vboxRandRDispatch(ClientPtr pClient)
338{
339 xReq *pReq = (xReq *)pClient->requestBuffer;
340
341 if (pReq->data == X_RRGetScreenInfo)
342 vboxRandRDispatchCore(pClient);
343 return g_pfnVBoxRandRProc(pClient);
344}
345
346static int vboxRandRSwappedDispatch(ClientPtr pClient)
347{
348 xReq *pReq = (xReq *)pClient->requestBuffer;
349
350 if (pReq->data == X_RRGetScreenInfo)
351 vboxRandRDispatchCore(pClient);
352 return g_pfnVBoxRandRSwappedProc(pClient);
353}
354
355static Bool vboxRandRCreateScreenResources(ScreenPtr pScreen)
356{
357 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
358 VBOXPtr pVBox = VBOXGetRec(pScrn);
359 ExtensionEntry *pExt;
360
361 pScreen->CreateScreenResources = pVBox->pfnCreateScreenResources;
362 if (!pScreen->CreateScreenResources(pScreen))
363 return FALSE;
364 /* I doubt we can be loaded twice - should I fail here? */
365 if (g_pfnVBoxRandRProc)
366 return TRUE;
367 pExt = CheckExtension(RANDR_NAME);
368 if (!pExt)
369 {
370 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
371 "RandR extension not found, disabling dynamic resizing.\n");
372 return TRUE;
373 }
374 if ( !ProcVector[pExt->base]
375#if !defined(XF86_VERSION_CURRENT) \
376 || XF86_VERSION_CURRENT >= XF86_VERSION_NUMERIC(4, 3, 99, 0, 0)
377 /* SwappedProcVector is not exported in XFree86, so we will not support
378 * swapped byte order clients. I doubt this is a big issue. */
379 || !SwappedProcVector[pExt->base]
380#endif
381 )
382 FatalError("RandR \"proc\" vector not initialised\n");
383 g_pfnVBoxRandRProc = ProcVector[pExt->base];
384 ProcVector[pExt->base] = vboxRandRDispatch;
385#if !defined(XF86_VERSION_CURRENT) \
386 || XF86_VERSION_CURRENT >= XF86_VERSION_NUMERIC(4, 3, 99, 0, 0)
387 g_pfnVBoxRandRSwappedProc = SwappedProcVector[pExt->base];
388 SwappedProcVector[pExt->base] = vboxRandRSwappedDispatch;
389#endif
390 return TRUE;
391}
392
393/** Install our private RandR hook procedure, so that we can detect
394 * GetScreenInfo requests from clients to update our dynamic mode. This works
395 * by installing a wrapper around CreateScreenResources(), which will be called
396 * after RandR is initialised. The wrapper then in turn wraps the RandR "proc"
397 * vectors with its own handlers which will get called on any client RandR
398 * request. This should not be used in conjunction with RandR 1.2 or later.
399 * A couple of points of interest in our RandR 1.1 support:
400 * * We use the first two screen modes as dynamic modes. When a new mode hint
401 * arrives we update the first of the two which is not the current mode with
402 * the new size.
403 * * RandR 1.1 always advertises a mode of the size of the initial virtual
404 * resolution via GetScreenInfo(), so we make sure that a mode of that size
405 * is always present in the list.
406 * * RandR adds each new mode it sees to an internal array, but never removes
407 * entries. This array might end up getting rather long given that we can
408 * report a lot more modes than physical hardware.
409 */
410void VBoxSetUpRandR11(ScreenPtr pScreen)
411{
412 VBOXPtr pVBox = VBOXGetRec(xf86Screens[pScreen->myNum]);
413
414 if (!pScreen->CreateScreenResources)
415 FatalError("called to early: CreateScreenResources not yet initialised\n");
416 pVBox->pfnCreateScreenResources = pScreen->CreateScreenResources;
417 pScreen->CreateScreenResources = vboxRandRCreateScreenResources;
418}
419
420#endif /* !VBOXVIDEO_13 */
421
422#ifdef VBOXVIDEO_13
423# ifdef RT_OS_LINUX
424static void acpiEventHandler(int fd, void *pvData)
425{
426 struct input_event event;
427 ssize_t rc;
428
429 RRGetInfo((ScreenPtr)pvData
430# if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) >= 5
431 , TRUE
432# endif
433 );
434 do
435 rc = read(fd, &event, sizeof(event));
436 while (rc > 0 || (rc == -1 && errno == EINTR));
437 if (rc == -1 && errno != EAGAIN) /* Why not just return 0? */
438 FatalError("Reading ACPI input event failed.\n");
439}
440
441void VBoxSetUpLinuxACPI(ScreenPtr pScreen)
442{
443 VBOXPtr pVBox = VBOXGetRec(xf86Screens[pScreen->myNum]);
444 struct dirent *pDirent;
445 DIR *pDir;
446 int fd = -1;
447
448 if (pVBox->fdACPIDevices != -1 || pVBox->hACPIEventHandler != NULL)
449 FatalError("ACPI input file descriptor not initialised correctly.\n");
450 pDir = opendir("/dev/input");
451 if (pDir == NULL)
452 return;
453 for (pDirent = readdir(pDir); pDirent != NULL; pDirent = readdir(pDir))
454 {
455 if (strncmp(pDirent->d_name, "event", sizeof("event") - 1) == 0)
456 {
457#define BITS_PER_BLOCK (sizeof(unsigned long) * 8)
458 char pszFile[64] = "/dev/input/";
459 char pszDevice[64] = "";
460 unsigned long afKeys[KEY_MAX / BITS_PER_BLOCK];
461
462 strncat(pszFile, pDirent->d_name, sizeof(pszFile));
463 if (fd != -1)
464 close(fd);
465 fd = open(pszFile, O_RDONLY | O_NONBLOCK);
466 if ( fd == -1
467 || ioctl(fd, EVIOCGNAME(sizeof(pszDevice)), pszDevice) == -1
468 || strcmp(pszDevice, "Video Bus") != 0)
469 continue;
470 if ( ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), afKeys) == -1
471 || (( afKeys[KEY_SWITCHVIDEOMODE / BITS_PER_BLOCK]
472 >> KEY_SWITCHVIDEOMODE % BITS_PER_BLOCK) & 1) == 0)
473 break;
474 if (ioctl(fd, EVIOCGRAB, (void *)1) != 0)
475 break;
476 pVBox->hACPIEventHandler
477 = xf86AddGeneralHandler(fd, acpiEventHandler, pScreen);
478 if (pVBox->hACPIEventHandler == NULL)
479 break;
480 /* We ignore the return value as the fall-back should be active
481 * anyway. */
482 VBoxHGSMISendCapsInfo(&pVBox->guestCtx, VBVACAPS_VIDEO_MODE_HINTS);
483 pVBox->fdACPIDevices = fd;
484 fd = -1;
485 break;
486#undef BITS_PER_BLOCK
487 }
488 }
489 if (fd != -1)
490 close(fd);
491 closedir(pDir);
492}
493
494void VBoxCleanUpLinuxACPI(ScreenPtr pScreen)
495{
496 VBOXPtr pVBox = VBOXGetRec(xf86Screens[pScreen->myNum]);
497 if (pVBox->fdACPIDevices != -1)
498 close(pVBox->fdACPIDevices);
499 pVBox->fdACPIDevices = -1;
500 xf86RemoveGeneralHandler(pVBox->hACPIEventHandler);
501 pVBox->hACPIEventHandler = NULL;
502}
503# endif /* RT_OS_LINUX */
504#endif /* VBOXVIDEO_13 */
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