1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Win32k subsystem 4 * PURPOSE: Support for logical devices 5 * FILE: win32ss/gdi/eng/ldevobj.c 6 * PROGRAMER: Timo Kreuzer (timo.kreuzer@reactos.org) 7 */ 8 9 #include <win32k.h> 10 #define NDEBUG 11 #include <debug.h> 12 DBG_DEFAULT_CHANNEL(EngLDev); 13 14 #ifndef RVA_TO_ADDR 15 #define RVA_TO_ADDR(Base,Rva) ((PVOID)(((ULONG_PTR)(Base)) + (Rva))) 16 #endif 17 18 /** Globals *******************************************************************/ 19 20 static HSEMAPHORE ghsemLDEVList; 21 static LIST_ENTRY gleLdevListHead; 22 static LDEVOBJ *gpldevWin32k = NULL; 23 24 25 /** Private functions *********************************************************/ 26 27 CODE_SEG("INIT") 28 NTSTATUS 29 NTAPI 30 InitLDEVImpl(VOID) 31 { 32 ULONG cbSize; 33 34 /* Initialize the LDEV list head */ 35 InitializeListHead(&gleLdevListHead); 36 37 /* Initialize the loader lock */ 38 ghsemLDEVList = EngCreateSemaphore(); 39 if (!ghsemLDEVList) 40 { 41 ERR("Failed to create ghsemLDEVList\n"); 42 return STATUS_INSUFFICIENT_RESOURCES; 43 } 44 45 /* Allocate a LDEVOBJ for win32k */ 46 gpldevWin32k = ExAllocatePoolWithTag(PagedPool, 47 sizeof(LDEVOBJ) + 48 sizeof(SYSTEM_GDI_DRIVER_INFORMATION), 49 GDITAG_LDEV); 50 if (!gpldevWin32k) 51 { 52 return STATUS_NO_MEMORY; 53 } 54 55 /* Initialize the LDEVOBJ for win32k */ 56 gpldevWin32k->leLink.Flink = NULL; 57 gpldevWin32k->leLink.Blink = NULL; 58 gpldevWin32k->ldevtype = LDEV_DEVICE_DISPLAY; 59 gpldevWin32k->cRefs = 1; 60 gpldevWin32k->ulDriverVersion = GDI_ENGINE_VERSION; 61 gpldevWin32k->pGdiDriverInfo = (PVOID)(gpldevWin32k + 1); 62 RtlInitUnicodeString(&gpldevWin32k->pGdiDriverInfo->DriverName, 63 L"\\SystemRoot\\System32\\win32k.sys"); 64 gpldevWin32k->pGdiDriverInfo->ImageAddress = &__ImageBase; 65 gpldevWin32k->pGdiDriverInfo->SectionPointer = NULL; 66 gpldevWin32k->pGdiDriverInfo->EntryPoint = (PVOID)DriverEntry; 67 gpldevWin32k->pGdiDriverInfo->ExportSectionPointer = 68 RtlImageDirectoryEntryToData(&__ImageBase, 69 TRUE, 70 IMAGE_DIRECTORY_ENTRY_EXPORT, 71 &cbSize); 72 gpldevWin32k->pGdiDriverInfo->ImageLength = 0; // FIXME 73 74 return STATUS_SUCCESS; 75 } 76 77 static 78 PLDEVOBJ 79 LDEVOBJ_AllocLDEV( 80 _In_ LDEVTYPE ldevtype) 81 { 82 PLDEVOBJ pldev; 83 84 /* Allocate the structure from paged pool */ 85 pldev = ExAllocatePoolWithTag(PagedPool, sizeof(LDEVOBJ), GDITAG_LDEV); 86 if (!pldev) 87 { 88 ERR("Failed to allocate LDEVOBJ.\n"); 89 return NULL; 90 } 91 92 /* Zero out the structure */ 93 RtlZeroMemory(pldev, sizeof(LDEVOBJ)); 94 95 /* Set the ldevtype */ 96 pldev->ldevtype = ldevtype; 97 98 return pldev; 99 } 100 101 static 102 VOID 103 LDEVOBJ_vFreeLDEV( 104 _In_ _Post_ptr_invalid_ PLDEVOBJ pldev) 105 { 106 /* Make sure we don't have a driver loaded */ 107 ASSERT(pldev && pldev->pGdiDriverInfo == NULL); 108 ASSERT(pldev->cRefs == 0); 109 110 /* Free the memory */ 111 ExFreePoolWithTag(pldev, GDITAG_LDEV); 112 } 113 114 static 115 BOOL 116 LDEVOBJ_bLoadImage( 117 _Inout_ PLDEVOBJ pldev, 118 _In_ PUNICODE_STRING pustrPathName) 119 { 120 PSYSTEM_GDI_DRIVER_INFORMATION pDriverInfo; 121 NTSTATUS Status; 122 ULONG cbSize; 123 124 /* Make sure no image is loaded yet */ 125 ASSERT(pldev && pldev->pGdiDriverInfo == NULL); 126 127 /* Allocate a SYSTEM_GDI_DRIVER_INFORMATION structure */ 128 cbSize = sizeof(SYSTEM_GDI_DRIVER_INFORMATION) + pustrPathName->Length; 129 pDriverInfo = ExAllocatePoolWithTag(PagedPool, cbSize, GDITAG_LDEV); 130 if (!pDriverInfo) 131 { 132 ERR("Failed to allocate SYSTEM_GDI_DRIVER_INFORMATION\n"); 133 return FALSE; 134 } 135 136 /* Initialize the UNICODE_STRING and copy the driver name */ 137 RtlInitEmptyUnicodeString(&pDriverInfo->DriverName, 138 (PWSTR)(pDriverInfo + 1), 139 pustrPathName->Length); 140 RtlCopyUnicodeString(&pDriverInfo->DriverName, pustrPathName); 141 142 /* Try to load the driver */ 143 Status = ZwSetSystemInformation(SystemLoadGdiDriverInformation, 144 pDriverInfo, 145 sizeof(SYSTEM_GDI_DRIVER_INFORMATION)); 146 if (!NT_SUCCESS(Status)) 147 { 148 ERR("Failed to load a GDI driver: '%wZ', Status = 0x%lx\n", 149 pustrPathName, Status); 150 151 /* Free the allocated memory */ 152 ExFreePoolWithTag(pDriverInfo, GDITAG_LDEV); 153 return FALSE; 154 } 155 156 /* Set the driver info */ 157 pldev->pGdiDriverInfo = pDriverInfo; 158 159 /* Return success. */ 160 return TRUE; 161 } 162 163 static 164 BOOL 165 LDEVOBJ_bEnableDriver( 166 _Inout_ PLDEVOBJ pldev, 167 _In_ PFN_DrvEnableDriver pfnEnableDriver) 168 { 169 DRVENABLEDATA ded; 170 ULONG i; 171 172 TRACE("LDEVOBJ_bEnableDriver('%wZ')\n", &pldev->pGdiDriverInfo->DriverName); 173 174 ASSERT(pldev); 175 ASSERT(pldev->cRefs == 0); 176 177 if (pldev->ldevtype == LDEV_IMAGE) 178 return TRUE; 179 180 /* Call the drivers DrvEnableDriver function */ 181 RtlZeroMemory(&ded, sizeof(ded)); 182 if (!pfnEnableDriver(GDI_ENGINE_VERSION, sizeof(ded), &ded)) 183 { 184 ERR("DrvEnableDriver failed\n"); 185 return FALSE; 186 } 187 188 /* Copy the returned driver version */ 189 pldev->ulDriverVersion = ded.iDriverVersion; 190 191 /* Fill the driver function array */ 192 for (i = 0; i < ded.c; i++) 193 { 194 pldev->apfn[ded.pdrvfn[i].iFunc] = ded.pdrvfn[i].pfn; 195 } 196 197 /* Return success. */ 198 return TRUE; 199 } 200 201 static 202 VOID 203 LDEVOBJ_vDisableDriver( 204 _Inout_ PLDEVOBJ pldev) 205 { 206 ASSERT(pldev); 207 ASSERT(pldev->cRefs == 0); 208 209 TRACE("LDEVOBJ_vDisableDriver('%wZ')\n", &pldev->pGdiDriverInfo->DriverName); 210 211 if (pldev->ldevtype == LDEV_IMAGE) 212 return; 213 214 if (pldev->pfn.DisableDriver) 215 { 216 /* Call the unload function */ 217 pldev->pfn.DisableDriver(); 218 } 219 } 220 221 static 222 PVOID 223 LDEVOBJ_pvFindImageProcAddress( 224 _In_ PLDEVOBJ pldev, 225 _In_z_ LPSTR pszProcName) 226 { 227 PVOID pvImageBase; 228 PIMAGE_EXPORT_DIRECTORY pExportDir; 229 PVOID pvProcAdress = NULL; 230 PUSHORT pOrdinals; 231 PULONG pNames, pAddresses; 232 ULONG i; 233 234 /* Make sure we have a driver info */ 235 ASSERT(pldev && pldev->pGdiDriverInfo != NULL); 236 237 /* Get the pointer to the export directory */ 238 pvImageBase = pldev->pGdiDriverInfo->ImageAddress; 239 pExportDir = pldev->pGdiDriverInfo->ExportSectionPointer; 240 if (!pExportDir) 241 { 242 ERR("LDEVOBJ_pvFindImageProcAddress: no export section found\n"); 243 return NULL; 244 } 245 246 /* Get pointers to some tables */ 247 pNames = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfNames); 248 pOrdinals = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfNameOrdinals); 249 pAddresses = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfFunctions); 250 251 /* Loop the export table */ 252 for (i = 0; i < pExportDir->NumberOfNames; i++) 253 { 254 /* Compare the name */ 255 if (_stricmp(pszProcName, RVA_TO_ADDR(pvImageBase, pNames[i])) == 0) 256 { 257 /* Found! Calculate the procedure address */ 258 pvProcAdress = RVA_TO_ADDR(pvImageBase, pAddresses[pOrdinals[i]]); 259 break; 260 } 261 } 262 263 /* Return the address */ 264 return pvProcAdress; 265 } 266 267 static 268 BOOL 269 LDEVOBJ_bUnloadImage( 270 _Inout_ PLDEVOBJ pldev) 271 { 272 NTSTATUS Status; 273 274 /* Make sure we have a driver info */ 275 ASSERT(pldev && pldev->pGdiDriverInfo != NULL); 276 ASSERT(pldev->cRefs == 0); 277 278 TRACE("LDEVOBJ_bUnloadImage('%wZ')\n", &pldev->pGdiDriverInfo->DriverName); 279 280 /* Unload the driver */ 281 #if 0 282 Status = ZwSetSystemInformation(SystemUnloadGdiDriverInformation, 283 &pldev->pGdiDriverInfo->SectionPointer, 284 sizeof(HANDLE)); 285 #else 286 /* Unfortunately, ntoskrnl allows unloading a driver, but fails loading 287 * it again with STATUS_IMAGE_ALREADY_LOADED. Prevent this problem by 288 * never unloading any driver. 289 */ 290 UNIMPLEMENTED; 291 Status = STATUS_NOT_IMPLEMENTED; 292 #endif 293 if (!NT_SUCCESS(Status)) 294 return FALSE; 295 296 ExFreePoolWithTag(pldev->pGdiDriverInfo, GDITAG_LDEV); 297 pldev->pGdiDriverInfo = NULL; 298 299 return TRUE; 300 } 301 302 PLDEVOBJ 303 LDEVOBJ_pLoadInternal( 304 _In_ PFN_DrvEnableDriver pfnEnableDriver, 305 _In_ ULONG ldevtype) 306 { 307 PLDEVOBJ pldev; 308 309 TRACE("LDEVOBJ_pLoadInternal(%lu)\n", ldevtype); 310 311 /* Lock loader */ 312 EngAcquireSemaphore(ghsemLDEVList); 313 314 /* Allocate a new LDEVOBJ */ 315 pldev = LDEVOBJ_AllocLDEV(ldevtype); 316 if (!pldev) 317 { 318 ERR("Could not allocate LDEV\n"); 319 goto leave; 320 } 321 322 /* Load the driver */ 323 if (!LDEVOBJ_bEnableDriver(pldev, pfnEnableDriver)) 324 { 325 ERR("LDEVOBJ_bEnableDriver failed\n"); 326 LDEVOBJ_vFreeLDEV(pldev); 327 pldev = NULL; 328 goto leave; 329 } 330 331 /* Insert the LDEV into the global list */ 332 InsertHeadList(&gleLdevListHead, &pldev->leLink); 333 334 /* Increase ref count */ 335 pldev->cRefs++; 336 337 leave: 338 /* Unlock loader */ 339 EngReleaseSemaphore(ghsemLDEVList); 340 341 TRACE("LDEVOBJ_pLoadInternal returning %p\n", pldev); 342 return pldev; 343 } 344 345 PLDEVOBJ 346 NTAPI 347 LDEVOBJ_pLoadDriver( 348 _In_z_ LPWSTR pwszDriverName, 349 _In_ ULONG ldevtype) 350 { 351 WCHAR acwBuffer[MAX_PATH]; 352 PLIST_ENTRY pleLink; 353 PLDEVOBJ pldev; 354 UNICODE_STRING strDriverName; 355 SIZE_T cwcLength; 356 LPWSTR pwsz; 357 358 TRACE("LDEVOBJ_pLoadDriver(%ls, %lu)\n", pwszDriverName, ldevtype); 359 ASSERT(pwszDriverName); 360 361 /* Initialize buffer for the the driver name */ 362 RtlInitEmptyUnicodeString(&strDriverName, acwBuffer, sizeof(acwBuffer)); 363 364 /* Start path with systemroot */ 365 RtlAppendUnicodeToString(&strDriverName, L"\\SystemRoot\\System32\\"); 366 367 /* Get Length of given string */ 368 cwcLength = wcslen(pwszDriverName); 369 370 /* Check if we have a system32 path given */ 371 pwsz = pwszDriverName + cwcLength; 372 while (pwsz > pwszDriverName) 373 { 374 if ((*pwsz == L'\\') && (_wcsnicmp(pwsz, L"\\system32\\", 10) == 0)) 375 { 376 /* Driver name starts after system32 */ 377 pwsz += 10; 378 break; 379 } 380 pwsz--; 381 } 382 383 /* Append the driver name */ 384 RtlAppendUnicodeToString(&strDriverName, pwsz); 385 386 /* MSDN says "The driver must include this suffix in the pwszDriver string." 387 But in fact it's optional. The function can also load .sys files without 388 appending the .dll extension. */ 389 if ((cwcLength < 4) || 390 ((_wcsnicmp(pwszDriverName + cwcLength - 4, L".dll", 4) != 0) && 391 (_wcsnicmp(pwszDriverName + cwcLength - 4, L".sys", 4) != 0)) ) 392 { 393 /* Append the .dll suffix */ 394 RtlAppendUnicodeToString(&strDriverName, L".dll"); 395 } 396 397 /* Lock loader */ 398 EngAcquireSemaphore(ghsemLDEVList); 399 400 /* Search the List of LDEVS for the driver name */ 401 for (pleLink = gleLdevListHead.Flink; 402 pleLink != &gleLdevListHead; 403 pleLink = pleLink->Flink) 404 { 405 pldev = CONTAINING_RECORD(pleLink, LDEVOBJ, leLink); 406 407 /* Check if the ldev is associated with a file */ 408 if (pldev->pGdiDriverInfo) 409 { 410 /* Check for match (case insensative) */ 411 if (RtlEqualUnicodeString(&pldev->pGdiDriverInfo->DriverName, &strDriverName, TRUE)) 412 { 413 /* Image found in LDEV list */ 414 break; 415 } 416 } 417 } 418 419 /* Did we find one? */ 420 if (pleLink == &gleLdevListHead) 421 { 422 /* No, allocate a new LDEVOBJ */ 423 pldev = LDEVOBJ_AllocLDEV(ldevtype); 424 if (!pldev) 425 { 426 ERR("Could not allocate LDEV\n"); 427 goto leave; 428 } 429 430 /* Load the image */ 431 if (!LDEVOBJ_bLoadImage(pldev, &strDriverName)) 432 { 433 LDEVOBJ_vFreeLDEV(pldev); 434 pldev = NULL; 435 ERR("LDEVOBJ_bLoadImage failed\n"); 436 goto leave; 437 } 438 439 /* Load the driver */ 440 if (!LDEVOBJ_bEnableDriver(pldev, pldev->pGdiDriverInfo->EntryPoint)) 441 { 442 ERR("LDEVOBJ_bEnableDriver failed\n"); 443 444 /* Unload the image. */ 445 LDEVOBJ_bUnloadImage(pldev); 446 LDEVOBJ_vFreeLDEV(pldev); 447 pldev = NULL; 448 goto leave; 449 } 450 451 /* Insert the LDEV into the global list */ 452 InsertHeadList(&gleLdevListHead, &pldev->leLink); 453 } 454 455 /* Increase ref count */ 456 pldev->cRefs++; 457 458 leave: 459 /* Unlock loader */ 460 EngReleaseSemaphore(ghsemLDEVList); 461 462 TRACE("LDEVOBJ_pLoadDriver returning %p\n", pldev); 463 return pldev; 464 } 465 466 static 467 VOID 468 LDEVOBJ_vDereference( 469 _Inout_ PLDEVOBJ pldev) 470 { 471 /* Lock loader */ 472 EngAcquireSemaphore(ghsemLDEVList); 473 474 /* Decrement reference count */ 475 ASSERT(pldev->cRefs > 0); 476 pldev->cRefs--; 477 478 /* More references left? */ 479 if (pldev->cRefs > 0) 480 { 481 EngReleaseSemaphore(ghsemLDEVList); 482 return; 483 } 484 485 LDEVOBJ_vDisableDriver(pldev); 486 487 if (LDEVOBJ_bUnloadImage(pldev)) 488 { 489 /* Remove ldev from the list */ 490 RemoveEntryList(&pldev->leLink); 491 492 /* Free the driver info structure */ 493 LDEVOBJ_vFreeLDEV(pldev); 494 } 495 else 496 { 497 WARN("Failed to unload driver '%wZ', trying to re-enable it.\n", &pldev->pGdiDriverInfo->DriverName); 498 LDEVOBJ_bEnableDriver(pldev, pldev->pGdiDriverInfo->EntryPoint); 499 500 /* Increment again reference count */ 501 pldev->cRefs++; 502 } 503 504 /* Unlock loader */ 505 EngReleaseSemaphore(ghsemLDEVList); 506 } 507 508 ULONG 509 LDEVOBJ_ulGetDriverModes( 510 _In_ LPWSTR pwszDriverName, 511 _In_ HANDLE hDriver, 512 _Out_ PDEVMODEW *ppdm) 513 { 514 PLDEVOBJ pldev = NULL; 515 ULONG cbSize = 0; 516 PDEVMODEW pdm = NULL; 517 518 TRACE("LDEVOBJ_ulGetDriverModes('%ls', %p)\n", pwszDriverName, hDriver); 519 520 pldev = LDEVOBJ_pLoadDriver(pwszDriverName, LDEV_DEVICE_DISPLAY); 521 if (!pldev) 522 goto cleanup; 523 524 /* Mirror drivers may omit this function */ 525 if (!pldev->pfn.GetModes) 526 goto cleanup; 527 528 /* Call the driver to get the required size */ 529 cbSize = pldev->pfn.GetModes(hDriver, 0, NULL); 530 if (!cbSize) 531 { 532 ERR("DrvGetModes returned 0\n"); 533 goto cleanup; 534 } 535 536 /* Allocate a buffer for the DEVMODE array */ 537 pdm = ExAllocatePoolWithTag(PagedPool, cbSize, GDITAG_DEVMODE); 538 if (!pdm) 539 { 540 ERR("Could not allocate devmodeinfo\n"); 541 goto cleanup; 542 } 543 544 /* Call the driver again to fill the buffer */ 545 cbSize = pldev->pfn.GetModes(hDriver, cbSize, pdm); 546 if (!cbSize) 547 { 548 /* Could not get modes */ 549 ERR("DrvrGetModes returned 0 on second call\n"); 550 ExFreePoolWithTag(pdm, GDITAG_DEVMODE); 551 pdm = NULL; 552 } 553 554 cleanup: 555 if (pldev) 556 LDEVOBJ_vDereference(pldev); 557 558 *ppdm = pdm; 559 return cbSize; 560 } 561 562 BOOL 563 LDEVOBJ_bBuildDevmodeList( 564 _Inout_ PGRAPHICS_DEVICE pGraphicsDevice) 565 { 566 PWSTR pwsz; 567 PDEVMODEINFO pdminfo; 568 PDEVMODEW pdm, pdmEnd; 569 ULONG i, cModes = 0; 570 ULONG cbSize, cbFull; 571 572 if (pGraphicsDevice->pdevmodeInfo) 573 return TRUE; 574 ASSERT(pGraphicsDevice->pDevModeList == NULL); 575 576 pwsz = pGraphicsDevice->pDiplayDrivers; 577 578 /* Loop through the driver names 579 * This is a REG_MULTI_SZ string */ 580 for (; *pwsz; pwsz += wcslen(pwsz) + 1) 581 { 582 /* Get the mode list from the driver */ 583 TRACE("Trying driver: %ls\n", pwsz); 584 cbSize = LDEVOBJ_ulGetDriverModes(pwsz, pGraphicsDevice->DeviceObject, &pdm); 585 if (!cbSize) 586 { 587 WARN("Driver %ls returned no valid mode\n", pwsz); 588 continue; 589 } 590 591 /* Add space for the header */ 592 cbFull = cbSize + FIELD_OFFSET(DEVMODEINFO, adevmode); 593 594 /* Allocate a buffer for the DEVMODE array */ 595 pdminfo = ExAllocatePoolWithTag(PagedPool, cbFull, GDITAG_DEVMODE); 596 if (!pdminfo) 597 { 598 ERR("Could not allocate devmodeinfo\n"); 599 ExFreePoolWithTag(pdm, GDITAG_DEVMODE); 600 continue; 601 } 602 603 pdminfo->cbdevmode = cbSize; 604 RtlCopyMemory(pdminfo->adevmode, pdm, cbSize); 605 ExFreePoolWithTag(pdm, GDITAG_DEVMODE); 606 607 /* Attach the mode info to the device */ 608 pdminfo->pdmiNext = pGraphicsDevice->pdevmodeInfo; 609 pGraphicsDevice->pdevmodeInfo = pdminfo; 610 611 /* Loop all DEVMODEs */ 612 pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode); 613 for (pdm = pdminfo->adevmode; 614 (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0); 615 pdm = (DEVMODEW*)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra)) 616 { 617 /* Count this DEVMODE */ 618 cModes++; 619 620 /* Some drivers like the VBox driver don't fill the dmDeviceName 621 with the name of the display driver. So fix that here. */ 622 RtlStringCbCopyW(pdm->dmDeviceName, sizeof(pdm->dmDeviceName), pwsz); 623 } 624 } 625 626 if (!pGraphicsDevice->pdevmodeInfo || cModes == 0) 627 { 628 ERR("No devmodes\n"); 629 return FALSE; 630 } 631 632 /* Allocate an index buffer */ 633 pGraphicsDevice->cDevModes = cModes; 634 pGraphicsDevice->pDevModeList = ExAllocatePoolWithTag(PagedPool, 635 cModes * sizeof(DEVMODEENTRY), 636 GDITAG_GDEVICE); 637 if (!pGraphicsDevice->pDevModeList) 638 { 639 ERR("No devmode list\n"); 640 return FALSE; 641 } 642 643 /* Loop through all DEVMODEINFOs */ 644 for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0; 645 pdminfo; 646 pdminfo = pdminfo->pdmiNext) 647 { 648 /* Calculate End of the DEVMODEs */ 649 pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode); 650 651 /* Loop through the DEVMODEs */ 652 for (pdm = pdminfo->adevmode; 653 (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0); 654 pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra)) 655 { 656 TRACE(" %S has mode %lux%lux%lu(%lu Hz)\n", 657 pdm->dmDeviceName, 658 pdm->dmPelsWidth, 659 pdm->dmPelsHeight, 660 pdm->dmBitsPerPel, 661 pdm->dmDisplayFrequency); 662 663 /* Initialize the entry */ 664 pGraphicsDevice->pDevModeList[i].dwFlags = 0; 665 pGraphicsDevice->pDevModeList[i].pdm = pdm; 666 i++; 667 } 668 } 669 return TRUE; 670 } 671 672 /* Search the closest display mode according to some settings. 673 * Note that we don't care about the DM_* flags in dmFields, but check if value != 0 instead */ 674 static 675 BOOL 676 LDEVOBJ_bGetClosestMode( 677 _Inout_ PGRAPHICS_DEVICE pGraphicsDevice, 678 _In_ PDEVMODEW RequestedMode, 679 _Out_ PDEVMODEW *pSelectedMode) 680 { 681 DEVMODEW dmDiff; 682 PDEVMODEW pdmCurrent, pdmBest = NULL; 683 ULONG i; 684 685 /* Use a DEVMODE to keep the differences between best mode found and expected mode. 686 * Initialize fields to max value so we can find better modes. */ 687 dmDiff.dmPelsWidth = 0xffffffff; 688 dmDiff.dmPelsHeight = 0xffffffff; 689 dmDiff.dmBitsPerPel = 0xffffffff; 690 dmDiff.dmDisplayFrequency = 0xffffffff; 691 692 /* Search the closest mode */ 693 #define DM_DIFF(field) (RequestedMode->field > pdmCurrent->field ? (RequestedMode->field - pdmCurrent->field) : (pdmCurrent->field - RequestedMode->field)) 694 for (i = 0; i < pGraphicsDevice->cDevModes; i++) 695 { 696 pdmCurrent = pGraphicsDevice->pDevModeList[i].pdm; 697 698 /* Skip current mode if it is worse than best mode found */ 699 if (RequestedMode->dmPelsWidth != 0 && DM_DIFF(dmPelsWidth) > dmDiff.dmPelsWidth) 700 continue; 701 if (RequestedMode->dmPelsHeight != 0 && DM_DIFF(dmPelsHeight) > dmDiff.dmPelsHeight) 702 continue; 703 if (RequestedMode->dmBitsPerPel != 0 && DM_DIFF(dmBitsPerPel) > dmDiff.dmBitsPerPel) 704 continue; 705 if (RequestedMode->dmDisplayFrequency != 0 && DM_DIFF(dmDisplayFrequency) > dmDiff.dmDisplayFrequency) 706 continue; 707 708 /* Better (or equivalent) mode found. Update differences */ 709 dmDiff.dmPelsWidth = DM_DIFF(dmPelsWidth); 710 dmDiff.dmPelsHeight = DM_DIFF(dmPelsHeight); 711 dmDiff.dmBitsPerPel = DM_DIFF(dmBitsPerPel); 712 dmDiff.dmDisplayFrequency = DM_DIFF(dmDisplayFrequency); 713 pdmBest = pdmCurrent; 714 } 715 #undef DM_DIFF 716 717 if (pdmBest) 718 { 719 TRACE("Closest display mode to '%dx%dx%d %d Hz' is '%dx%dx%d %d Hz'\n", 720 RequestedMode->dmPelsWidth, 721 RequestedMode->dmPelsHeight, 722 RequestedMode->dmBitsPerPel, 723 RequestedMode->dmDisplayFrequency, 724 pdmBest->dmPelsWidth, 725 pdmBest->dmPelsHeight, 726 pdmBest->dmBitsPerPel, 727 pdmBest->dmDisplayFrequency); 728 } 729 730 *pSelectedMode = pdmBest; 731 return pdmBest != NULL; 732 } 733 734 BOOL 735 LDEVOBJ_bProbeAndCaptureDevmode( 736 _Inout_ PGRAPHICS_DEVICE pGraphicsDevice, 737 _In_ PDEVMODEW RequestedMode, 738 _Out_ PDEVMODEW *pSelectedMode, 739 _In_ BOOL bSearchClosestMode) 740 { 741 DEVMODEW dmSearch; 742 PDEVMODEW pdmCurrent, pdm, pdmSelected = NULL; 743 ULONG i; 744 ULONG ulVirtualWidth = 0, ulVirtualHeight = 0; 745 BOOL bResult = TRUE; 746 NTSTATUS Status; 747 748 if (!LDEVOBJ_bBuildDevmodeList(pGraphicsDevice)) 749 return FALSE; 750 751 /* At first, load information from registry */ 752 RtlZeroMemory(&dmSearch, sizeof(dmSearch)); 753 Status = EngpGetDisplayDriverParameters(pGraphicsDevice, &dmSearch); 754 if (!NT_SUCCESS(Status)) 755 { 756 ERR("EngpGetDisplayDriverParameters() failed with status 0x%08x\n", Status); 757 return FALSE; 758 } 759 760 /* Override values with the new ones provided */ 761 762 _SEH2_TRY 763 { 764 bSearchClosestMode |= RequestedMode->dmFields == 0; 765 766 /* Copy standard fields (if provided) */ 767 if (RequestedMode->dmFields & DM_BITSPERPEL && RequestedMode->dmBitsPerPel != 0) 768 dmSearch.dmBitsPerPel = RequestedMode->dmBitsPerPel; 769 if (RequestedMode->dmFields & DM_PELSWIDTH && RequestedMode->dmPelsWidth != 0) 770 dmSearch.dmPelsWidth = RequestedMode->dmPelsWidth; 771 if (RequestedMode->dmFields & DM_PELSHEIGHT && RequestedMode->dmPelsHeight != 0) 772 dmSearch.dmPelsHeight = RequestedMode->dmPelsHeight; 773 if (RequestedMode->dmFields & DM_DISPLAYFREQUENCY && RequestedMode->dmDisplayFrequency != 0) 774 dmSearch.dmDisplayFrequency = RequestedMode->dmDisplayFrequency; 775 776 if ((RequestedMode->dmFields & (DM_PANNINGWIDTH | DM_PANNINGHEIGHT)) == (DM_PANNINGWIDTH | DM_PANNINGHEIGHT) && 777 RequestedMode->dmPanningWidth != 0 && RequestedMode->dmPanningHeight != 0 && 778 RequestedMode->dmPanningWidth < dmSearch.dmPelsWidth && 779 RequestedMode->dmPanningHeight < dmSearch.dmPelsHeight) 780 { 781 /* Get new panning values */ 782 ulVirtualWidth = RequestedMode->dmPelsWidth; 783 ulVirtualHeight = RequestedMode->dmPelsHeight; 784 dmSearch.dmPelsWidth = RequestedMode->dmPanningWidth; 785 dmSearch.dmPelsHeight = RequestedMode->dmPanningHeight; 786 } 787 else if (dmSearch.dmPanningWidth != 0 && dmSearch.dmPanningHeight != 0 && 788 dmSearch.dmPanningWidth < dmSearch.dmPelsWidth && 789 dmSearch.dmPanningHeight < dmSearch.dmPelsHeight) 790 { 791 /* Keep existing panning values */ 792 ulVirtualWidth = dmSearch.dmPelsWidth; 793 ulVirtualHeight = dmSearch.dmPelsHeight; 794 dmSearch.dmPelsWidth = dmSearch.dmPanningWidth; 795 dmSearch.dmPelsHeight = dmSearch.dmPanningHeight; 796 } 797 } 798 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 799 { 800 bResult = FALSE; 801 } 802 _SEH2_END; 803 804 if (!bResult) 805 return FALSE; 806 807 if (LDEVOBJ_bGetClosestMode(pGraphicsDevice, &dmSearch, &pdmSelected)) 808 { 809 if (bSearchClosestMode) 810 { 811 /* Ok, found a closest mode. Update search */ 812 dmSearch.dmBitsPerPel = pdmSelected->dmBitsPerPel; 813 dmSearch.dmPelsWidth = pdmSelected->dmPelsWidth; 814 dmSearch.dmPelsHeight = pdmSelected->dmPelsHeight; 815 dmSearch.dmDisplayFrequency = pdmSelected->dmDisplayFrequency; 816 } 817 else 818 { 819 /* Only update not provided fields */ 820 _SEH2_TRY 821 { 822 if (!(RequestedMode->dmFields & DM_BITSPERPEL) || RequestedMode->dmBitsPerPel == 0) 823 dmSearch.dmBitsPerPel = pdmSelected->dmBitsPerPel; 824 if (!(RequestedMode->dmFields & DM_PELSWIDTH) || RequestedMode->dmPelsWidth == 0) 825 dmSearch.dmPelsWidth = pdmSelected->dmPelsWidth; 826 if (!(RequestedMode->dmFields & DM_PELSHEIGHT) || RequestedMode->dmPelsHeight == 0) 827 dmSearch.dmPelsHeight = pdmSelected->dmPelsHeight; 828 if (!(RequestedMode->dmFields & DM_DISPLAYFREQUENCY) || RequestedMode->dmDisplayFrequency == 0) 829 dmSearch.dmDisplayFrequency = pdmSelected->dmDisplayFrequency; 830 } 831 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 832 { 833 bResult = FALSE; 834 } 835 _SEH2_END; 836 837 if (!bResult) 838 return FALSE; 839 } 840 } 841 842 /* Now, search the exact mode to return to caller */ 843 for (i = 0; i < pGraphicsDevice->cDevModes; i++) 844 { 845 pdmCurrent = pGraphicsDevice->pDevModeList[i].pdm; 846 847 /* For now, we only need those */ 848 if (pdmCurrent->dmBitsPerPel != dmSearch.dmBitsPerPel) 849 continue; 850 if (pdmCurrent->dmPelsWidth != dmSearch.dmPelsWidth) 851 continue; 852 if (pdmCurrent->dmPelsHeight != dmSearch.dmPelsHeight) 853 continue; 854 if (pdmCurrent->dmDisplayFrequency != dmSearch.dmDisplayFrequency) 855 continue; 856 857 pdmSelected = pdmCurrent; 858 break; 859 } 860 861 if (!pdmSelected) 862 { 863 ERR("Requested mode not found (%dx%dx%d %d Hz)\n", 864 dmSearch.dmPelsWidth, 865 dmSearch.dmPelsHeight, 866 dmSearch.dmBitsPerPel, 867 dmSearch.dmDisplayFrequency); 868 return FALSE; 869 } 870 871 /* Allocate memory for output */ 872 pdm = ExAllocatePoolZero(PagedPool, pdmSelected->dmSize + pdmSelected->dmDriverExtra, GDITAG_DEVMODE); 873 if (!pdm) 874 return FALSE; 875 876 /* Copy selected mode */ 877 RtlCopyMemory(pdm, pdmSelected, pdmSelected->dmSize); 878 RtlCopyMemory((PVOID)((ULONG_PTR)pdm + pdm->dmSize), 879 (PVOID)((ULONG_PTR)pdmSelected + pdmSelected->dmSize), 880 pdmSelected->dmDriverExtra); 881 882 /* Add back panning */ 883 if (ulVirtualWidth != 0 && ulVirtualHeight != 0 && 884 pdm->dmPelsWidth < ulVirtualWidth && 885 pdm->dmPelsHeight < ulVirtualHeight) 886 { 887 pdm->dmFields |= DM_PANNINGWIDTH | DM_PANNINGHEIGHT; 888 pdm->dmPanningWidth = pdm->dmPelsWidth; 889 pdm->dmPanningHeight = pdm->dmPelsHeight; 890 pdm->dmPelsWidth = ulVirtualWidth; 891 pdm->dmPelsHeight = ulVirtualHeight; 892 } 893 894 *pSelectedMode = pdm; 895 return TRUE; 896 } 897 898 /** Exported functions ********************************************************/ 899 900 HANDLE 901 APIENTRY 902 EngLoadImage( 903 _In_ LPWSTR pwszDriverName) 904 { 905 return (HANDLE)LDEVOBJ_pLoadDriver(pwszDriverName, LDEV_IMAGE); 906 } 907 908 909 VOID 910 APIENTRY 911 EngUnloadImage( 912 _In_ HANDLE hModule) 913 { 914 PLDEVOBJ pldev = (PLDEVOBJ)hModule; 915 916 /* Make sure the LDEV is in the list */ 917 ASSERT((pldev->leLink.Flink != NULL) && (pldev->leLink.Blink != NULL)); 918 919 LDEVOBJ_vDereference(pldev); 920 } 921 922 923 PVOID 924 APIENTRY 925 EngFindImageProcAddress( 926 _In_ HANDLE hModule, 927 _In_ LPSTR lpProcName) 928 { 929 PLDEVOBJ pldev = (PLDEVOBJ)hModule; 930 931 ASSERT(gpldevWin32k != NULL); 932 933 /* Check if win32k is requested */ 934 if (!pldev) 935 { 936 pldev = gpldevWin32k; 937 } 938 939 /* Check if the drivers entry point is requested */ 940 if (_strnicmp(lpProcName, "DrvEnableDriver", 15) == 0) 941 { 942 return pldev->pGdiDriverInfo->EntryPoint; 943 } 944 945 /* Try to find the address */ 946 return LDEVOBJ_pvFindImageProcAddress(pldev, lpProcName); 947 } 948 949 /* EOF */ 950