Changeset 60083 in vbox for trunk/src/VBox/Additions/x11/VBoxClient
- Timestamp:
- Mar 17, 2016 6:40:28 PM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Additions/x11/VBoxClient/display.cpp
r59943 r60083 16 16 */ 17 17 18 #include "VBoxClient.h" 19 20 #include <iprt/err.h> 21 #include <iprt/file.h> 22 #include <iprt/mem.h> 23 #include <iprt/string.h> 24 25 #include <X11/Xlib.h> 26 #include <X11/Xatom.h> 27 #include <X11/extensions/Xrandr.h> 28 18 29 /** @todo this should probably be replaced by something IPRT */ 19 30 /* For system() and WEXITSTATUS() */ … … 22 33 #include <sys/wait.h> 23 34 #include <errno.h> 24 25 #include <X11/Xlib.h> 26 #include <X11/Xatom.h> 27 28 #include <iprt/asm.h> 29 #include <iprt/assert.h> 30 #include <iprt/err.h> 31 #include <iprt/file.h> 32 #include <iprt/mem.h> 33 #include <iprt/string.h> 34 #include <iprt/thread.h> 35 #include <VBox/log.h> 36 #include <VBox/VMMDev.h> 37 #include <VBox/VBoxGuestLib.h> 38 39 #include "VBoxClient.h" 35 #include <limits.h> 36 #include <poll.h> 37 #include <time.h> 40 38 41 39 /* TESTING: Dynamic resizing and mouse integration toggling should work … … 44 42 * virtual terminal while a user session is in place should disable dynamic 45 43 * resizing and cursor integration, switching back should re-enable them. */ 46 47 /** Most recent information received for a particular screen. */48 struct screenInformation49 {50 unsigned cx;51 unsigned cy;52 unsigned cBPP;53 unsigned x;54 unsigned y;55 bool fEnabled;56 bool fUpdateSize;57 volatile bool fUpdatePosition;58 };59 44 60 45 /** Display magic number, start of a UUID. */ … … 73 58 /** The connection to the server. */ 74 59 Display *pDisplay; 60 /** The RandR extension base event number. */ 61 int cRREventBase; 75 62 /** Can we use version 1.2 or later of the RandR protocol here? */ 76 63 bool fHaveRandR12; … … 79 66 * would it make sense to use absolute paths on all systems? */ 80 67 const char *pcszXrandr; 81 /** The number of screens we are currently aware of. */ 82 unsigned cScreensTracked; 83 /** Array of information about different screens. */ 84 struct screenInformation *paScreenInformation; 68 /** Was there a recent mode hint with no following root window resize, and 69 * if so, have we waited for a reasonable time? */ 70 time_t timeLastModeHint; 85 71 }; 86 72 87 /** Thread to monitor and react to X server VT switches and exits. */ 88 static DECLCALLBACK(int) vboxClientMonitorThread(RTTHREAD self, void *pvUser) 89 { 90 struct DISPLAYSTATE *pState = (struct DISPLAYSTATE *)pvUser; 91 Display *pDisplay; 92 bool fHasVT = true; 93 94 pDisplay = XOpenDisplay(NULL); 95 if (!pDisplay) 96 VBClFatalError(("Failed to open the X11 display\n")); 97 XSelectInput(pDisplay, DefaultRootWindow(pDisplay), PropertyChangeMask); 73 static unsigned char *getRootProperty(struct DISPLAYSTATE *pState, const char *pszName, 74 long cItems, Atom type) 75 { 76 Atom actualType = None; 77 int iFormat = 0; 78 ulong cReturned = 0; 79 ulong cAfter = 0; 80 unsigned char *pData = 0; 81 82 if (XGetWindowProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), 83 XInternAtom(pState->pDisplay, pszName, 0), 0, cItems, 84 False /* delete */, type, &actualType, &iFormat, 85 &cReturned, &cAfter, &pData)) 86 return NULL; 87 return pData; 88 } 89 90 static void doResize(struct DISPLAYSTATE *pState) 91 { 92 /** @note The xrandr command can fail if something else accesses RandR at 93 * the same time. We just ignore failure for now as we do not know what 94 * someone else is doing. */ 95 if (!pState->fHaveRandR12) 96 { 97 char szCommand[256]; 98 unsigned char *pData; 99 100 pData = getRootProperty(pState, "VBOXVIDEO_PREFERRED_MODE", 1, XA_INTEGER); 101 if (pData != NULL) 102 { 103 RTStrPrintf(szCommand, sizeof(szCommand), "%s -s %ux%u", 104 pState->pcszXrandr, ((unsigned long *)pData)[0] >> 16, ((unsigned long *)pData)[0] & 0xFFFF); 105 system(szCommand); 106 XFree(pData); 107 } 108 } 109 else 110 { 111 const char szCommandBase[] = 112 "%s --output VGA-0 --auto --output VGA-1 --auto --right-of VGA-0 " 113 "--output VGA-2 --auto --right-of VGA-1 --output VGA-3 --auto --right-of VGA-2 " 114 "--output VGA-4 --auto --right-of VGA-3 --output VGA-5 --auto --right-of VGA-4 " 115 "--output VGA-6 --auto --right-of VGA-5 --output VGA-7 --auto --right-of VGA-6 " 116 "--output VGA-8 --auto --right-of VGA-7 --output VGA-9 --auto --right-of VGA-8 " 117 "--output VGA-10 --auto --right-of VGA-9 --output VGA-11 --auto --right-of VGA-10 " 118 "--output VGA-12 --auto --right-of VGA-11 --output VGA-13 --auto --right-of VGA-12 " 119 "--output VGA-14 --auto --right-of VGA-13 --output VGA-15 --auto --right-of VGA-14 " 120 "--output VGA-16 --auto --right-of VGA-15 --output VGA-17 --auto --right-of VGA-16 " 121 "--output VGA-18 --auto --right-of VGA-17 --output VGA-19 --auto --right-of VGA-18 " 122 "--output VGA-20 --auto --right-of VGA-19 --output VGA-21 --auto --right-of VGA-20 " 123 "--output VGA-22 --auto --right-of VGA-21 --output VGA-23 --auto --right-of VGA-22 " 124 "--output VGA-24 --auto --right-of VGA-23 --output VGA-25 --auto --right-of VGA-24 " 125 "--output VGA-26 --auto --right-of VGA-25 --output VGA-27 --auto --right-of VGA-26 " 126 "--output VGA-28 --auto --right-of VGA-27 --output VGA-29 --auto --right-of VGA-28 " 127 "--output VGA-30 --auto --right-of VGA-29 --output VGA-31 --auto --right-of VGA-30"; 128 char szCommand[sizeof(szCommandBase) + 256]; 129 RTStrPrintf(szCommand, sizeof(szCommand), szCommandBase, pState->pcszXrandr); 130 system(szCommand); 131 } 132 } 133 134 /** Main loop: handle display hot-plug events, property updates (which can 135 * signal VT switches hot-plug in old X servers). */ 136 static void runDisplay(struct DISPLAYSTATE *pState) 137 { 138 Display *pDisplay = pState->pDisplay; 139 long cValue = 1; 140 141 /* One way or another we want the preferred mode at server start-up. */ 142 doResize(pState); 143 XSelectInput(pDisplay, DefaultRootWindow(pDisplay), PropertyChangeMask | StructureNotifyMask); 144 if (pState->fHaveRandR12) 145 XRRSelectInput(pDisplay, DefaultRootWindow(pDisplay), RRScreenChangeNotifyMask); 146 /* Semantics: when VBOXCLIENT_STARTED is set, pre-1.3 X.Org Server driver 147 * assumes that a client capable of handling mode hints will be present for the 148 * rest of the X session. If we crash things will not work as they should. 149 * I thought that preferable to implementing complex crash-handling logic. 150 */ 151 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOXCLIENT_STARTED", 0), 152 XA_INTEGER, 32, PropModeReplace, (unsigned char *)&cValue, 1); 153 /* Interrupting this cleanly will be more work than making it robust 154 * against spontaneous termination, especially as it will never get 155 * properly tested, so I will go for the second. */ 98 156 while (true) 99 157 { 100 158 XEvent event; 101 102 XNextEvent(pDisplay, &event); 103 /* This property is deleted when the server regains the virtual 104 * terminal. Force the main thread to call xrandr again, as old X 105 * servers could not handle it while switched out. */ 106 if (pState->fHaveRandR12) 107 continue; 108 if ( event.type != PropertyNotify 109 || event.xproperty.state != PropertyDelete 110 || event.xproperty.window != DefaultRootWindow(pDisplay) 111 || event.xproperty.atom != XInternAtom(pDisplay, "VBOXVIDEO_NO_VT", False)) 112 continue; 113 LogRel(("VBoxClient/Display: entered virtual terminal.\n")); 114 ASMAtomicWriteBool(&pState->paScreenInformation[0].fUpdateSize, true); 115 VbglR3InterruptEventWaits(); 159 struct pollfd PollFd; 160 int pollTimeOut = -1; 161 int cFds; 162 163 /* Do not handle overflow. */ 164 if (pState->timeLastModeHint > 0 && pState->timeLastModeHint < INT_MAX - 2) 165 pollTimeOut = 2 - (time(0) - pState->timeLastModeHint); 166 PollFd.fd = ConnectionNumber(pDisplay); 167 PollFd.events = POLLIN; /* Hang-up is always reported. */ 168 XFlush(pDisplay); 169 cFds = poll(&PollFd, 1, pollTimeOut >= 0 ? pollTimeOut * 1000 : -1); 170 while (XPending(pDisplay)) 171 { 172 XNextEvent(pDisplay, &event); 173 /* This property is deleted when the server regains the virtual 174 * terminal. Force the main thread to call xrandr again, as old X 175 * servers could not handle it while switched out. */ 176 if ( !pState->fHaveRandR12 177 && event.type == PropertyNotify 178 && event.xproperty.state == PropertyDelete 179 && event.xproperty.window == DefaultRootWindow(pDisplay) 180 && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_NO_VT", False)) 181 doResize(pState); 182 if ( !pState->fHaveRandR12 183 && event.type == PropertyNotify 184 && event.xproperty.state == PropertyNewValue 185 && event.xproperty.window == DefaultRootWindow(pDisplay) 186 && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_PREFERRED_MODE", False)) 187 doResize(pState); 188 if ( pState->fHaveRandR12 189 && event.type == pState->cRREventBase + RRScreenChangeNotify) 190 pState->timeLastModeHint = time(0); 191 if ( event.type == ConfigureNotify 192 && event.xproperty.window == DefaultRootWindow(pDisplay)) 193 pState->timeLastModeHint = 0; 194 } 195 if (cFds == 0 && pState->timeLastModeHint > 0) 196 doResize(pState); 116 197 } 117 return VINF_SUCCESS; /* Should never be reached. */118 }119 120 static int startMonitorThread(struct DISPLAYSTATE *pState)121 {122 int rc;123 124 rc = RTThreadCreate(NULL, vboxClientMonitorThread, (void *)pState, 0, RTTHREADTYPE_INFREQUENT_POLLER, 0, "VT_MONITOR");125 if (rc != VINF_SUCCESS)126 VBClFatalError(("Failed to start the VT monitor thread, rc=%Rrc\n", rc));127 return VINF_SUCCESS;128 198 } 129 199 … … 133 203 int status; 134 204 135 /* Initialise the guest library. */136 int rc = VbglR3InitUser();137 if (RT_FAILURE(rc))138 VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));139 205 pState->pDisplay = XOpenDisplay(NULL); 140 206 if (!pState->pDisplay) 207 return VERR_NOT_FOUND; 208 if (!XRRQueryExtension(pState->pDisplay, &pState->cRREventBase, &status)) 141 209 return VERR_NOT_FOUND; 142 210 pState->fHaveRandR12 = false; … … 151 219 if (WEXITSTATUS(status) == 0) 152 220 pState->fHaveRandR12 = true; 153 pState->cScreensTracked = 0;154 pState->paScreenInformation = NULL;155 221 return VINF_SUCCESS; 156 }157 158 static void updateScreenInformation(struct DISPLAYSTATE *pState, unsigned cx, unsigned cy, unsigned cBPP, unsigned iDisplay,159 unsigned x, unsigned y, bool fEnabled, bool fUpdatePosition)160 {161 uint32_t i;162 163 if (iDisplay >= pState->cScreensTracked)164 {165 pState->paScreenInformation =166 (struct screenInformation *)RTMemRealloc(pState->paScreenInformation,167 (iDisplay + 1) * sizeof(*pState->paScreenInformation));168 if (!pState->paScreenInformation)169 VBClFatalError(("Failed to re-allocate screen information.\n"));170 for (i = pState->cScreensTracked; i < iDisplay + 1; ++i)171 RT_ZERO(pState->paScreenInformation[i]);172 pState->cScreensTracked = iDisplay + 1;173 }174 pState->paScreenInformation[iDisplay].cx = cx;175 pState->paScreenInformation[iDisplay].cy = cy;176 pState->paScreenInformation[iDisplay].cBPP = cBPP;177 pState->paScreenInformation[iDisplay].x = x;178 pState->paScreenInformation[iDisplay].y = y;179 pState->paScreenInformation[iDisplay].fEnabled = fEnabled;180 pState->paScreenInformation[iDisplay].fUpdateSize = true;181 pState->paScreenInformation[iDisplay].fUpdatePosition = fUpdatePosition;182 }183 184 static void updateSizeHintsProperty(struct DISPLAYSTATE *pState)185 {186 long *paSizeHints = (long *)RTMemTmpAllocZ(pState->cScreensTracked * sizeof(long) * 2);187 unsigned i;188 189 if (paSizeHints == NULL)190 VBClFatalError(("Failed to allocate size hint property memory.\n"));191 for (i = 0; i < pState->cScreensTracked; ++i)192 {193 if (pState->paScreenInformation[i].fEnabled)194 paSizeHints[2 * i] = (pState->paScreenInformation[i].cx & 0x8fff) << 16195 | (pState->paScreenInformation[i].cy & 0x8fff);196 else if (pState->paScreenInformation[i].cx != 0 && pState->paScreenInformation[i].cy != 0)197 paSizeHints[2 * i] = -1;198 if ( pState->paScreenInformation[i].fEnabled199 && pState->paScreenInformation[i].fUpdatePosition)200 paSizeHints[2 * i + 1] = (pState->paScreenInformation[i].x & 0x8fff) << 16201 | (pState->paScreenInformation[i].y & 0x8fff);202 else203 paSizeHints[2 * i + 1] = -1;204 }205 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOX_SIZE_HINTS", 0),206 XA_INTEGER, 32, PropModeReplace, (unsigned char *)paSizeHints, pState->cScreensTracked * 2);207 XFlush(pState->pDisplay);208 RTMemTmpFree(paSizeHints);209 }210 211 static void notifyXServerRandR11(struct DISPLAYSTATE *pState)212 {213 char szCommand[256];214 215 /** @note The xrandr command can fail if something else accesses RandR at216 * the same time. We just ignore failure for now and let the user try217 * again as we do not know what someone else is doing. */218 if ( pState->paScreenInformation[0].fUpdateSize219 && pState->paScreenInformation[0].cx > 0 && pState->paScreenInformation[0].cy > 0)220 {221 int ret;222 223 RTStrPrintf(szCommand, sizeof(szCommand), "%s -s %ux%u",224 pState->pcszXrandr, pState->paScreenInformation[0].cx, pState->paScreenInformation[0].cy);225 ret = system(szCommand);226 LogRel(("VBoxClient/Display: executed \"%s\", returned %d.\n", szCommand, ret));227 pState->paScreenInformation[0].fUpdateSize = false;228 }229 }230 231 static void updateMouseCapabilities(struct DISPLAYSTATE *pState)232 {233 uint32_t fFeatures = 0;234 int rc;235 236 rc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);237 238 if (rc != VINF_SUCCESS)239 VBClFatalError(("Failed to get mouse status, rc=%Rrc\n", rc));240 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay),241 XInternAtom(pState->pDisplay, "VBOX_MOUSE_CAPABILITIES", 0), XA_INTEGER, 32, PropModeReplace,242 (unsigned char *)&fFeatures, 1);243 XFlush(pState->pDisplay);244 }245 246 /**247 * Display change request monitor thread function.248 */249 static void runDisplay(struct DISPLAYSTATE *pState)250 {251 int rc;252 unsigned i, cScreensTracked;253 uint32_t fModeSet = false;254 255 LogRelFlowFunc(("\n"));256 rc = VbglR3VideoModeGetHighestSavedScreen(&cScreensTracked);257 if (rc != VINF_SUCCESS && rc != VERR_NOT_SUPPORTED)258 VBClFatalError(("Failed to get the number of saved screen modes, rc=%Rrc\n", rc));259 /* Make sure that we have an entry for screen 1 at least. */260 updateScreenInformation(pState, 1024, 768, 0, 1, 0, 0, true, false);261 if (rc == VINF_SUCCESS)262 {263 /* The "8" is for the sanity test below. */264 for (i = 0; i < RT_MAX(cScreensTracked + 1, 8); ++i)265 {266 unsigned cx = 0, cy = 0, cBPP = 0, x = 0, y = 0;267 bool fEnabled = true;268 269 rc = VbglR3RetrieveVideoMode(i, &cx, &cy, &cBPP, &x, &y,270 &fEnabled);271 /* Sanity test for VbglR3VideoModeGetHighestSavedScreen(). */272 if (i > cScreensTracked && rc != VERR_NOT_FOUND)273 VBClFatalError(("Internal error retrieving the number of saved screen modes.\n"));274 if (rc == VINF_SUCCESS)275 updateScreenInformation(pState, cx, cy, cBPP, i, x, y, fEnabled, true);276 }277 }278 /* Semantics: when VBOX_HAS_GRAPHICS is set, the X server driver assumes279 * that a client capable of forwarding mode hints will be present for the280 * rest of the X session. If we crash things will not work as they should.281 * I thought that preferable to implementing complex crash-handling logic.282 */283 XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOX_HAS_GRAPHICS", 0),284 XA_INTEGER, 32, PropModeReplace, (unsigned char *)&fModeSet, 1);285 while (true)286 {287 uint32_t fEvents;288 updateMouseCapabilities(pState);289 updateSizeHintsProperty(pState);290 if (!pState->fHaveRandR12)291 notifyXServerRandR11(pState);292 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED, RT_INDEFINITE_WAIT,293 &fEvents);294 /* Interrupted is used to re-set the mode without changing it. */295 if (rc == VERR_INTERRUPTED)296 rc = VINF_SUCCESS;297 if (RT_FAILURE(rc)) /* VERR_NO_MEMORY? */298 VBClFatalError(("event wait failed, rc=%Rrc\n", rc));299 /* If it is a size hint, set the new size. */300 if (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)301 {302 uint32_t cx = 0, cy = 0, cBPP = 0, iDisplay = 0, x = 0, y = 0;303 bool fEnabled = true, fUpdatePosition = true;304 VMMDevSeamlessMode Mode;305 306 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBPP, &iDisplay,307 &x, &y, &fEnabled,308 &fUpdatePosition, true);309 if (rc != VINF_SUCCESS)310 VBClFatalError(("Failed to get display change request, rc=%Rrc\n",311 rc));312 else313 LogRelFlowFunc(("Got size hint from host cx=%d, cy=%d, bpp=%d, iDisplay=%d, x=%d, y=%d fEnabled=%d\n",314 cx, cy, cBPP, iDisplay, x, y, fEnabled));315 if (iDisplay > INT32_MAX)316 VBClFatalError(("Received a size hint for too high display number %u\n",317 (unsigned) iDisplay));318 updateScreenInformation(pState, cx, cy, cBPP, iDisplay, x, y, fEnabled, fUpdatePosition);319 rc = VbglR3SeamlessGetLastEvent(&Mode);320 if (RT_FAILURE(rc))321 VBClFatalError(("Failed to check seamless mode, rc=%Rrc\n", rc));322 if (Mode == VMMDev_Seamless_Disabled)323 {324 rc = VbglR3SaveVideoMode(iDisplay, cx, cy, cBPP, x, y,325 fEnabled);326 if (RT_FAILURE(rc) && rc != VERR_NOT_SUPPORTED)327 VBClFatalError(("Failed to save size hint, rc=%Rrc\n", rc));328 }329 }330 }331 222 } 332 223 … … 354 245 if (RT_FAILURE(rc)) 355 246 return rc; 356 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED | VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);357 if (RT_FAILURE(rc))358 VBClFatalError(("Failed to set filter mask, rc=%Rrc.\n", rc));359 247 if (RT_SUCCESS(rc)) 360 248 pSelf->mfInit = true; … … 369 257 if (!pSelf->mfInit) 370 258 return VERR_WRONG_ORDER; 371 rc = startMonitorThread(pSelf);372 if (RT_FAILURE(rc))373 VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));374 259 runDisplay(pSelf); 375 260 return VERR_INTERNAL_ERROR; /* "Should never reach here." */ 376 }377 378 static void cleanup(struct VBCLSERVICE **ppInterface)379 {380 NOREF(ppInterface);381 VbglR3CtlFilterMask(0, VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED | VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST);382 VbglR3Term();383 261 } 384 262 … … 388 266 init, 389 267 run, 390 cleanup268 VBClServiceDefaultCleanup 391 269 }; 392 270
Note:
See TracChangeset
for help on using the changeset viewer.