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 pvProcAddress = 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 pvProcAddress = RVA_TO_ADDR(pvImageBase, pAddresses[pOrdinals[i]]); 259 break; 260 } 261 } 262 263 /* Return the address */ 264 return pvProcAddress; 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 insensitive) */ 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 if (LDEVOBJ_bUnloadImage(pldev)) 446 LDEVOBJ_vFreeLDEV(pldev); 447 else 448 ERR("Could not unload driver. Leaking memory\n"); 449 pldev = NULL; 450 goto leave; 451 } 452 453 /* Insert the LDEV into the global list */ 454 InsertHeadList(&gleLdevListHead, &pldev->leLink); 455 } 456 457 /* Increase ref count */ 458 pldev->cRefs++; 459 460 leave: 461 /* Unlock loader */ 462 EngReleaseSemaphore(ghsemLDEVList); 463 464 TRACE("LDEVOBJ_pLoadDriver returning %p\n", pldev); 465 return pldev; 466 } 467 468 static 469 VOID 470 LDEVOBJ_vDereference( 471 _Inout_ PLDEVOBJ pldev) 472 { 473 /* Lock loader */ 474 EngAcquireSemaphore(ghsemLDEVList); 475 476 /* Decrement reference count */ 477 ASSERT(pldev->cRefs > 0); 478 pldev->cRefs--; 479 480 /* More references left? */ 481 if (pldev->cRefs > 0) 482 { 483 EngReleaseSemaphore(ghsemLDEVList); 484 return; 485 } 486 487 LDEVOBJ_vDisableDriver(pldev); 488 489 if (LDEVOBJ_bUnloadImage(pldev)) 490 { 491 /* Remove ldev from the list */ 492 RemoveEntryList(&pldev->leLink); 493 494 /* Free the driver info structure */ 495 LDEVOBJ_vFreeLDEV(pldev); 496 } 497 else 498 { 499 WARN("Failed to unload driver '%wZ', trying to re-enable it.\n", &pldev->pGdiDriverInfo->DriverName); 500 LDEVOBJ_bEnableDriver(pldev, pldev->pGdiDriverInfo->EntryPoint); 501 502 /* Increment again reference count */ 503 pldev->cRefs++; 504 } 505 506 /* Unlock loader */ 507 EngReleaseSemaphore(ghsemLDEVList); 508 } 509 510 ULONG 511 LDEVOBJ_ulGetDriverModes( 512 _In_ LPWSTR pwszDriverName, 513 _In_ HANDLE hDriver, 514 _Out_ PDEVMODEW *ppdm) 515 { 516 PLDEVOBJ pldev = NULL; 517 ULONG cbSize = 0; 518 PDEVMODEW pdm = NULL; 519 520 TRACE("LDEVOBJ_ulGetDriverModes('%ls', %p)\n", pwszDriverName, hDriver); 521 522 pldev = LDEVOBJ_pLoadDriver(pwszDriverName, LDEV_DEVICE_DISPLAY); 523 if (!pldev) 524 goto cleanup; 525 526 /* Mirror drivers may omit this function */ 527 if (!pldev->pfn.GetModes) 528 goto cleanup; 529 530 /* Call the driver to get the required size */ 531 cbSize = pldev->pfn.GetModes(hDriver, 0, NULL); 532 if (!cbSize) 533 { 534 ERR("DrvGetModes returned 0\n"); 535 goto cleanup; 536 } 537 538 /* Allocate a buffer for the DEVMODE array */ 539 pdm = ExAllocatePoolWithTag(PagedPool, cbSize, GDITAG_DEVMODE); 540 if (!pdm) 541 { 542 ERR("Could not allocate devmodeinfo\n"); 543 goto cleanup; 544 } 545 546 /* Call the driver again to fill the buffer */ 547 cbSize = pldev->pfn.GetModes(hDriver, cbSize, pdm); 548 if (!cbSize) 549 { 550 /* Could not get modes */ 551 ERR("DrvrGetModes returned 0 on second call\n"); 552 ExFreePoolWithTag(pdm, GDITAG_DEVMODE); 553 pdm = NULL; 554 } 555 556 cleanup: 557 if (pldev) 558 LDEVOBJ_vDereference(pldev); 559 560 *ppdm = pdm; 561 return cbSize; 562 } 563 564 BOOL 565 LDEVOBJ_bBuildDevmodeList( 566 _Inout_ PGRAPHICS_DEVICE pGraphicsDevice) 567 { 568 PWSTR pwsz; 569 PDEVMODEINFO pdminfo; 570 PDEVMODEW pdm, pdmEnd; 571 ULONG i, cModes = 0; 572 ULONG cbSize, cbFull; 573 574 if (pGraphicsDevice->pdevmodeInfo) 575 return TRUE; 576 ASSERT(pGraphicsDevice->pDevModeList == NULL); 577 578 pwsz = pGraphicsDevice->pDiplayDrivers; 579 580 /* Loop through the driver names 581 * This is a REG_MULTI_SZ string */ 582 for (; *pwsz; pwsz += wcslen(pwsz) + 1) 583 { 584 /* Get the mode list from the driver */ 585 TRACE("Trying driver: %ls\n", pwsz); 586 cbSize = LDEVOBJ_ulGetDriverModes(pwsz, pGraphicsDevice->DeviceObject, &pdm); 587 if (!cbSize) 588 { 589 WARN("Driver %ls returned no valid mode\n", pwsz); 590 continue; 591 } 592 593 /* Add space for the header */ 594 cbFull = cbSize + FIELD_OFFSET(DEVMODEINFO, adevmode); 595 596 /* Allocate a buffer for the DEVMODE array */ 597 pdminfo = ExAllocatePoolWithTag(PagedPool, cbFull, GDITAG_DEVMODE); 598 if (!pdminfo) 599 { 600 ERR("Could not allocate devmodeinfo\n"); 601 ExFreePoolWithTag(pdm, GDITAG_DEVMODE); 602 continue; 603 } 604 605 pdminfo->cbdevmode = cbSize; 606 RtlCopyMemory(pdminfo->adevmode, pdm, cbSize); 607 ExFreePoolWithTag(pdm, GDITAG_DEVMODE); 608 609 /* Attach the mode info to the device */ 610 pdminfo->pdmiNext = pGraphicsDevice->pdevmodeInfo; 611 pGraphicsDevice->pdevmodeInfo = pdminfo; 612 613 /* Loop all DEVMODEs */ 614 pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode); 615 for (pdm = pdminfo->adevmode; 616 (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0); 617 pdm = (DEVMODEW*)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra)) 618 { 619 /* Count this DEVMODE */ 620 cModes++; 621 622 /* Some drivers like the VBox driver don't fill the dmDeviceName 623 with the name of the display driver. So fix that here. */ 624 RtlStringCbCopyW(pdm->dmDeviceName, sizeof(pdm->dmDeviceName), pwsz); 625 } 626 } 627 628 if (!pGraphicsDevice->pdevmodeInfo || cModes == 0) 629 { 630 ERR("No devmodes\n"); 631 return FALSE; 632 } 633 634 /* Allocate an index buffer */ 635 pGraphicsDevice->cDevModes = cModes; 636 pGraphicsDevice->pDevModeList = ExAllocatePoolWithTag(PagedPool, 637 cModes * sizeof(DEVMODEENTRY), 638 GDITAG_GDEVICE); 639 if (!pGraphicsDevice->pDevModeList) 640 { 641 ERR("No devmode list\n"); 642 return FALSE; 643 } 644 645 /* Loop through all DEVMODEINFOs */ 646 for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0; 647 pdminfo; 648 pdminfo = pdminfo->pdmiNext) 649 { 650 /* Calculate End of the DEVMODEs */ 651 pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode); 652 653 /* Loop through the DEVMODEs */ 654 for (pdm = pdminfo->adevmode; 655 (pdm + 1 <= pdmEnd) && (pdm->dmSize != 0); 656 pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra)) 657 { 658 TRACE(" %S has mode %lux%lux%lu(%lu Hz)\n", 659 pdm->dmDeviceName, 660 pdm->dmPelsWidth, 661 pdm->dmPelsHeight, 662 pdm->dmBitsPerPel, 663 pdm->dmDisplayFrequency); 664 665 /* Initialize the entry */ 666 pGraphicsDevice->pDevModeList[i].dwFlags = 0; 667 pGraphicsDevice->pDevModeList[i].pdm = pdm; 668 i++; 669 } 670 } 671 return TRUE; 672 } 673 674 /* Search the closest display mode according to some settings. 675 * Note that we don't care about the DM_* flags in dmFields, but check if value != 0 instead */ 676 static 677 BOOL 678 LDEVOBJ_bGetClosestMode( 679 _Inout_ PGRAPHICS_DEVICE pGraphicsDevice, 680 _In_ PDEVMODEW RequestedMode, 681 _Out_ PDEVMODEW *pSelectedMode) 682 { 683 DEVMODEW dmDiff; 684 PDEVMODEW pdmCurrent, pdmBest = NULL; 685 ULONG i; 686 687 /* Use a DEVMODE to keep the differences between best mode found and expected mode. 688 * Initialize fields to max value so we can find better modes. */ 689 dmDiff.dmPelsWidth = 0xffffffff; 690 dmDiff.dmPelsHeight = 0xffffffff; 691 dmDiff.dmBitsPerPel = 0xffffffff; 692 dmDiff.dmDisplayFrequency = 0xffffffff; 693 694 /* Search the closest mode */ 695 #define DM_DIFF(field) (RequestedMode->field > pdmCurrent->field ? (RequestedMode->field - pdmCurrent->field) : (pdmCurrent->field - RequestedMode->field)) 696 for (i = 0; i < pGraphicsDevice->cDevModes; i++) 697 { 698 pdmCurrent = pGraphicsDevice->pDevModeList[i].pdm; 699 700 /* Skip current mode if it is worse than best mode found */ 701 if (RequestedMode->dmPelsWidth != 0 && DM_DIFF(dmPelsWidth) > dmDiff.dmPelsWidth) 702 continue; 703 if (RequestedMode->dmPelsHeight != 0 && DM_DIFF(dmPelsHeight) > dmDiff.dmPelsHeight) 704 continue; 705 if (RequestedMode->dmBitsPerPel != 0 && DM_DIFF(dmBitsPerPel) > dmDiff.dmBitsPerPel) 706 continue; 707 if (RequestedMode->dmDisplayFrequency != 0 && DM_DIFF(dmDisplayFrequency) > dmDiff.dmDisplayFrequency) 708 continue; 709 710 /* Better (or equivalent) mode found. Update differences */ 711 dmDiff.dmPelsWidth = DM_DIFF(dmPelsWidth); 712 dmDiff.dmPelsHeight = DM_DIFF(dmPelsHeight); 713 dmDiff.dmBitsPerPel = DM_DIFF(dmBitsPerPel); 714 dmDiff.dmDisplayFrequency = DM_DIFF(dmDisplayFrequency); 715 pdmBest = pdmCurrent; 716 } 717 #undef DM_DIFF 718 719 if (pdmBest) 720 { 721 TRACE("Closest display mode to '%dx%dx%d %d Hz' is '%dx%dx%d %d Hz'\n", 722 RequestedMode->dmPelsWidth, 723 RequestedMode->dmPelsHeight, 724 RequestedMode->dmBitsPerPel, 725 RequestedMode->dmDisplayFrequency, 726 pdmBest->dmPelsWidth, 727 pdmBest->dmPelsHeight, 728 pdmBest->dmBitsPerPel, 729 pdmBest->dmDisplayFrequency); 730 } 731 732 *pSelectedMode = pdmBest; 733 return pdmBest != NULL; 734 } 735 736 BOOL 737 LDEVOBJ_bProbeAndCaptureDevmode( 738 _Inout_ PGRAPHICS_DEVICE pGraphicsDevice, 739 _In_ PDEVMODEW RequestedMode, 740 _Out_ PDEVMODEW *pSelectedMode, 741 _In_ BOOL bSearchClosestMode) 742 { 743 DEVMODEW dmSearch; 744 PDEVMODEW pdmCurrent, pdm, pdmSelected = NULL; 745 ULONG i; 746 ULONG ulVirtualWidth = 0, ulVirtualHeight = 0; 747 BOOL bResult = TRUE; 748 NTSTATUS Status; 749 750 if (!LDEVOBJ_bBuildDevmodeList(pGraphicsDevice)) 751 return FALSE; 752 753 /* At first, load information from registry */ 754 RtlZeroMemory(&dmSearch, sizeof(dmSearch)); 755 Status = EngpGetDisplayDriverParameters(pGraphicsDevice, &dmSearch); 756 if (!NT_SUCCESS(Status)) 757 { 758 ERR("EngpGetDisplayDriverParameters() failed with status 0x%08x\n", Status); 759 return FALSE; 760 } 761 762 /* Override values with the new ones provided */ 763 764 _SEH2_TRY 765 { 766 bSearchClosestMode |= RequestedMode->dmFields == 0; 767 768 /* Copy standard fields (if provided) */ 769 if (RequestedMode->dmFields & DM_BITSPERPEL && RequestedMode->dmBitsPerPel != 0) 770 dmSearch.dmBitsPerPel = RequestedMode->dmBitsPerPel; 771 if (RequestedMode->dmFields & DM_PELSWIDTH && RequestedMode->dmPelsWidth != 0) 772 dmSearch.dmPelsWidth = RequestedMode->dmPelsWidth; 773 if (RequestedMode->dmFields & DM_PELSHEIGHT && RequestedMode->dmPelsHeight != 0) 774 dmSearch.dmPelsHeight = RequestedMode->dmPelsHeight; 775 if (RequestedMode->dmFields & DM_DISPLAYFREQUENCY && RequestedMode->dmDisplayFrequency != 0) 776 dmSearch.dmDisplayFrequency = RequestedMode->dmDisplayFrequency; 777 778 if ((RequestedMode->dmFields & (DM_PANNINGWIDTH | DM_PANNINGHEIGHT)) == (DM_PANNINGWIDTH | DM_PANNINGHEIGHT) && 779 RequestedMode->dmPanningWidth != 0 && RequestedMode->dmPanningHeight != 0 && 780 RequestedMode->dmPanningWidth < dmSearch.dmPelsWidth && 781 RequestedMode->dmPanningHeight < dmSearch.dmPelsHeight) 782 { 783 /* Get new panning values */ 784 ulVirtualWidth = RequestedMode->dmPelsWidth; 785 ulVirtualHeight = RequestedMode->dmPelsHeight; 786 dmSearch.dmPelsWidth = RequestedMode->dmPanningWidth; 787 dmSearch.dmPelsHeight = RequestedMode->dmPanningHeight; 788 } 789 else if (dmSearch.dmPanningWidth != 0 && dmSearch.dmPanningHeight != 0 && 790 dmSearch.dmPanningWidth < dmSearch.dmPelsWidth && 791 dmSearch.dmPanningHeight < dmSearch.dmPelsHeight) 792 { 793 /* Keep existing panning values */ 794 ulVirtualWidth = dmSearch.dmPelsWidth; 795 ulVirtualHeight = dmSearch.dmPelsHeight; 796 dmSearch.dmPelsWidth = dmSearch.dmPanningWidth; 797 dmSearch.dmPelsHeight = dmSearch.dmPanningHeight; 798 } 799 } 800 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 801 { 802 bResult = FALSE; 803 } 804 _SEH2_END; 805 806 if (!bResult) 807 return FALSE; 808 809 if (LDEVOBJ_bGetClosestMode(pGraphicsDevice, &dmSearch, &pdmSelected)) 810 { 811 if (bSearchClosestMode) 812 { 813 /* Ok, found a closest mode. Update search */ 814 dmSearch.dmBitsPerPel = pdmSelected->dmBitsPerPel; 815 dmSearch.dmPelsWidth = pdmSelected->dmPelsWidth; 816 dmSearch.dmPelsHeight = pdmSelected->dmPelsHeight; 817 dmSearch.dmDisplayFrequency = pdmSelected->dmDisplayFrequency; 818 } 819 else 820 { 821 /* Only update not provided fields */ 822 _SEH2_TRY 823 { 824 if (!(RequestedMode->dmFields & DM_BITSPERPEL) || RequestedMode->dmBitsPerPel == 0) 825 dmSearch.dmBitsPerPel = pdmSelected->dmBitsPerPel; 826 if (!(RequestedMode->dmFields & DM_PELSWIDTH) || RequestedMode->dmPelsWidth == 0) 827 dmSearch.dmPelsWidth = pdmSelected->dmPelsWidth; 828 if (!(RequestedMode->dmFields & DM_PELSHEIGHT) || RequestedMode->dmPelsHeight == 0) 829 dmSearch.dmPelsHeight = pdmSelected->dmPelsHeight; 830 if (!(RequestedMode->dmFields & DM_DISPLAYFREQUENCY) || RequestedMode->dmDisplayFrequency == 0) 831 dmSearch.dmDisplayFrequency = pdmSelected->dmDisplayFrequency; 832 } 833 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 834 { 835 bResult = FALSE; 836 } 837 _SEH2_END; 838 839 if (!bResult) 840 return FALSE; 841 } 842 } 843 844 /* Now, search the exact mode to return to caller */ 845 for (i = 0; i < pGraphicsDevice->cDevModes; i++) 846 { 847 pdmCurrent = pGraphicsDevice->pDevModeList[i].pdm; 848 849 /* For now, we only need those */ 850 if (pdmCurrent->dmBitsPerPel != dmSearch.dmBitsPerPel) 851 continue; 852 if (pdmCurrent->dmPelsWidth != dmSearch.dmPelsWidth) 853 continue; 854 if (pdmCurrent->dmPelsHeight != dmSearch.dmPelsHeight) 855 continue; 856 if (pdmCurrent->dmDisplayFrequency != dmSearch.dmDisplayFrequency) 857 continue; 858 859 pdmSelected = pdmCurrent; 860 break; 861 } 862 863 if (!pdmSelected) 864 { 865 ERR("Requested mode not found (%dx%dx%d %d Hz)\n", 866 dmSearch.dmPelsWidth, 867 dmSearch.dmPelsHeight, 868 dmSearch.dmBitsPerPel, 869 dmSearch.dmDisplayFrequency); 870 return FALSE; 871 } 872 873 /* Allocate memory for output */ 874 pdm = ExAllocatePoolZero(PagedPool, pdmSelected->dmSize + pdmSelected->dmDriverExtra, GDITAG_DEVMODE); 875 if (!pdm) 876 return FALSE; 877 878 /* Copy selected mode */ 879 RtlCopyMemory(pdm, pdmSelected, pdmSelected->dmSize); 880 RtlCopyMemory((PVOID)((ULONG_PTR)pdm + pdm->dmSize), 881 (PVOID)((ULONG_PTR)pdmSelected + pdmSelected->dmSize), 882 pdmSelected->dmDriverExtra); 883 884 /* Add back panning */ 885 if (ulVirtualWidth != 0 && ulVirtualHeight != 0 && 886 pdm->dmPelsWidth < ulVirtualWidth && 887 pdm->dmPelsHeight < ulVirtualHeight) 888 { 889 pdm->dmFields |= DM_PANNINGWIDTH | DM_PANNINGHEIGHT; 890 pdm->dmPanningWidth = pdm->dmPelsWidth; 891 pdm->dmPanningHeight = pdm->dmPelsHeight; 892 pdm->dmPelsWidth = ulVirtualWidth; 893 pdm->dmPelsHeight = ulVirtualHeight; 894 } 895 896 *pSelectedMode = pdm; 897 return TRUE; 898 } 899 900 /** Exported functions ********************************************************/ 901 902 HANDLE 903 APIENTRY 904 EngLoadImage( 905 _In_ LPWSTR pwszDriverName) 906 { 907 return (HANDLE)LDEVOBJ_pLoadDriver(pwszDriverName, LDEV_IMAGE); 908 } 909 910 911 VOID 912 APIENTRY 913 EngUnloadImage( 914 _In_ HANDLE hModule) 915 { 916 PLDEVOBJ pldev = (PLDEVOBJ)hModule; 917 918 /* Make sure the LDEV is in the list */ 919 ASSERT((pldev->leLink.Flink != NULL) && (pldev->leLink.Blink != NULL)); 920 921 LDEVOBJ_vDereference(pldev); 922 } 923 924 925 PVOID 926 APIENTRY 927 EngFindImageProcAddress( 928 _In_ HANDLE hModule, 929 _In_ LPSTR lpProcName) 930 { 931 PLDEVOBJ pldev = (PLDEVOBJ)hModule; 932 933 ASSERT(gpldevWin32k != NULL); 934 935 /* Check if win32k is requested */ 936 if (!pldev) 937 { 938 pldev = gpldevWin32k; 939 } 940 941 /* Check if the drivers entry point is requested */ 942 if (_strnicmp(lpProcName, "DrvEnableDriver", 15) == 0) 943 { 944 return pldev->pGdiDriverInfo->EntryPoint; 945 } 946 947 /* Try to find the address */ 948 return LDEVOBJ_pvFindImageProcAddress(pldev, lpProcName); 949 } 950 951 /* EOF */ 952