From 2f00ca4663bd035ed1356a6436fccf5ea3af00d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Thu, 1 May 2025 10:37:04 +0200 Subject: [PATCH] [WIN32SS:ENG] Retrieve and store list of associated monitors for each video adapter device --- win32ss/gdi/eng/device.c | 141 +++++++++++++++++++++++++++++++++++---- win32ss/gdi/eng/device.h | 5 ++ 2 files changed, 133 insertions(+), 13 deletions(-) diff --git a/win32ss/gdi/eng/device.c b/win32ss/gdi/eng/device.c index ba49bd4dd7e..5b205418fe7 100644 --- a/win32ss/gdi/eng/device.c +++ b/win32ss/gdi/eng/device.c @@ -512,6 +512,131 @@ VideoPortCallout( } } +/* Sends a TargetDeviceRelation request to PDO + * On success, caller needs to free pDeviceRelations with ExFreePool() + */ +static +NTSTATUS +EngpPnPTargetRelationRequest( + _In_ PDEVICE_OBJECT pDeviceObject, + _Out_ PDEVICE_RELATIONS *pDeviceRelations) +{ + PIO_STACK_LOCATION IrpSp; + KEVENT Event; + PIRP pIrp; + IO_STATUS_BLOCK Iosb; + NTSTATUS Status; + + /* Initialize an event */ + KeInitializeEvent(&Event, SynchronizationEvent, FALSE); + + /* Build IRP */ + pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, + pDeviceObject, + NULL, 0, + NULL, + &Event, + &Iosb); + if (!pIrp) + return STATUS_INSUFFICIENT_RESOURCES; + + /* Initialize IRP */ + pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IrpSp = IoGetNextIrpStackLocation(pIrp); + IrpSp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS; + IrpSp->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation; + + /* Call the driver */ + Status = IoCallDriver(pDeviceObject, pIrp); + + /* Wait if neccessary */ + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 0); + Status = Iosb.Status; + } + + /* Return information to the caller about the operation. */ + if (NT_SUCCESS(Status)) + *pDeviceRelations = (PDEVICE_RELATIONS)Iosb.Information; + + return Status; +} + +NTSTATUS +NTAPI +EngpUpdateMonitorDevices( + _In_ PGRAPHICS_DEVICE pGraphicsDevice) +{ + PDEVICE_RELATIONS pDeviceRelations; + PVIDEO_MONITOR_DEVICE pMonitorDevices; + ULONG i, bytesWritten, monitorCount; + NTSTATUS Status; + + /* Request right PDO for device relations */ + Status = EngpPnPTargetRelationRequest(pGraphicsDevice->DeviceObject, &pDeviceRelations); + if (!NT_SUCCESS(Status)) + { + ERR("EngpPnPTargetRelationRequest() failed with status 0x%08x\n", Status); + return Status; + } + ASSERT(pDeviceRelations->Count == 1); + + /* Invalidate relations, so that videoprt reenumerates its monitors */ + IoSynchronousInvalidateDeviceRelations(pDeviceRelations->Objects[0], BusRelations); + + /* Free returned structure */ + for (i = 0; i < pDeviceRelations->Count; i++) + ObDereferenceObject(pDeviceRelations->Objects[i]); + ExFreePool(pDeviceRelations); + + /* Now, get list of monitor PDOs */ + Status = EngDeviceIoControl(pGraphicsDevice->DeviceObject, + IOCTL_VIDEO_ENUM_MONITOR_PDO, + NULL, 0, + &pMonitorDevices, sizeof(pMonitorDevices), + &bytesWritten); + if (Status != ERROR_SUCCESS) + { + ERR("EngDeviceIoControl(IOCTL_VIDEO_ENUM_MONITOR_PDO) failed with status 0x%08x\n", Status); + return Status; + } + ASSERT(bytesWritten == sizeof(pMonitorDevices)); + + /* Count number of available monitors */ + for (monitorCount = 0; pMonitorDevices[monitorCount].pdo; ++monitorCount) + ; + + if (pGraphicsDevice->pvMonDev) + { + /* Erase everything */ + for (i = 0; i < pGraphicsDevice->dwMonCnt; i++) + ObDereferenceObject(pGraphicsDevice->pvMonDev[i].pdo); + ExFreePoolWithTag(pGraphicsDevice->pvMonDev, GDITAG_GDEVICE); + pGraphicsDevice->pvMonDev = NULL; + pGraphicsDevice->dwMonCnt = 0; + } + pGraphicsDevice->pvMonDev = ExAllocatePoolZero(PagedPool, + monitorCount * sizeof(VIDEO_MONITOR_DEVICE), + GDITAG_GDEVICE); + if (!pGraphicsDevice->pvMonDev) + { + for (i = 0; pMonitorDevices[i].pdo; ++i) + ObDereferenceObject(pMonitorDevices[i].pdo); + ExFreePool(pMonitorDevices); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Copy data */ + for (i = 0; i < monitorCount; i++) + { + TRACE("%S\\Monitor%u: PDO %p HwID %u\n", pGraphicsDevice->szWinDeviceName, i, pMonitorDevices[i].pdo, pMonitorDevices[i].HwID); + pGraphicsDevice->pvMonDev[pGraphicsDevice->dwMonCnt++] = pMonitorDevices[i]; + } + ExFreePool(pMonitorDevices); + return STATUS_SUCCESS; +} + PGRAPHICS_DEVICE NTAPI EngpRegisterGraphicsDevice( @@ -580,19 +705,6 @@ EngpRegisterGraphicsDevice( // if (Win32kCallbacks.DualviewFlags & ???) pGraphicsDevice->PhysDeviceHandle = Win32kCallbacks.pPhysDeviceObject; - /* FIXME: Enumerate children monitor devices for this video adapter - * - * - Force the adapter to re-enumerate its monitors: - * IoSynchronousInvalidateDeviceRelations(pdo, BusRelations) - * - * - Retrieve all monitor PDOs from VideoPrt: - * EngDeviceIoControl(0x%p, IOCTL_VIDEO_ENUM_MONITOR_PDO) - * - * - Initialize these fields and structures accordingly: - * pGraphicsDevice->dwMonCnt - * pGraphicsDevice->pvMonDev[0..dwMonCnt-1] - */ - /* Copy the device name */ RtlStringCbCopyNW(pGraphicsDevice->szNtDeviceName, sizeof(pGraphicsDevice->szNtDeviceName), @@ -629,6 +741,9 @@ EngpRegisterGraphicsDevice( pustrDescription->Length); pGraphicsDevice->pwszDescription[pustrDescription->Length/sizeof(WCHAR)] = 0; + /* Update list of connected monitors */ + EngpUpdateMonitorDevices(pGraphicsDevice); + /* Lock loader */ EngAcquireSemaphore(ghsemGraphicsDeviceList); diff --git a/win32ss/gdi/eng/device.h b/win32ss/gdi/eng/device.h index 54caac283b3..ec975ea8257 100644 --- a/win32ss/gdi/eng/device.h +++ b/win32ss/gdi/eng/device.h @@ -16,6 +16,11 @@ EngFileWrite( _In_ SIZE_T nLength, _Out_ PSIZE_T lpBytesWritten); +NTSTATUS +NTAPI +EngpUpdateMonitorDevices( + _In_ PGRAPHICS_DEVICE pGraphicsDevice); + PGRAPHICS_DEVICE NTAPI EngpFindGraphicsDevice(