1/* $Id: getmode.c,v 1.2 2020/10/22 20:47:23 thorpej Exp $ */ 2/** @file 3 * VirtualBox X11 Additions graphics driver dynamic video mode functions. 4 */ 5 6/* 7 * Copyright (C) 2006-2017 Oracle Corporation 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a 10 * copy of this software and associated documentation files (the "Software"), 11 * to deal in the Software without restriction, including without limitation 12 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 * and/or sell copies of the Software, and to permit persons to whom the 14 * Software is furnished to do so, subject to the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included in 17 * all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 * USE OR OTHER DEALINGS IN THE SOFTWARE. 26 */ 27 28#include "vboxvideo_drv.h" 29 30#define NEED_XF86_TYPES 31#include "xf86.h" 32 33#ifdef XORG_7X 34# include <stdio.h> 35# include <stdlib.h> 36# include <string.h> 37#endif 38 39#ifdef VBOXVIDEO_13 40# ifdef RT_OS_LINUX 41# include <linux/input.h> 42# ifndef EVIOCGRAB 43# define EVIOCGRAB _IOW('E', 0x90, int) 44# endif 45# ifndef KEY_SWITCHVIDEOMODE 46# define KEY_SWITCHVIDEOMODE 227 47# endif 48# include <dirent.h> 49# include <errno.h> 50# include <fcntl.h> 51# include <unistd.h> 52# endif /* RT_OS_LINUX */ 53#endif /* VBOXVIDEO_13 */ 54 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/** 102 * Allocates an empty display mode and links it into the doubly linked list of 103 * modes pointed to by pScrn->modes. Returns a pointer to the newly allocated 104 * memory. 105 */ 106static DisplayModePtr vboxAddEmptyScreenMode(ScrnInfoPtr pScrn) 107{ 108 DisplayModePtr pMode = xnfcalloc(sizeof(DisplayModeRec), 1); 109 110 TRACE_ENTRY(); 111 if (!pScrn->modes) 112 { 113 pScrn->modes = pMode; 114 pMode->next = pMode; 115 pMode->prev = pMode; 116 } 117 else 118 { 119 pMode->next = pScrn->modes; 120 pMode->prev = pScrn->modes->prev; 121 pMode->next->prev = pMode; 122 pMode->prev->next = pMode; 123 } 124 return pMode; 125} 126 127/** 128 * Create display mode entries in the screen information structure for each 129 * of the graphics modes that we wish to support, that is: 130 * - A dynamic mode in first place which will be updated by the RandR code. 131 * - Any modes that the user requested in xorg.conf/XFree86Config. 132 */ 133void vboxAddModes(ScrnInfoPtr pScrn) 134{ 135 unsigned cx = 0; 136 unsigned cy = 0; 137 unsigned i; 138 DisplayModePtr pMode; 139 140 /* Add two dynamic mode entries. When we receive a new size hint we will 141 * update whichever of these is not current. */ 142 pMode = vboxAddEmptyScreenMode(pScrn); 143 vboxFillDisplayMode(pScrn, pMode, NULL, 800, 600); 144 pMode = vboxAddEmptyScreenMode(pScrn); 145 vboxFillDisplayMode(pScrn, pMode, NULL, 800, 600); 146 /* Add any modes specified by the user. We assume here that the mode names 147 * reflect the mode sizes. */ 148 for (i = 0; pScrn->display->modes && pScrn->display->modes[i]; i++) 149 { 150 if (sscanf(pScrn->display->modes[i], "%ux%u", &cx, &cy) == 2) 151 { 152 pMode = vboxAddEmptyScreenMode(pScrn); 153 vboxFillDisplayMode(pScrn, pMode, pScrn->display->modes[i], cx, cy); 154 } 155 } 156} 157 158/** Set the initial values for the guest screen size hints to standard values 159 * in case nothing else is available. */ 160void VBoxInitialiseSizeHints(ScrnInfoPtr pScrn) 161{ 162 VBOXPtr pVBox = VBOXGetRec(pScrn); 163 unsigned i; 164 165 for (i = 0; i < pVBox->cScreens; ++i) 166 { 167 pVBox->pScreens[i].aPreferredSize.cx = 800; 168 pVBox->pScreens[i].aPreferredSize.cy = 600; 169 pVBox->pScreens[i].afConnected = true; 170 } 171 /* Set up the first mode correctly to match the requested initial mode. */ 172 pScrn->modes->HDisplay = pVBox->pScreens[0].aPreferredSize.cx; 173 pScrn->modes->VDisplay = pVBox->pScreens[0].aPreferredSize.cy; 174} 175 176static Bool useHardwareCursor(uint32_t fCursorCapabilities) 177{ 178 if (fCursorCapabilities & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE) 179 return true; 180 return false; 181} 182 183static void compareAndMaybeSetUseHardwareCursor(VBOXPtr pVBox, uint32_t fCursorCapabilities, Bool *pfChanged, Bool fSet) 184{ 185 if (pVBox->fUseHardwareCursor != useHardwareCursor(fCursorCapabilities)) 186 *pfChanged = true; 187 if (fSet) 188 pVBox->fUseHardwareCursor = useHardwareCursor(fCursorCapabilities); 189} 190 191#define COMPARE_AND_MAYBE_SET(pDest, src, pfChanged, fSet) \ 192do { \ 193 if (*(pDest) != (src)) \ 194 { \ 195 if (fSet) \ 196 *(pDest) = (src); \ 197 *(pfChanged) = true; \ 198 } \ 199} while(0) 200 201/** Read in information about the most recent size hints and cursor 202 * capabilities requested for the guest screens from HGSMI. */ 203void vbvxReadSizesAndCursorIntegrationFromHGSMI(ScrnInfoPtr pScrn, Bool *pfNeedUpdate) 204{ 205 VBOXPtr pVBox = VBOXGetRec(pScrn); 206 int rc; 207 unsigned i; 208 Bool fChanged = false; 209 uint32_t fCursorCapabilities; 210 211 if (!pVBox->fHaveHGSMIModeHints) 212 return; 213 rc = VBoxHGSMIGetModeHints(&pVBox->guestCtx, pVBox->cScreens, pVBox->paVBVAModeHints); 214 AssertMsg(rc == VINF_SUCCESS, ("VBoxHGSMIGetModeHints failed, rc=%d.\n", rc)); 215 for (i = 0; i < pVBox->cScreens; ++i) 216 if (pVBox->paVBVAModeHints[i].magic == VBVAMODEHINT_MAGIC) 217 { 218 COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredSize.cx, pVBox->paVBVAModeHints[i].cx & 0x8fff, &fChanged, true); 219 COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredSize.cy, pVBox->paVBVAModeHints[i].cy & 0x8fff, &fChanged, true); 220 COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].afConnected, RT_BOOL(pVBox->paVBVAModeHints[i].fEnabled), &fChanged, true); 221 COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredLocation.x, (int32_t)pVBox->paVBVAModeHints[i].dx & 0x8fff, &fChanged, 222 true); 223 COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredLocation.y, (int32_t)pVBox->paVBVAModeHints[i].dy & 0x8fff, &fChanged, 224 true); 225 if (pVBox->paVBVAModeHints[i].dx != ~(uint32_t)0 && pVBox->paVBVAModeHints[i].dy != ~(uint32_t)0) 226 COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].afHaveLocation, true, &fChanged, true); 227 else 228 COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].afHaveLocation, false, &fChanged, true); 229 } 230 rc = VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &fCursorCapabilities); 231 AssertMsg(rc == VINF_SUCCESS, ("Getting VBOX_VBVA_CONF32_CURSOR_CAPABILITIES failed, rc=%d.\n", rc)); 232 compareAndMaybeSetUseHardwareCursor(pVBox, fCursorCapabilities, &fChanged, true); 233 if (pfNeedUpdate != NULL && fChanged) 234 *pfNeedUpdate = true; 235} 236 237#undef COMPARE_AND_MAYBE_SET 238 239#ifdef VBOXVIDEO_13 240# ifdef RT_OS_LINUX 241/** We have this for two purposes: one is to ensure that the X server is woken 242 * up when we get a video ACPI event. Two is to grab ACPI video events to 243 * prevent gnome-settings-daemon from seeing them, as older versions ignored 244 * the time stamp and handled them at the wrong time. */ 245static void acpiEventHandler(int fd, void *pvData) 246{ 247 struct input_event event; 248 ssize_t rc; 249 RT_NOREF(pvData); 250 251 do 252 rc = read(fd, &event, sizeof(event)); 253 while (rc > 0 || (rc == -1 && errno == EINTR)); 254 /* Why do they return EAGAIN instead of zero bytes read like everyone else does? */ 255 AssertMsg(rc != -1 || errno == EAGAIN, ("Reading ACPI input event failed.\n")); 256} 257 258void vbvxSetUpLinuxACPI(ScreenPtr pScreen) 259{ 260 VBOXPtr pVBox = VBOXGetRec(xf86Screens[pScreen->myNum]); 261 struct dirent *pDirent; 262 DIR *pDir; 263 int fd = -1; 264 265 if (pVBox->fdACPIDevices != -1 || pVBox->hACPIEventHandler != NULL) 266 FatalError("ACPI input file descriptor not initialised correctly.\n"); 267 pDir = opendir("/dev/input"); 268 if (pDir == NULL) 269 return; 270 for (pDirent = readdir(pDir); pDirent != NULL; pDirent = readdir(pDir)) 271 { 272 if (strncmp(pDirent->d_name, "event", sizeof("event") - 1) == 0) 273 { 274#define BITS_PER_BLOCK (sizeof(unsigned long) * 8) 275 char szFile[64] = "/dev/input/"; 276 char szDevice[64] = ""; 277 unsigned long afKeys[KEY_MAX / BITS_PER_BLOCK]; 278 279 strncat(szFile, pDirent->d_name, sizeof(szFile) - sizeof("/dev/input/")); 280 if (fd != -1) 281 close(fd); 282 fd = open(szFile, O_RDONLY | O_NONBLOCK); 283 if ( fd == -1 284 || ioctl(fd, EVIOCGNAME(sizeof(szDevice)), szDevice) == -1 285 || strcmp(szDevice, "Video Bus") != 0) 286 continue; 287 if ( ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(afKeys)), afKeys) == -1 288 || (( afKeys[KEY_SWITCHVIDEOMODE / BITS_PER_BLOCK] 289 >> KEY_SWITCHVIDEOMODE % BITS_PER_BLOCK) & 1) == 0) 290 break; 291 if (ioctl(fd, EVIOCGRAB, (void *)1) != 0) 292 break; 293 pVBox->hACPIEventHandler 294 = xf86AddGeneralHandler(fd, acpiEventHandler, pScreen); 295 if (pVBox->hACPIEventHandler == NULL) 296 break; 297 pVBox->fdACPIDevices = fd; 298 fd = -1; 299 break; 300#undef BITS_PER_BLOCK 301 } 302 } 303 if (fd != -1) 304 close(fd); 305 closedir(pDir); 306} 307 308void vbvxCleanUpLinuxACPI(ScreenPtr pScreen) 309{ 310 VBOXPtr pVBox = VBOXGetRec(xf86Screens[pScreen->myNum]); 311 if (pVBox->fdACPIDevices != -1) 312 close(pVBox->fdACPIDevices); 313 pVBox->fdACPIDevices = -1; 314 xf86RemoveGeneralHandler(pVBox->hACPIEventHandler); 315 pVBox->hACPIEventHandler = NULL; 316} 317# endif /* RT_OS_LINUX */ 318#endif /* VBOXVIDEO_13 */ 319