Changeset 92399 in vbox
- Timestamp:
- Nov 12, 2021 2:28:15 PM (3 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Additions/x11/VBoxClient/display-drm.cpp
r92370 r92399 117 117 unsigned g_cVerbosity = 0; 118 118 119 /** Path to the PID file. */ 120 static const char g_szPidFile[RTPATH_MAX] = "/var/run/VBoxDRMClient"; 121 119 122 /** 120 123 * Attempts to open DRM device by given path and check if it is … … 210 213 } 211 214 215 /** 216 * This function converts input monitors layout array passed from DevVMM 217 * into monitors layout array to be passed to DRM stack. 218 * 219 * @return VINF_SUCCESS on success, IPRT error code otherwise. 220 * @param aDisplaysIn Input displays array. 221 * @param cDisplaysIn Number of elements in input displays array. 222 * @param aDisplaysOut Output displays array. 223 * @param cDisplaysOutMax Number of elements in output displays array. 224 * @param pcActualDisplays Number of displays to report to DRM stack (number of enabled displays). 225 */ 226 static int drmValidateLayout(VMMDevDisplayDef *aDisplaysIn, uint32_t cDisplaysIn, 227 struct DRMVMWRECT *aDisplaysOut, uint32_t cDisplaysOutMax, uint32_t *pcActualDisplays) 228 { 229 /* This array is a cache of what was received from DevVMM so far. 230 * DevVMM may send to us partial information bout scree layout. This 231 * cache remembers entire picture. */ 232 static struct VMMDevDisplayDef aVmMonitorsCache[VMW_MAX_HEADS]; 233 /* Number of valid (enabled) displays in output array. */ 234 uint32_t cDisplaysOut = 0; 235 /* Flag indicates that current layout cache is consistent and can be passed to DRM stack. */ 236 bool fValid = true; 237 238 /* Make sure input array fits cache size. */ 239 if (cDisplaysIn > VMW_MAX_HEADS) 240 { 241 VBClLogError("VBoxDRMClient: unable to validate screen layout: input (%u) array does not fit to cache size (%u)\n", 242 cDisplaysIn, VMW_MAX_HEADS); 243 return VERR_INVALID_PARAMETER; 244 } 245 246 /* Make sure there is enough space in output array. */ 247 if (cDisplaysIn > cDisplaysOutMax) 248 { 249 VBClLogError("VBoxDRMClient: unable to validate screen layout: input array (%u) is bigger than output one (%u)\n", 250 cDisplaysIn, cDisplaysOut); 251 return VERR_INVALID_PARAMETER; 252 } 253 254 /* Make sure input and output arrays are of non-zero size. */ 255 if (!(cDisplaysIn > 0 && cDisplaysOutMax > 0)) 256 { 257 VBClLogError("VBoxDRMClient: unable to validate screen layout: invalid size of either input (%u) or output display array\n", 258 cDisplaysIn, cDisplaysOutMax); 259 return VERR_INVALID_PARAMETER; 260 } 261 262 /* Update cache. */ 263 for (uint32_t i = 0; i < cDisplaysIn; i++) 264 { 265 uint32_t idDisplay = aDisplaysIn[i].idDisplay; 266 if (idDisplay < VMW_MAX_HEADS) 267 { 268 aVmMonitorsCache[idDisplay].idDisplay = idDisplay; 269 aVmMonitorsCache[idDisplay].fDisplayFlags = aDisplaysIn[i].fDisplayFlags; 270 aVmMonitorsCache[idDisplay].cBitsPerPixel = aDisplaysIn[i].cBitsPerPixel; 271 aVmMonitorsCache[idDisplay].cx = aDisplaysIn[i].cx; 272 aVmMonitorsCache[idDisplay].cy = aDisplaysIn[i].cy; 273 aVmMonitorsCache[idDisplay].xOrigin = aDisplaysIn[i].xOrigin; 274 aVmMonitorsCache[idDisplay].yOrigin = aDisplaysIn[i].yOrigin; 275 } 276 else 277 { 278 VBClLogError("VBoxDRMClient: received display ID (0x%x, position %u) is invalid\n", idDisplay, i); 279 /* If monitor configuration cannot be placed into cache, consider entire cache is invalid. */ 280 fValid = false; 281 } 282 } 283 284 /* Now, go though complete cache and check if it is valid. */ 285 for (uint32_t i = 0; i < VMW_MAX_HEADS; i++) 286 { 287 if (i == 0) 288 { 289 if (aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED) 290 { 291 VBClLogError("VBoxDRMClient: unable to validate screen layout: first monitor is not allowed to be disabled"); 292 fValid = false; 293 } 294 else 295 cDisplaysOut++; 296 } 297 else 298 { 299 /* Check if there is no hole in between monitors (i.e., if current monitor is enabled, but privious one does not). */ 300 if ( !(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED) 301 && aVmMonitorsCache[i - 1].fDisplayFlags & VMMDEV_DISPLAY_DISABLED) 302 { 303 VBClLogError("VBoxDRMClient: unable to validate screen layout: there is a hole in displays layout config, " 304 "monitor (%u) is ENABLED while (%u) does not\n", i, i - 1); 305 fValid = false; 306 } 307 else 308 { 309 /* Align displays next to each other (if needed) and check if there is no holes in between monitors. */ 310 if (!(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN)) 311 { 312 aVmMonitorsCache[i].xOrigin = aVmMonitorsCache[i - 1].xOrigin + aVmMonitorsCache[i - 1].cx; 313 aVmMonitorsCache[i].yOrigin = aVmMonitorsCache[i - 1].yOrigin; 314 } 315 316 /* Only count enabled monitors. */ 317 if (!(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)) 318 cDisplaysOut++; 319 } 320 } 321 } 322 323 /* Copy out layout data. */ 324 if (fValid) 325 { 326 for (uint32_t i = 0; i < cDisplaysOut; i++) 327 { 328 aDisplaysOut[i].x = aVmMonitorsCache[i].xOrigin; 329 aDisplaysOut[i].y = aVmMonitorsCache[i].yOrigin; 330 aDisplaysOut[i].w = aVmMonitorsCache[i].cx; 331 aDisplaysOut[i].h = aVmMonitorsCache[i].cy; 332 333 VBClLogInfo("VBoxDRMClient: update monitor %u parameters: %dx%d, (%d, %d)\n", 334 i, 335 aDisplaysOut[i].w, aDisplaysOut[i].h, 336 aDisplaysOut[i].x, aDisplaysOut[i].y); 337 338 } 339 340 *pcActualDisplays = cDisplaysOut; 341 } 342 343 return (fValid && cDisplaysOut > 0) ? VINF_SUCCESS : VERR_INVALID_PARAMETER; 344 } 345 212 346 static void drmSendHints(RTFILE hDevice, struct DRMVMWRECT *paRects, unsigned cHeads) 213 347 { 348 int rc; 349 struct DRMVMWUPDATELAYOUT ioctlLayout; 214 350 uid_t curuid = getuid(); 215 351 if (setreuid(0, 0) != 0) 216 perror("setreuid failed during drm ioctl."); 217 int rc; 218 struct DRMVMWUPDATELAYOUT ioctlLayout; 352 { 353 VBClLogError("VBoxDRMClient: setreuid failed during drm ioctl\n"); 354 return; 355 } 219 356 220 357 ioctlLayout.cOutputs = cHeads; 221 358 ioctlLayout.ptrRects = (uint64_t)paRects; 359 222 360 rc = RTFileIoCtl(hDevice, DRM_IOCTL_VMW_UPDATE_LAYOUT, 223 361 &ioctlLayout, sizeof(ioctlLayout), NULL); 224 if (RT_FAILURE(rc) && rc != VERR_INVALID_PARAMETER) 225 VBClLogFatalError("Failure updating layout, rc=%Rrc\n", rc); 362 363 VBClLogInfo("VBoxDRMClient: push layout data to DRM stack %s, %Rrc\n", 364 RT_SUCCESS(rc) ? "successful" : "failed", rc); 365 226 366 if (setreuid(curuid, 0) != 0) 227 perror("reset of setreuid failed after drm ioctl."); 367 { 368 VBClLogError("VBoxDRMClient: reset of setreuid failed after drm ioctl"); 369 } 228 370 } 229 371 230 int main(int argc, char *argv[]) 231 { 232 RTFILE hDevice = NIL_RTFILE; 233 static struct VMMDevDisplayDef aMonitors[VMW_MAX_HEADS]; 234 unsigned cEnabledMonitors; 235 /* Do not acknowledge the first event we query for to pick up old events, 236 * e.g. from before a guest reboot. */ 237 bool fAck = false; 238 239 /** The name and handle of the PID file. */ 240 static const char szPidFile[RTPATH_MAX] = "/var/run/VBoxDRMClient"; 241 RTFILE hPidFile; 242 243 int rc = RTR3InitExe(argc, &argv, 0); 244 if (RT_FAILURE(rc)) 245 return RTMsgInitFailure(rc); 246 247 rc = VbglR3InitUser(); 248 if (RT_FAILURE(rc)) 249 VBClLogFatalError("VbglR3InitUser failed: %Rrc", rc); 250 251 /* Check PID file before attempting to initialize anything. */ 252 rc = VbglR3PidFile(szPidFile, &hPidFile); 253 if (rc == VERR_FILE_LOCK_VIOLATION) 254 { 255 VBClLogInfo("VBoxDRMClient: already running, exiting\n"); 256 return RTEXITCODE_SUCCESS; 257 } 258 else if (RT_FAILURE(rc)) 259 { 260 VBClLogError("VBoxDRMClient: unable to lock PID file (%Rrc), exiting\n", rc); 261 return RTEXITCODE_FAILURE; 262 } 263 264 hDevice = drmOpenVmwgfx(); 265 if (hDevice == NIL_RTFILE) 266 return VERR_OPEN_FAILED; 267 268 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0); 269 if (RT_FAILURE(rc)) 270 { 271 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc); 272 return VERR_INVALID_HANDLE; 273 } 274 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false); 275 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */ 276 { 277 return VERR_RESOURCE_BUSY; 278 } 279 if (RT_FAILURE(rc)) 280 { 281 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc); 282 return VERR_INVALID_HANDLE; 283 } 372 static void drmMainLoop(RTFILE hDevice) 373 { 374 int rc; 284 375 285 376 for (;;) 286 377 { 378 /* Do not acknowledge the first event we query for to pick up old events, 379 * e.g. from before a guest reboot. */ 380 bool fAck = false; 381 287 382 uint32_t events; 288 struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS]; 289 uint32_t cDisplaysOut; 383 384 VMMDevDisplayDef aDisplaysIn[VMW_MAX_HEADS]; 385 uint32_t cDisplaysIn = 0; 386 387 struct DRMVMWRECT aDisplaysOut[VMW_MAX_HEADS]; 388 uint32_t cDisplaysOut = 0; 389 390 RT_ZERO(aDisplaysIn); 391 RT_ZERO(aDisplaysOut); 392 290 393 /* Query the first size without waiting. This lets us e.g. pick up 291 394 * the last event before a guest reboot when we start again after. */ 292 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplays Out, aDisplays, fAck);395 rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysIn, aDisplaysIn, fAck); 293 396 fAck = true; 294 397 if (RT_FAILURE(rc)) 398 { 295 399 VBClLogError("Failed to get display change request, rc=%Rrc\n", rc); 296 if (cDisplaysOut > VMW_MAX_HEADS) 297 VBClLogError("Display change request contained, rc=%Rrc\n", rc); 298 if (cDisplaysOut > 0) 299 { 300 for (unsigned i = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i) 301 { 302 uint32_t idDisplay = aDisplays[i].idDisplay; 303 if (idDisplay >= VMW_MAX_HEADS) 304 continue; 305 aMonitors[idDisplay].fDisplayFlags = aDisplays[i].fDisplayFlags; 306 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)) 307 { 308 if ((idDisplay == 0) || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN)) 309 { 310 aMonitors[idDisplay].xOrigin = aDisplays[i].xOrigin; 311 aMonitors[idDisplay].yOrigin = aDisplays[i].yOrigin; 312 } else { 313 aMonitors[idDisplay].xOrigin = aMonitors[idDisplay - 1].xOrigin + aMonitors[idDisplay - 1].cx; 314 aMonitors[idDisplay].yOrigin = aMonitors[idDisplay - 1].yOrigin; 315 } 316 aMonitors[idDisplay].cx = aDisplays[i].cx; 317 aMonitors[idDisplay].cy = aDisplays[i].cy; 318 } 319 } 320 /* Create an dense (consisting of enabled monitors only) array to pass to DRM. */ 321 cEnabledMonitors = 0; 322 struct DRMVMWRECT aEnabledMonitors[VMW_MAX_HEADS]; 323 for (int j = 0; j < VMW_MAX_HEADS; ++j) 324 { 325 if (!(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)) 326 { 327 aEnabledMonitors[cEnabledMonitors].x = aMonitors[j].xOrigin; 328 aEnabledMonitors[cEnabledMonitors].y = aMonitors[j].yOrigin; 329 aEnabledMonitors[cEnabledMonitors].w = aMonitors[j].cx; 330 aEnabledMonitors[cEnabledMonitors].h = aMonitors[j].cy; 331 if (cEnabledMonitors > 0) 332 aEnabledMonitors[cEnabledMonitors].x = aEnabledMonitors[cEnabledMonitors - 1].x + aEnabledMonitors[cEnabledMonitors - 1].w; 333 ++cEnabledMonitors; 334 } 335 } 336 for (unsigned i = 0; i < cEnabledMonitors; ++i) 337 printf("Monitor %u: %dx%d, (%d, %d)\n", i, (int)aEnabledMonitors[i].w, (int)aEnabledMonitors[i].h, 338 (int)aEnabledMonitors[i].x, (int)aEnabledMonitors[i].y); 339 drmSendHints(hDevice, aEnabledMonitors, cEnabledMonitors); 340 } 400 } 401 else 402 { 403 /* Validate displays layout and push it to DRM stack if valid. */ 404 rc = drmValidateLayout(aDisplaysIn, cDisplaysIn, aDisplaysOut, sizeof(aDisplaysOut), &cDisplaysOut); 405 if (RT_SUCCESS(rc)) 406 { 407 VBClLogInfo("VBoxDRMClient: push to DRM stack info about %u display(s)\n", cDisplaysOut); 408 drmSendHints(hDevice, aDisplaysOut, cDisplaysOut); 409 } 410 else 411 { 412 VBClLogError("VBoxDRMClient: displays layout is invalid, will not notify guest driver, rc=%Rrc\n", rc); 413 } 414 } 415 341 416 do 342 417 { … … 346 421 VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc); 347 422 } 423 } 424 425 int main(int argc, char *argv[]) 426 { 427 RTFILE hDevice = NIL_RTFILE; 428 RTFILE hPidFile; 429 430 int rc = RTR3InitExe(argc, &argv, 0); 431 if (RT_FAILURE(rc)) 432 return RTMsgInitFailure(rc); 433 434 rc = VbglR3InitUser(); 435 if (RT_FAILURE(rc)) 436 VBClLogFatalError("VBoxDRMClient: VbglR3InitUser failed: %Rrc", rc); 437 438 439 440 rc = VBClLogCreate(""); 441 if (RT_FAILURE(rc)) 442 VBClLogFatalError("VBoxDRMClient: failed to setup logging, rc=%Rrc\n", rc); 443 444 PRTLOGGER pReleaseLog = RTLogRelGetDefaultInstance(); 445 if (pReleaseLog) 446 { 447 rc = RTLogDestinations(pReleaseLog, "stdout"); 448 if (RT_FAILURE(rc)) 449 VBClLogFatalError("VBoxDRMClient: failed to redirert error output, rc=%Rrc", rc); 450 } 451 else 452 { 453 VBClLogFatalError("VBoxDRMClient: failed to get logger instance"); 454 } 455 456 /* Check PID file before attempting to initialize anything. */ 457 rc = VbglR3PidFile(g_szPidFile, &hPidFile); 458 if (rc == VERR_FILE_LOCK_VIOLATION) 459 { 460 VBClLogInfo("VBoxDRMClient: already running, exiting\n"); 461 return RTEXITCODE_SUCCESS; 462 } 463 else if (RT_FAILURE(rc)) 464 { 465 VBClLogError("VBoxDRMClient: unable to lock PID file (%Rrc), exiting\n", rc); 466 return RTEXITCODE_FAILURE; 467 } 468 469 hDevice = drmOpenVmwgfx(); 470 if (hDevice == NIL_RTFILE) 471 return VERR_OPEN_FAILED; 472 473 rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0); 474 if (RT_FAILURE(rc)) 475 { 476 VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc); 477 return VERR_INVALID_HANDLE; 478 } 479 rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false); 480 if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */ 481 { 482 return VERR_RESOURCE_BUSY; 483 } 484 if (RT_FAILURE(rc)) 485 { 486 VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc); 487 return VERR_INVALID_HANDLE; 488 } 489 490 drmMainLoop(hDevice); 348 491 349 492 /** @todo this code never executed since we do not have yet a clean way to exit … … 353 496 354 497 VBClLogInfo("VBoxDRMClient: releasing PID file lock\n"); 355 VbglR3ClosePidFile( szPidFile, hPidFile);498 VbglR3ClosePidFile(g_szPidFile, hPidFile); 356 499 357 500 return 0;
Note:
See TracChangeset
for help on using the changeset viewer.