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