diff --git a/.gitignore b/.gitignore index e7845ef7..67ff1692 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ bld/ # Visual Studio 2015 cache/options directory .vs/ +.idea/ +.vscode/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ diff --git a/Community Scripts/README.md b/Community Scripts/README.md index 9a0dec13..acaa6e4a 100644 --- a/Community Scripts/README.md +++ b/Community Scripts/README.md @@ -1,20 +1,18 @@ -# Scripts +#Scripts -This is a collection of Powershell scripts, written during the whole driver development. PR's are welcome for improvements or other/more functions. There haven't been a focus on keeping the scripts "up to date" all the time. Which means changes that might have occurred in the modules that these scripts depend upon, haven't been corrected either. These are more ment as a framework for "tinkers" to make use of Powershell to change settings on the fly. To use these, you should be at least be comfortable with Powershell and understand Admin privilegiets, risk/reward. +这是一组在驱动开发过程中编写的PowerShell脚本集。欢迎提出改进意见或新增其他功能。我们并未始终专注于保持脚本的"时效性",这意味着这些脚本所依赖的模块可能发生的变更也未得到修正。它们更多是作为一个框架,供"实验者"利用PowerShell即时修改设置。使用这些脚本需至少熟悉PowerShell并理解管理员权限的风险与回报。 -Anyway here is a short description of each script: -| Script | Description | -| ---------------------- | ---------------- | -| [changeres‑VDD.ps1](changeres-VDD.ps1) | Hot‑swaps the virtual panel’s resolution to the width and height you pass on the command line. | -| [get_disp_num.ps1](get_disp_num.ps1) | Returns the numeric adapter ID of the VDD screen by scanning WMI for your custom “MTT1337” monitor identifier. | -| [HDRswitch‑VDD.ps1](HDRswitch-VDD.ps1) | Flips the virtual screen between SDR (8‑bit) and HDR (10‑bit) colour modes in one click. Unsure if it works anylonger, since changes in modules | -| [modules_install.bat](modules_install.bat) | Opens an elevated PowerShell session that installs the DisplayConfig and MonitorConfig modules so every helper script has its prerequisites. | -| [primary‑VDD.ps1](primary-VDD.ps1) | Makes the virtual display the Windows primary so you can stream or remote‑desktop from a headless rig. | -| [refreshrate‑VDD.ps1](refreshrate.ps1) | Changes the VDD monitor’s refresh rate (30‑‑500 Hz) after validating the value against a safe list. | -| [rotate‑VDD.ps1](rotate-VDD.ps1) | Rotates the virtual display 90°, 180°, or 270° by calling DisplayConfig with the matching rotation token. | -| [scale‑VDD.ps1](scale-VDD.ps1) | Sets or resets DPI scaling on the virtual monitor, respecting Windows’ maximum allowed scale factor. | -| [set‑dependencies.ps1](set-dependencies.ps1) | Verifies the exact module versions required, installs or imports them on demand, and aborts downstream execution if anything is missing. | -| [silent‑install.ps1](silent-install.ps1) | Silently fetches the latest signed Virtual Display Driver from GitHub and installs it via Nefcon, then tidies up the temp workspace. | -| [toggle‑VDD.ps1](toggle-VDD.ps1) | A one‑click PowerShell switch that first elevates itself to admin, then enables or disables your Virtual Display Driver and immediately flips Windows between Extended and Cloned desktops, perfect for streamers who need to bring a virtual monitor online or offline on demand. | -| [virtual-driver-manager.ps1](virtual-driver-manager.ps1) | A comprehensive script to manage the Virtual Display Driver. It can install, uninstall, enable, disable, toggle, and check the status of the driver. | -| [winp‑VDD.ps1](winp-VDD.ps1) | A lightweight companion script that leaves the driver untouched and simply yo‑yos Windows between Extend and Clone modes, giving you an instant “presentation toggle” when the virtual display should stay permanently enabled. | +以下是每段脚本的简短说明: + +silent‑install.ps1 – 静默地从GitHub获取最新的已签名虚拟显示驱动程序,通过DevCon进行安装,然后清理临时工作区。 +modules_install.bat – 打开一个提升的PowerShell会话,该会话安装DisplayConfig和MonitorConfig模块,以便每个帮助脚本都有其先决条件。 +set‑dependencies.ps1 – 验证所需的确切模块版本,按需安装或导入它们,如果缺少任何内容,则中止下游执行。 +get_disp_num.ps1 – 通过扫描WMI以查找自定义的“MTT1337”监视器标识符,返回VDD屏幕的数字适配器ID。 +changeres‑VDD.ps1 – 将虚拟面板的分辨率热交换为您在命令行上传递的宽度和高度。 +refreshrate‑VDD.ps1 – 根据安全列表验证值后,更改VDD监视器的刷新率(30-500Hz)。 +rotate‑VDD.ps1 – 将虚拟显示器旋转90°,180°,或270°通过使用匹配的旋转标记调用DisplayConfig。 +scale‑VDD.ps1 – 在虚拟监视器上设置或重置DPI缩放,遵守Windows允许的最大缩放因子。 +HDRswitch‑VDD.ps1 – 一键即可在SDR(8位)和HDR(10位)颜色模式之间翻转虚拟屏幕。不确定它是否还能继续工作,因为moduels发生了变化) + primary‑VDD.ps1 – 将虚拟显示器设置为Windows主显示器,以便您可以从无头设备流式传输或远程桌面。 +toggle‑VDD.ps1 – 一键式PowerShell开关,首先将自身提升为管理员,然后启用或禁用您的虚拟显示驱动程序,并立即在扩展和克隆桌面之间翻转Windows,非常适合需要按需将虚拟显示器联机或脱机的流媒体用户。 +winp‑VDD.ps1 – 一个轻量级的配套脚本,使驱动程序不受影响,只需在扩展和克隆模式之间切换窗口,在虚拟显示器应永久启用时为您提供即时的“演示切换”。 diff --git a/Community Scripts/SetAutoLogin.bat b/Community Scripts/SetAutoLogin.bat new file mode 100644 index 00000000..9afad6ac --- /dev/null +++ b/Community Scripts/SetAutoLogin.bat @@ -0,0 +1,17 @@ +@echo off +chcp 65001 + +REM 设置允许自动登录 +reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v DevicePasswordLessBuildVersion /t REG_DWORD /d "0" /f + +echo 在打开的窗口中 取消勾选‌ “要使用本机,用户必须输入用户名和密码” 选项 ,并点击 "应用"。 + +echo 系统会弹出“自动登录”窗口,输入你当前账户的‌完整密码‌(如果是微软账户,请输入邮箱绑定的密码,而非PIN码),然后点击 ‌“确定”‌。 + +echo 正在准备进入设置界面... + +pause + +netplwiz + +pause \ No newline at end of file diff --git a/Community Scripts/changeres-VDD.ps1 b/Community Scripts/changeres-VDD.ps1 index 2fd7e38a..1a90cdab 100644 --- a/Community Scripts/changeres-VDD.ps1 +++ b/Community Scripts/changeres-VDD.ps1 @@ -4,7 +4,10 @@ param( [int]$xres, [Parameter(Mandatory, Position=1)] [Alias("Y","VerticalResolution")] - [int]$yres + [int]$yres, + [Parameter(Mandatory, Position=2)] + [Alias("id","displayIndex")] + [int]$displayId ) # Self-elevate the script if required @@ -21,7 +24,7 @@ if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdent . "$PSScriptRoot\set-dependencies.ps1" # Getting the correct display -$disp = Get-DisplayInfo | Where-Object { $_.DisplayName -eq "VDD by MTT" } +#$disp = Get-DisplayInfo | Where-Object { $_.DisplayName -eq "VDD by MTT" } # Setting the resolution on the display -Set-DisplayResolution -DisplayId $disp.DisplayId -Width $xres -Height $yres \ No newline at end of file +Set-DisplayResolution -DisplayId $displayId -Width $xres -Height $yres \ No newline at end of file diff --git a/Community Scripts/set-dependencies.ps1 b/Community Scripts/set-dependencies.ps1 index 40a3df42..c56a9fbf 100644 --- a/Community Scripts/set-dependencies.ps1 +++ b/Community Scripts/set-dependencies.ps1 @@ -24,6 +24,11 @@ if (-not (Get-PSRepository -Name $repo.Name -ErrorAction SilentlyContinue)) { Set-PSRepository -Name $repo.Name -InstallationPolicy Trusted ` -InformationAction Ignore -WarningAction SilentlyContinue +#import DisplayConfig +Import-Module DisplayConfig +#import MonitorConfig +Import-Module MonitorConfig + # Loop through the modules and ensure they are imported & installed foreach ($m in $modules) { diff --git a/Community Scripts/silent-install.ps1 b/Community Scripts/silent-install.ps1 index e32eef2f..7836fdd1 100644 --- a/Community Scripts/silent-install.ps1 +++ b/Community Scripts/silent-install.ps1 @@ -3,7 +3,7 @@ param( # Latest stable version of NefCon installer [Parameter(Mandatory=$false)] - [string]$NefConURL = "https://github.com/nefarius/nefcon/releases/download/v1.14.0/nefcon_v1.14.0.zip", + [string]$NefConURL = "https://github.com/nefarius/nefcon/releases/download/v1.17.40/nefcon_v1.17.40.zip", # Latest stable version of VDD driver only [Parameter(Mandatory=$false)] @@ -11,47 +11,72 @@ param( ); # Create temp directory -$tempDir = Join-Path $env:TEMP "VDDInstall"; +$tempDir = $PSScriptRoot; New-Item -ItemType Directory -Path $tempDir -Force | Out-Null; -# Download and unzip NefCon -Write-Host "Downloading and extracting NefCon..." -ForegroundColor Cyan; -$NefConZipPath = Join-Path $tempDir "nefcon.zip"; -Invoke-WebRequest -Uri $NefConURL -OutFile $NefConZipPath -UseBasicParsing -ErrorAction Stop; -Expand-Archive -Path $NefConZipPath -DestinationPath $tempDir -Force -ErrorAction Stop; -$NefConExe = Join-Path $tempDir "x64\nefconw.exe"; - -# Download and unzip VDD -Write-Host "Downloading and extracting VDD..." -ForegroundColor Cyan; -$driverZipPath = Join-Path $tempDir 'driver.zip'; -Invoke-WebRequest -Uri $DriverURL -OutFile $driverZipPath; -Expand-Archive -Path $driverZipPath -DestinationPath $tempDir -Force; +# Define path to nefcon executable +$NefConExe = Join-Path $tempDir "x64\nefconc.exe"; +# Download and run DevCon Installer +if (-not (Test-Path $NefConExe)) +{ + $NefConZipPath = Join-Path $tempDir "nefcon.zip"; + if (-not (Test-Path $NefConZipPath)) + { + Write-Host "Downloading NefCon..." -ForegroundColor Cyan; + Invoke-WebRequest -Uri $NefConURL -OutFile $NefConZipPath -UseBasicParsing -ErrorAction Stop; + } + Write-Host "extracting NefCon..." -ForegroundColor Cyan; + Expand-Archive -Path $NefConZipPath -DestinationPath $tempDir -Force -ErrorAction Stop; + Write-Host "extracting NefCon Completed..." -ForegroundColor Cyan; +} -# Extract the SignPath certificates -Write-Host "Extracting SignPath certificates..." -ForegroundColor Cyan; -$catFile = Join-Path $tempDir 'VirtualDisplayDriver\mttvdd.cat'; -$catBytes = [System.IO.File]::ReadAllBytes($catFile); -$certificates = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection; -$certificates.Import($catBytes); +# Check if VDD is installed. Or else, install it +$check = & $NefConExe --find-hwid ---hardware-id "Root\NztVdd"; +if ($check -match "Virtual Display Driver") { + Write-Host "Virtual Display Driver already present. No installation." -ForegroundColor Green; +} else { + # Extract the signPath certificates + $catFile = Join-Path $tempDir 'VirtualDisplayDriver\NztVdd.cat'; + if (-not (Test-Path $catFile)){ + # Download and unzip VDD + $driverZipPath = Join-Path $tempDir 'driver.zip'; + if (-not (Test-Path $driverZipPath)) + { + Write-Host "Downloading VDD..." -ForegroundColor Cyan; + Invoke-WebRequest -Uri $DriverURL -OutFile $driverZipPath; + } + Expand-Archive -Path $driverZipPath -DestinationPath $tempDir -Force; + } -# Create the temp directory for certificates -$certsFolder = Join-Path $tempDir "ExportedCerts"; -New-Item -ItemType Directory -Path $certsFolder -Force | Out-Null; + # Extract the SignPath certificates + Write-Host "Extracting SignPath certificates..." -ForegroundColor Cyan; + $signature = Get-AuthenticodeSignature -FilePath $catFile; + $catBytes = [System.IO.File]::ReadAllBytes($catFile); + $certificates = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection; + $certificates.Import($catBytes); -# Write and store the driver certificates on local machine -Write-Host "Installing driver certificates on local machine." -ForegroundColor Cyan; -foreach ($cert in $certificates) { - $certFilePath = Join-Path -Path $certsFolder -ChildPath "$($cert.Thumbprint).cer"; - $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert) | Set-Content -Path $certFilePath -Encoding Byte; - Import-Certificate -FilePath $certFilePath -CertStoreLocation "Cert:\LocalMachine\TrustedPublisher"; -} + # Create the temp directory for certificates + $certsFolder = Join-Path $tempDir "VirtualDisplayDriver"; + if (-not (Test-Path $certsFolder)) + { + New-Item -ItemType Directory -Path $certsFolder -Force | Out-Null; + } + # Write and store the driver certificates on local machine + Write-Host "Installing driver certificates on local machine." -ForegroundColor Cyan; + foreach ($cert in $certificates) { + $certFilePath = Join-Path -Path $certsFolder -ChildPath "NztVdd.cer"; + $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert) | Set-Content -Path $certFilePath -Encoding Byte; + Import-Certificate -FilePath $certFilePath -CertStoreLocation "Cert:\LocalMachine\Root"; + Import-Certificate -FilePath $certFilePath -CertStoreLocation "Cert:\LocalMachine\TrustedPublisher"; + } -# Install VDD -Write-Host "Installing Virtual Display Driver silently..." -ForegroundColor Cyan; -Push-Location $tempDir; -& $NefConExe install .\VirtualDisplayDriver\MttVDD.inf "Root\MttVDD"; -Start-Sleep -Seconds 10; -Pop-Location; + # Install VDD + Write-Host "Installing Virtual Display Driver silently..." -ForegroundColor Cyan; + Push-Location $tempDir; + & $NefConExe install .\VirtualDisplayDriver\NztVdd.inf "Root\NztVdd"; + Start-Sleep -Seconds 2; + Pop-Location; -Write-Host "Driver installation completed." -ForegroundColor Green; -Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue; + Write-Host "Driver installation completed." -ForegroundColor Green; +} +#Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue; diff --git a/Community Scripts/silent-uninstall.ps1 b/Community Scripts/silent-uninstall.ps1 new file mode 100644 index 00000000..765d5663 --- /dev/null +++ b/Community Scripts/silent-uninstall.ps1 @@ -0,0 +1,17 @@ +# Run this script in a powershell with administrator rights (run as administrator) +[CmdletBinding()] +param( + +); + +# Create temp directory +$tempDir = $PSScriptRoot; +New-Item -ItemType Directory -Path $tempDir -Force | Out-Null; +$NefConExe = Join-Path $tempDir "x64\nefconc.exe"; +Push-Location $tempDir; +& $NefConExe remove "Root\NztVdd"; +& $NefConExe --uninstall-driver --inf-path .\VirtualDisplayDriver\NztVdd.inf; +Write-Host "Driver installation removed." -ForegroundColor Green; +Start-Sleep -Seconds 2; +Pop-Location; + diff --git a/Community Scripts/vdd-install.bat b/Community Scripts/vdd-install.bat new file mode 100644 index 00000000..ed60e4c2 --- /dev/null +++ b/Community Scripts/vdd-install.bat @@ -0,0 +1,28 @@ +@echo off +chcp 65001 + +echo "正在安装虚拟显示器..." + +REM 将我们的配置目录写入注册表 +rem install +set "DRIVER_DIR=%~dp0VirtualDisplayDriver" +echo driver directory:%DRIVER_DIR% +rem Get root directory,current in the scripts directory. +for %%I in ("%~dp0\..\..") do set "ROOT_DIR=%%~fI" +set "CONFIG_DIR=%ROOT_DIR%\config" +set "VDD_CONFIG=%CONFIG_DIR%\option.txt" +echo driver config path: %VDD_CONFIG% +REM 总是从原始目录中拷贝配置到配置目录,不管是用于恢复还是备份,都是不错的选择 +copy "%DRIVER_DIR%\option.txt" "%VDD_CONFIG%" +REM 因为默认读的是注册表配置的目录,我们需要将注册表的目录配置一下 +reg add "HKLM\SOFTWARE\MikeTheTech\VirtualDisplayDriver" /v path /t REG_SZ /d "%CONFIG_DIR%" /f +REM 注册表设置允许远程RPC +reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v AllowRemoteRPC /t REG_DWORD /d "1" /f + +REM 安装具体的驱动 +powershell.exe -ExecutionPolicy Bypass -File "%~dp0silent-install.ps1" + +REM 初次安装后,还应设置成扩展这些显示器的显示模式 +powershell -NoProfile -ExecutionPolicy Bypass -Command "& "C:\Windows\System32\DisplaySwitch.exe" /extend" + +echo "虚拟显示器安装完成" \ No newline at end of file diff --git a/Community Scripts/modules_install.bat b/Community Scripts/vdd-modules_install.bat similarity index 97% rename from Community Scripts/modules_install.bat rename to Community Scripts/vdd-modules_install.bat index 5e3e78ea..17d86781 100644 --- a/Community Scripts/modules_install.bat +++ b/Community Scripts/vdd-modules_install.bat @@ -1,20 +1,19 @@ -@echo off - -:: Use PowerShell for elevation check and execution -powershell -NoProfile -ExecutionPolicy Bypass -Command ^ - "$elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator');" ^ - "if (-not $elevated) {" ^ - "$CommandLine = '-File \"' + $MyInvocation.MyCommand.Path + '\" ' + $MyInvocation.UnboundArguments;" ^ - "Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine;" ^ - "Exit" ^ - "} else {" ^ - "Install-Module -Name DisplayConfig -Scope AllUsers -Force -AllowClobber;" ^ - "Install-Module -Name MonitorConfig -Scope AllUsers -Force -AllowClobber;" ^ - "if ($?) {" ^ - "Write-Output 'Modules installed successfully.'" ^ - "} else {" ^ - "Write-Output 'An error occurred while installing the modules.'" ^ - "}" ^ - "}" - -pause \ No newline at end of file +@echo off +chcp 65001 + +:: Use PowerShell for elevation check and execution +powershell -NoProfile -ExecutionPolicy Bypass -Command ^ + "$elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator');" ^ + "if (-not $elevated) {" ^ + "$CommandLine = '-File \"' + $MyInvocation.MyCommand.Path + '\" ' + $MyInvocation.UnboundArguments;" ^ + "Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine;" ^ + "Exit" ^ + "} else {" ^ + "Install-Module -Name DisplayConfig -Scope AllUsers -Force -AllowClobber;" ^ + "Install-Module -Name MonitorConfig -Scope AllUsers -Force -AllowClobber;" ^ + "if ($?) {" ^ + "Write-Output 'Modules installed successfully.'" ^ + "} else {" ^ + "Write-Output 'An error occurred while installing the modules.'" ^ + "}" ^ + "}" diff --git a/Community Scripts/vdd-uninstall.bat b/Community Scripts/vdd-uninstall.bat new file mode 100644 index 00000000..95e1f79e --- /dev/null +++ b/Community Scripts/vdd-uninstall.bat @@ -0,0 +1,4 @@ +@echo off +chcp 65001 + +powershell.exe -ExecutionPolicy Bypass -File "%~dp0\silent-uninstall.ps1" diff --git a/Community Scripts/virtual-driver-manager.ps1 b/Community Scripts/virtual-driver-manager.ps1 index c67ba1a2..f61678b8 100644 --- a/Community Scripts/virtual-driver-manager.ps1 +++ b/Community Scripts/virtual-driver-manager.ps1 @@ -1,12 +1,11 @@ <# .SYNOPSIS A comprehensive script to manage the Virtual Display Driver. -It can install, uninstall, enable, disable, toggle, and check the status of the driver. +It can enable, disable, toggle, and check the status of the driver. .DESCRIPTION This script handles the full lifecycle of the Virtual Display Driver. - If no action is specified, it interactively prompts the user to select one. -- For install/uninstall, it automatically resolves the correct version of Microsoft's DevCon utility using a nearest-build matching algorithm for maximum compatibility. - For enable/disable/toggle/status, it uses fast, built-in PowerShell commands. - It requires Administrator privileges and will self-elevate if needed by re-launching in a new window. - All temporary files are automatically cleaned up unless in Verbose mode for diagnostics. @@ -14,8 +13,6 @@ This script handles the full lifecycle of the Virtual Display Driver. .PARAMETER Action Specifies the operation to perform. If omitted, the script will prompt for a selection. -.PARAMETER DriverVersion -Used only with the 'install' action to specify a version of the Virtual Display Driver, otherwise defaults to 'latest'. .PARAMETER Json If present, all output will be in JSON format for easy parsing by other programs. @@ -32,12 +29,6 @@ If present, prints detailed diagnostic information and prevents the temporary fo # Run without an action to get an interactive menu. The window will pause when finished. .\virtual-driver-manager.ps1 -# Install the driver and pause for review afterwards (default behavior). -.\virtual-driver-manager.ps1 -Action install - -# Uninstall the driver and close the window automatically. -.\virtual-driver-manager.ps1 -Action uninstall -Silent - .EXAMPLE # --- USAGE FROM CMD.EXE OR ANOTHER PROCESS --- @@ -50,7 +41,7 @@ powershell.exe -ExecutionPolicy Bypass -File .\virtual-driver-manager.ps1 -Actio [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $false)] - [ValidateSet('install', 'uninstall', 'enable', 'disable', 'toggle', 'status')] + [ValidateSet('enable', 'disable', 'toggle', 'status')] [string]$Action, [Parameter(Mandatory = $false)] @@ -104,7 +95,7 @@ if (-not $PSBoundParameters.ContainsKey('Action')) { exit 1 } - $options = 'install', 'uninstall', 'enable', 'disable', 'toggle', 'status' + $options = 'enable', 'disable', 'toggle', 'status' Write-Host "`nPlease select an action to perform:" -ForegroundColor Yellow for ($i = 0; $i -lt $options.Length; $i++) { @@ -162,11 +153,6 @@ if (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdenti #---------------------------------------------------------------------- # SECTION 4: SETUP AND GUARANTEED CLEANUP #---------------------------------------------------------------------- -# Create a unique temporary directory to avoid conflicts if the script is run multiple times concurrently. -$tempDir = Join-Path $env:TEMP "VDD-Manager-$(Get-Random)" -New-Item -ItemType Directory -Path $tempDir -Force | Out-Null -Write-Verbose "Created temporary directory at $tempDir" - # Use a try/catch/finally block to ensure that no matter what happens (success or error), # the 'finally' block will ALWAYS run to clean up temporary files. try { @@ -188,7 +174,7 @@ try { } # Fallback: If not found by name, use the more specific and stable Hardware ID. if (-not $device) { - $device = Get-PnpDevice -HardwareID "Root\MttVDD" -ErrorAction Silentlycontinue + $device = Get-PnpDevice -HardwareID "Root\NztVdd" -ErrorAction Silentlycontinue } return $device } @@ -196,135 +182,7 @@ try { #---------------------------------------------------------------------- # SECTION 6: MAIN ACTION LOGIC #---------------------------------------------------------------------- - if ($Action -in @('install', 'uninstall')) { - - #--- 6a. Automatically Resolve DevCon Hash --- - Write-Log -Message "Action '$Action' requires the DevCon utility. Determining correct version..." - $osInfo = Get-CimInstance -ClassName Win32_OperatingSystem - $osBuild = [int]$osInfo.BuildNumber - - $osMajorVersion = 'Server' - if ($osInfo.Caption -match 'Windows 11') { $osMajorVersion = '11' } - elseif ($osInfo.Caption -match 'Windows 10') { $osMajorVersion = '10' } - - Write-Log -Message "Detected: Windows $osMajorVersion (Build $osBuild)" - Write-Verbose "OS detection complete. Starting DevCon source matching." - - # This map is the core of the matching logic. It translates the human-readable version names - # from the devcon_sources.json file into their corresponding OS build numbers for comparison. - $versionNameToBuildMap = @{ - "Windows 11 version 24H2" = 26100; "Windows 11 version 23H2" = 22631; "Windows 11 version 22H2" = 22621; - "Windows 11 version 21H2" = 22000; "Windows Server 2022" = 20348; "Windows 10 version 2004" = 19041; - "Windows 10 version 1903" = 18362; "Windows 10 version 1809" = 17763; "Windows Server 2019" = 17763 - } - - $sourcesUrl = "https://raw.githubusercontent.com/Drawbackz/DevCon-Installer/refs/heads/master/devcon_sources.json" - Write-Verbose "Fetching DevCon sources from $sourcesUrl" - $devconSources = Invoke-RestMethod -Uri $sourcesUrl - - # Enrich the downloaded source list with a calculated 'BuildNumber' property to make it sortable. - $enrichedSources = $devconSources | ForEach-Object { - $source = $_; $matchedBuild = 0 - foreach ($entry in $versionNameToBuildMap.GetEnumerator()) { - if ($source.Name.Contains($entry.Key)) { $matchedBuild = $entry.Value; break } - } - $source | Add-Member -MemberType NoteProperty -Name "BuildNumber" -Value $matchedBuild; $source - } | Where-Object { $_.BuildNumber -gt 0 } - - $osFamilySources = $enrichedSources | Where-Object { $_.Name -like "*Windows $osMajorVersion*" -or ($osMajorVersion -eq 'Server' -and $_.Name -like "*Server*") } - - # --- NEAREST-BUILD MATCHING LOGIC --- - # 1. Try for a perfect match first. - $bestMatch = $osFamilySources | Where-Object { $_.BuildNumber -eq $osBuild } | Select-Object -First 1 - - if (-not $bestMatch) { - Write-Log -Message "No exact DevCon match for build $osBuild. Finding nearest available version..." -Status 'Warning' - # 2. If no exact match, find the newest version that is still older than (or equal to) the current OS. - # This is the safest fallback, as it guarantees API compatibility. - $bestOlderMatch = $osFamilySources | Where-Object { $_.BuildNumber -le $osBuild } | Sort-Object BuildNumber -Descending | Select-Object -First 1 - - # 3. If no older versions exist, find the oldest version that is newer than the current OS. - # This is a less-safe fallback but better than failing completely. - $bestNewerMatch = $osFamilySources | Where-Object { $_.BuildNumber -gt $osBuild } | Sort-Object BuildNumber | Select-Object -First 1 - - if ($bestOlderMatch) { $bestMatch = $bestOlderMatch } elseif ($bestNewerMatch) { $bestMatch = $bestNewerMatch } - Write-Verbose "Nearest older match: $($bestOlderMatch.Name) | Nearest newer match: $($bestNewerMatch.Name)" - } - - if (-not $bestMatch) { throw "Could not find any compatible DevCon versions for your OS (Build $osBuild)." } - - $devconHash = ($bestMatch.Sources | Where-Object { $_.Architecture -eq 'X64' }).Sha256 - Write-Log -Message "Using DevCon Source: $($bestMatch.Name) (Build $($bestMatch.BuildNumber))" -Status 'Success' - Write-Verbose "Selected DevCon Hash (X64): $devconHash" - if (-not $devconHash) { throw "Could not find a 64-bit DevCon hash in the selected source: $($bestMatch.Name)" } - - #--- 6b. Acquire DevCon Utility --- - Write-Log -Message "Acquiring secure DevCon utility..." - $devconInstallerUrl = "https://github.com/Drawbackz/DevCon-Installer/releases/download/1.4-rc/Devcon.Installer.exe" - $devconInstallerPath = Join-Path $tempDir "Devcon.Installer.exe" - Write-Verbose "Downloading DevCon Installer from $devconInstallerUrl" - Invoke-WebRequest -Uri $devconInstallerUrl -OutFile $devconInstallerPath - Write-Verbose "DevCon Installer downloaded successfully." - - # Use the hash to ensure the DevCon-Installer utility downloads the correct, secure version of DevCon. - $devconArgs = "install -hash $devconHash -update -dir `"$tempDir`"" - Write-Verbose "Running DevCon Installer with arguments: $devconArgs" - Start-Process -FilePath $devconInstallerPath -ArgumentList $devconArgs -Wait -NoNewWindow - - $devconExe = Join-Path $tempDir "devcon.exe" - if (-not (Test-Path $devconExe)) { throw "Failed to acquire devcon.exe." } - Write-Verbose "devcon.exe acquired successfully at $devconExe" - - #--- 6c. Execute Install or Uninstall --- - if ($Action -eq 'install') { - Write-Log -Message "Starting driver installation..." - $downloadUrl = $null - if ($DriverVersion -eq "latest") { - $apiUrl = "https://api.github.com/repos/VirtualDrivers/Virtual-Display-Driver/releases/latest" - # Technical Choice: Many APIs, including GitHub's, require a User-Agent header. - # Omitting this can lead to connection errors (like 403 Forbidden or 404 Not Found). - $headers = @{ "User-Agent" = "PowerShell-VDD-Manager-Script" } - - Write-Verbose "Querying GitHub API for latest driver release: $apiUrl" - $releaseInfo = Invoke-RestMethod -Uri $apiUrl -Headers $headers - Write-Verbose "API call successful. Latest release found: $($releaseInfo.tag_name)" - - $asset = $releaseInfo.assets | Where-Object { $_.name -match "x64\.zip$" } | Select-Object -First 1 - if (-not $asset) { throw "Could not find a 64-bit driver asset (x64.zip) in the latest GitHub release." } - - $downloadUrl = $asset.browser_download_url - Write-Verbose "Found driver asset: $($asset.name)" - } - else { - $downloadUrl = "https://github.com/VirtualDrivers/Virtual-Display-Driver/releases/download/$DriverVersion/Signed-Driver-v$DriverVersion-x64.zip" - Write-Verbose "Using specified driver version: $DriverVersion" - } - - if (-not $downloadUrl) { throw "Could not determine a valid driver download URL for version '$DriverVersion'." } - - Write-Verbose "Downloading driver from URL: $downloadUrl" - $driverZipPath = Join-Path $tempDir "driver.zip" - Invoke-WebRequest -Uri $downloadUrl -OutFile $driverZipPath - Write-Verbose "Driver ZIP file downloaded to $driverZipPath" - - Expand-Archive -Path $driverZipPath -DestinationPath $tempDir -Force - Write-Verbose "Driver archive expanded." - - # Use DevCon to install the driver by pointing to its INF file and specifying its unique Hardware ID. - # "Root\MttVDD" identifies this as a root-enumerated virtual device. - Write-Verbose "Running DevCon to install the driver..." - & $devconExe install (Join-Path $tempDir "MttVDD.inf") "Root\MttVDD" - } - else { # Action must be 'uninstall' - Write-Log -Message "Starting driver uninstallation..." - if (Get-VirtualDisplayDevice) { - Write-Verbose "Driver found. Preparing to remove." - & $devconExe remove "Root\MttVDD" - } - else { Write-Log -Message "Driver is not currently installed. Nothing to do." } - } - } - elseif ($Action -in @('enable', 'disable', 'toggle')) { + if ($Action -in @('enable', 'disable', 'toggle')) { $device = Get-VirtualDisplayDevice if (-not $device) { Write-Log -Message "Device not found. Cannot perform '$Action'. Please install the driver first." -Status 'Warning' } elseif ($Action -eq 'enable') { Write-Log -Message "Enabling device: $($device.FriendlyName)..."; $device | Enable-PnpDevice -Confirm:$false } @@ -383,16 +241,7 @@ finally { # This block ALWAYS runs, ensuring cleanup happens after success or failure. # If the user ran with -Verbose, we assume they are debugging. # We will NOT delete the temporary folder so they can inspect its contents. - if ($PSBoundParameters.ContainsKey('Verbose')) { - Write-Verbose "Verbose mode is active. Temporary directory will not be deleted so you can inspect its contents: $tempDir" - } - else { - if (Test-Path $tempDir) { - # This Write-Verbose message will not be visible without -Verbose, but is good practice. - Write-Verbose "Cleaning up temporary directory: $tempDir" - Remove-Item -Path $tempDir -Recurse -Force - } - } + } # Add a final pause unless in Silent or JSON mode so the user can see the output. diff --git a/Community Scripts/x64/nefconc.exe b/Community Scripts/x64/nefconc.exe new file mode 100644 index 00000000..6a966897 Binary files /dev/null and b/Community Scripts/x64/nefconc.exe differ diff --git a/Community Scripts/x64/nefconw.exe b/Community Scripts/x64/nefconw.exe new file mode 100644 index 00000000..a1c38c01 Binary files /dev/null and b/Community Scripts/x64/nefconw.exe differ diff --git a/README.md b/README.md index 3378f621..89ee875a 100644 --- a/README.md +++ b/README.md @@ -1,164 +1 @@ -# 🛠️ Virtual Display Driver Development Team - -| 👤 Developer | 🏷️ Role | 💖 Support Us | -| --------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -| **[MikeTheTech](https://github.com/itsmikethetech)** | Project Manager, Lead Programmer | [Patreon](https://www.patreon.com/mikethetech) :gem: / [GitHub Sponsors](https://github.com/sponsors/itsmikethetech/) 💖 | -| **[Jocke](https://github.com/zjoasan)** | Programmer, Concept Design | [GitHub Sponsors ](https://github.com/sponsors/zjoasan) 💖 | - -:bulb: *We appreciate your support—every contribution helps us keep building amazing experiences!* - -# Virtual Display Driver -This project creates a _virtual monitor_ in Windows that functions just like a physical display. It is particularly useful for applications such as **streaming, virtual reality, screen recording,** and **headless servers—systems** that operate without a physical display attached. - -Unlike traditional monitors, this virtual display supports custom resolutions and refresh rates beyond hardware limitations—offering greater flexibility for advanced setups. You can also use custom EDIDs to simulate or emulate existing hardware displays. - -# Check out our other Software! - -| 💻 Software | 📝 What It Does | -| ----------- | --------------- | -| [Linux Virtual Display Driver](https://github.com/VirtualDrivers/Linux-Virtual-Display-Driver) | Add virtual monitors to Linux for streaming, OBS, Sunshine, and desktop sharing workflows. | -| [Virtual Audio Driver](https://github.com/VirtualDrivers/Virtual-Audio-Driver) | Add virtual speaker and microphone devices to Windows 10/11. | -| [Virtual Driver Control](https://github.com/VirtualDrivers/Virtual-Driver-Control) | Control and configure VirtualDrivers tools from a unified app/interface. | -| [VirtualBT](https://github.com/VirtualDrivers/VirtualBT) | A Windows Bluetooth server for simulating input devices like keyboards, mice, and gamepads. Still very early and not fully functional. | -| **[BurnBin](https://burnb.in)** | A portable Windows file-sharing tool that turns your own PC into a temporary file drop. Share files with short-lived public links, optional return uploads, endpoint health checks, managed BurnBin links, Cloudflared fallback links, and Direct IP sharing when available. | -| **[FireFetch](https://firefetch.app)** | A private media-saving app for building an offline library from videos, songs, playlists, torrents, and direct files. It uses tools like yt-dlp, FFmpeg, aria2c, MusicBrainz, YouTube Music metadata, and local indexing to save, queue, organize, and browse media without cloud lock-in or AI-generated recommendations. | -| **[InfinitePIP](https://infinitepip.app)** | An always-on-top desktop picture-in-picture tool that turns live screens, app windows, and custom regions into borderless floating windows. Create multiple PiPs, pan, zoom, adjust opacity, keep captures above other windows, and even run virtual monitors from Virtual Display Driver inside floating PiP views. | -| **[BurnFlix](https://burnflix.app)** | A local-first media server that turns movies, shows, personal videos, games, and apps into one polished, living room-ready library. It loads from an offline cache, scans in the background, enriches metadata with providers like TMDB, OMDb, TheTVDB, Steam, Libretro, and IGDB, and keeps artwork, recommendations, and library health local. | - -Explore more games, tools, apps, and experiments from PyroSoft: **[Visit PyroSoft.Pro](https://pyrosoft.pro)** - -## 🧾 Changelog (recent) - -- **PCI-bus based GPU selection (LUID)**: Improved multi-GPU render adapter selection by resolving an adapter **LUID from a PCI bus number** (more deterministic than name-only matching). -- **Device/PCI-slot friendly naming**: Improved device identification / friendly naming using DeviceID and PCI slot info. -- **GetIddCx helper tool**: Added source for the `GetIddCx` utility to query IddCx versions. -- **EDID integration**: Added EDID integration support for custom/virtual monitor profiles. -- **Performance optimization**: Driver performance improvements (various internal optimizations). - -## ⬇️ Download Latest Version - -- [Driver Installer (Windows 10/11)](https://github.com/VirtualDrivers/Virtual-Display-Driver/releases) - Check the [Releases](https://github.com/VirtualDrivers/Virtual-Display-Driver/releases) page for the latest version and release notes. -- **Winget:** `winget install --id=VirtualDrivers.Virtual-Display-Driver -e` - -> [!IMPORTANT] -> Before using the Virtual Display Driver, ensure the following dependencies are installed: -> - **Microsoft Visual C++ Redistributable** -> If you encounter the error `vcruntime140.dll not found`, download and install the latest version from the [Microsoft Visual C++ Redistributable page](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170). -> -> **Driver update safety:** If you're about to install **major GPU/chipset driver updates**, uninstall VDD first. If you get a black screen or display priority gets scrambled, boot into **Safe Mode** and uninstall VDD to recover. - - -## 🛠️ Installation - -- Step 1: Download the Virtual Driver Control app. - - You can download the installer directly from the [Releases](https://github.com/VirtualDrivers/Virtual-Display-Driver/releases) page. - -- Step 2: Extract to a folder and run the app - - Launch the VDC. - - Click the Install button. - -- Step 3: Verify the Installation (Optional) - - Check if the Virtual Display Driver is correctly installed by running the following: - - **Device Manager:** Check "Device Manager" under "Display Adapters." - - **Settings:** Check display settings under system settings and see if the virtual displays show. - -While VDC is a good and friendly way to work with VDD, you can also do a a lot manually. Like adding or removing resolutions or enable/disable -functions, which is done by editing vdd_settings.xml. You should be able to locate the file at the default location: -```C:\VirtualDisplayDriver\vdd_settings.xml ``` - -For more information about manual installation, uninstallation and "personalization", please check out the [Wiki](https://github.com/VirtualDrivers/Virtual-Display-Driver/wiki) here on -the project GitHub repository. If you are into tinkering, check out the Powershell scripts in [Community scripts](https://github.com/VirtualDrivers/Virtual-Display-Driver/tree/master/Community%20Scripts). - -## 🤔 Comparison with other IDDs - -The table below shows a comparison with other popular Indirect Display Driver -projects. - -| Project | Iddcx version | Signed | SDR | HDR | H-Cursor | Tweakable | ARM64 Support | Custom EDID | Floating Point Refresh Rates | -| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | -| [Virtual-Display-Driver (HDR)] | 1.10 (latest) | ✅ | ✅ (8/10bit) | ✅ (10/12bit) | ✅ | ✅ | ✅¹ | ✅ | ✅ | -| [usbmmid_v2] | | ✅ | ✅ (8bit) | | | | | | | -| [virtual-display-rs] | 1.5 | | ✅ (8bit) | | ✅ | ✅ | | | | -| [parsec-vdd]| 1.5 | ✅ | ✅ (8bit) | | ✅ | ✅ | | | | -| [lddSampleDriver] | 1.2 | | ✅ (8bit) | | | | | | | -| [RustDesklddDriver] | 1.2 | | ✅ (8bit) | | | | | | | - -¹ ARM64 Support in Windows 11 24H2 or later may require test signing be enabled. - -HDR Support Now Available for Windows 11 23H2+ - -## ▶️ Videos and Tutorials - -### Installation Video - -[![Thumbnail24 (1)](https://github.com/user-attachments/assets/383d840c-8327-439b-a89b-84b271320371)](https://youtu.be/jN5YnHlC0fE) - -![Powerpoint](https://github.com/user-attachments/assets/9ac05776-36e1-4ba1-ac52-3f189dbd7730) - -## Troubleshooting -### ⚠️ Important: GPU/Chipset Driver Updates - -VDD can conflict with major driver updates, causing display priority issues or black screens on boot. - -**Before Updating Drivers** - -If you're planning to update GPU or chipset drivers: -1. Uninstall VDD first (via VDC app or Device Manager) -2. Complete your driver update -3. Reinstall VDD - -**If You're Stuck at Black Screen** - -*Boot into Safe Mode:* -* Force shutdown 2-3 times until Windows Recovery appears -* Choose: Troubleshoot → Advanced Options → Startup Settings → Restart → F4 (Safe Mode) - -*Remove VDD:* -* Open Device Manager in Safe Mode -* Expand Display Adapters → Uninstall Virtual Display Driver -* Restart normally - -*Quick Fix Alternative:* -* Press `Win + P` at black screen -* Press ↓ arrow and Enter multiple times to cycle display modes - -**Why This Happens** - -During driver updates, Windows re-enumerates display devices and may prioritize the virtual display over your physical monitor. Since VDD has no physical screen attached, this results in a black screen even though Windows is running normally. - -## 🤝 Sponsors - - - - - - -
Free code signing on Windows provided by SignPath.io, certificate by SignPath Foundation
- - - - - -
Advanced 32bit IEEE Float Audio brought to you by **Lune Studios**.
- -## Acknowledgements - -- Special thanks to **[@ye4241](https://github.com/ye4241)** for submitting the package to Microsoft (WinGet). - -- Shoutout to **[MikeTheTech](https://github.com/itsmikethetech)** Project Manager, Owner, and Programmer -- Shoutout to **[zjoasan](https://github.com/zjoasan)** Programmer. For scripts, EDID integration, and parts of installer. -- Shoutout to **[Bud](https://github.com/bud3699)** Former Lead Programmer, has since left the project. -- Shoutout to **[Roshkins](https://github.com/roshkins/IddSampleDriver)** for the original repo. -- Shoutout to **[Baloukj](https://github.com/baloukj/IddSampleDriver)** for the 8-bit / 10-bit support. (Also, first to push the new Microsoft Driver public!) -- Shoutout to **[Anakngtokwa](https://github.com/Anakngtokwa)** for assisting with finding driver sources. -- **[Microsoft](https://github.com/microsoft/Windows-driver-samples/tree/master/video/IndirectDisplay)** Indirect Display Driver/Sample (Driver code) -- Thanks to **[AKATrevorJay](https://github.com/akatrevorjay/edid-generator)** for the hi-res EDID. -- Shoutout to **[LexTrack](https://github.com/lextrack/)** for the MiniScreenRecorder script. - -## Star History - -[![Star History Chart](https://api.star-history.com/svg?repos=VirtualDrivers/Virtual-Display-Driver&type=Date)](https://www.star-history.com/#VirtualDrivers/Virtual-Display-Driver&Date) - -## Disclaimer: - -This software is provided "AS IS" with NO IMPLICIT OR EXPLICIT warranty. It's worth noting that while this software functioned without issues on our systems, there is no guarantee that it will not impact your computer. It operates in User Mode(Session0), which reduces the likelihood of causing system instability, such as the Blue Screen of Death. However, exercise caution when using this software. +#### Virtual Display for sunshine-super diff --git a/Virtual Display Driver (HDR)/GetIddCx/IddCxVersionQuery.cpp b/Virtual Display Driver (HDR)/GetIddCx/IddCxVersionQuery.cpp index 3294d691..f3a1acc2 100644 --- a/Virtual Display Driver (HDR)/GetIddCx/IddCxVersionQuery.cpp +++ b/Virtual Display Driver (HDR)/GetIddCx/IddCxVersionQuery.cpp @@ -2,6 +2,12 @@ #include #include #include +#include +#include +#include +#include +//必须使用这个命名空间,才能让 "ms" 后缀生效 +using namespace std::chrono_literals; struct IddCxVersionInfo { std::string windowsVersion; @@ -65,37 +71,161 @@ IddCxVersionInfo GetIddCxVersionFromBuild(DWORD buildNumber) { return {"Unknown Windows Version", "Unknown", 0x0000}; } +constexpr auto kMaxRetryCount = 3; +constexpr auto kInitialRetryDelay = 500ms; +constexpr auto kMaxRetryDelay = 5000ms; +const DWORD kPipeTimeoutMs = 5000; +const DWORD kPipeBufferSize = 4096; -int main() { - std::cout << "IddCx Version Query Tool\n"; - std::cout << "========================\n\n"; - - DWORD buildNumber = GetWindowsBuildNumber(); - - if (buildNumber == 0) { - std::cout << "Error: Could not retrieve Windows build number.\n"; - return 1; +std::wstring StringToWString(const std::string& str) +{ + int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); + std::wstring wstr(size_needed,0); + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstr[0], size_needed); + return wstr; +} + + +std::chrono::milliseconds calculate_exponential_backoff(int attempt) { + auto delay = kInitialRetryDelay * (1 << attempt); + if(delay.count()>kMaxRetryDelay.count()){ + return kMaxRetryDelay; } - - std::cout << "Windows Build Number: " << buildNumber << "\n"; - - IddCxVersionInfo versionInfo = GetIddCxVersionFromBuild(buildNumber); - - std::cout << "Windows Version: " << versionInfo.windowsVersion << "\n"; - std::cout << "IddCx Version: " << versionInfo.iddcxVersion << "\n"; - std::cout << "IddCx Version Value: 0x" << std::hex << std::uppercase << versionInfo.versionValue << "\n\n"; - - if (versionInfo.iddcxVersion == "Unknown") { - std::cout << "Note: This Windows build may not support IddCx or uses an unknown version.\n"; - std::cout << "IddCx was introduced in Windows 10 Creators Update (build 15063).\n"; - } else { - std::cout << "IddCx Framework Information:\n"; - std::cout << "- IddCx (Indirect Display Driver Class eXtension) is a framework for\n"; - std::cout << " developing indirect display drivers in Windows.\n"; - std::cout << "- This version provides specific capabilities and APIs for indirect\n"; - std::cout << " display driver development.\n"; + return delay; +} + +HANDLE connect_to_pipe_with_retry(const wchar_t *pipe_name, int max_retries=3) { + HANDLE hPipe = INVALID_HANDLE_VALUE; + int attempt = 0; + auto retry_delay = kInitialRetryDelay; + + while (attempt < max_retries) { + hPipe = CreateFileW(pipe_name, + GENERIC_READ | GENERIC_WRITE, + 0,NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, // 使用异步IO + NULL); + + if (hPipe != INVALID_HANDLE_VALUE) { + DWORD mode = PIPE_READMODE_MESSAGE; + if (SetNamedPipeHandleState(hPipe, &mode, NULL, NULL)) { + return hPipe; + } + CloseHandle(hPipe); + } + + ++attempt; + retry_delay = calculate_exponential_backoff(attempt); + std::cout << "will retry after (ms):"<< retry_delay.count() < -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - - +#include "Driver.tmh" -#define PIPE_NAME L"\\\\.\\pipe\\MTTVirtualDisplayPipe" - -#pragma comment(lib, "xmllite.lib") -#pragma comment(lib, "shlwapi.lib") +#include "Utils.h" +#include "IOHelper.h" +#include "version.h" +#include -HANDLE hPipeThread = NULL; -bool g_Running = true; -mutex g_Mutex; -HANDLE g_pipeHandle = INVALID_HANDLE_VALUE; +#include +#include +#include using namespace std; using namespace Microsoft::IndirectDisp; using namespace Microsoft::WRL; -void vddlog(const char* type, const char* message); - -extern "C" DRIVER_INITIALIZE DriverEntry; - -EVT_WDF_DRIVER_DEVICE_ADD VirtualDisplayDriverDeviceAdd; -EVT_WDF_DEVICE_D0_ENTRY VirtualDisplayDriverDeviceD0Entry; - -EVT_IDD_CX_ADAPTER_INIT_FINISHED VirtualDisplayDriverAdapterInitFinished; -EVT_IDD_CX_ADAPTER_COMMIT_MODES VirtualDisplayDriverAdapterCommitModes; - -EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION VirtualDisplayDriverParseMonitorDescription; -EVT_IDD_CX_MONITOR_GET_DEFAULT_DESCRIPTION_MODES VirtualDisplayDriverMonitorGetDefaultModes; -EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES VirtualDisplayDriverMonitorQueryModes; - -EVT_IDD_CX_MONITOR_ASSIGN_SWAPCHAIN VirtualDisplayDriverMonitorAssignSwapChain; -EVT_IDD_CX_MONITOR_UNASSIGN_SWAPCHAIN VirtualDisplayDriverMonitorUnassignSwapChain; +#define CONFIG_FILE "option.txt" +#define RESET_EVENT_NAME "Global\\NztDisplayReset" +#define DISPLAY_SIGN_NAME "Global\\NztDisplaySign" +#define DRIVER_NAME "NztVdd" +#define LOG_FILE_NAME DRIVER_NAME".log" +#define DUMP_FILE_NAME DRIVER_NAME".dmp" +#define EDID_FILE_NAME DRIVER_NAME"_edid.bin" -EVT_IDD_CX_ADAPTER_QUERY_TARGET_INFO VirtualDisplayDriverEvtIddCxAdapterQueryTargetInfo; -EVT_IDD_CX_MONITOR_SET_DEFAULT_HDR_METADATA VirtualDisplayDriverEvtIddCxMonitorSetDefaultHdrMetadata; -EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION2 VirtualDisplayDriverEvtIddCxParseMonitorDescription2; -EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES2 VirtualDisplayDriverEvtIddCxMonitorQueryTargetModes2; -EVT_IDD_CX_ADAPTER_COMMIT_MODES2 VirtualDisplayDriverEvtIddCxAdapterCommitModes2; +std::string configPath = "C:\\ProgramData\\Nzt\\Vdd";//默认配置路径 ,这边考虑到需要读取配置 -EVT_IDD_CX_MONITOR_SET_GAMMA_RAMP VirtualDisplayDriverEvtIddCxMonitorSetGammaRamp; - -struct -{ - AdapterOption Adapter; +struct{ + AdapterOption Adapter; } Options; -vector> monitorModes; -vector< DISPLAYCONFIG_VIDEO_SIGNAL_INFO> s_KnownMonitorModes2; -UINT numVirtualDisplays; -wstring gpuname; -wstring confpath = L"C:\\VirtualDisplayDriver"; -bool logsEnabled = false; -bool debugLogs = false; -bool HDRPlus = false; -bool SDR10 = false; -bool customEdid = false; -bool hardwareCursor = false; -bool preventManufacturerSpoof = false; -bool edidCeaOverride = false; -bool sendLogsThroughPipe = true; - -constexpr DISPLAYCONFIG_VIDEO_SIGNAL_INFO dispinfo(UINT32 h, UINT32 v, UINT32 rn, UINT32 rd); - -namespace -{ - void RebuildKnownMonitorModesCache() - { - s_KnownMonitorModes2.clear(); - s_KnownMonitorModes2.reserve(monitorModes.size()); - - for (const auto& mode : monitorModes) - { - s_KnownMonitorModes2.push_back( - dispinfo( - std::get<0>(mode), - std::get<1>(mode), - std::get<2>(mode), - std::get<3>(mode))); - } - } -} - -//Mouse settings -bool alphaCursorSupport = true; -int CursorMaxX = 128; -int CursorMaxY = 128; -IDDCX_XOR_CURSOR_SUPPORT XorCursorSupportLevel = IDDCX_XOR_CURSOR_SUPPORT_FULL; - - -//Rest -IDDCX_BITS_PER_COMPONENT SDRCOLOUR = IDDCX_BITS_PER_COMPONENT_8; -IDDCX_BITS_PER_COMPONENT HDRCOLOUR = IDDCX_BITS_PER_COMPONENT_10; - -wstring ColourFormat = L"RGB"; - -// === EDID INTEGRATION SETTINGS === -bool edidIntegrationEnabled = false; -bool autoConfigureFromEdid = false; -wstring edidProfilePath = L"EDID/monitor_profile.xml"; -bool overrideManualSettings = false; -bool fallbackOnError = true; - -// === HDR ADVANCED SETTINGS === -bool hdr10StaticMetadataEnabled = false; -double maxDisplayMasteringLuminance = 1000.0; -double minDisplayMasteringLuminance = 0.05; -int maxContentLightLevel = 1000; -int maxFrameAvgLightLevel = 400; - -bool colorPrimariesEnabled = false; -double redX = 0.708, redY = 0.292; -double greenX = 0.170, greenY = 0.797; -double blueX = 0.131, blueY = 0.046; -double whiteX = 0.3127, whiteY = 0.3290; - -bool colorSpaceEnabled = false; -double gammaCorrection = 2.4; -wstring primaryColorSpace = L"sRGB"; -bool enableMatrixTransform = false; - -// === AUTO RESOLUTIONS SETTINGS === -bool autoResolutionsEnabled = false; -wstring sourcePriority = L"manual"; -int minRefreshRate = 24; -int maxRefreshRate = 240; -bool excludeFractionalRates = false; -int minResolutionWidth = 640; -int minResolutionHeight = 480; -int maxResolutionWidth = 7680; -int maxResolutionHeight = 4320; -bool useEdidPreferred = false; -int fallbackWidth = 1920; -int fallbackHeight = 1080; -int fallbackRefresh = 60; - -// === COLOR ADVANCED SETTINGS === -bool autoSelectFromColorSpace = false; -wstring forceBitDepth = L"auto"; -bool fp16SurfaceSupport = true; -bool wideColorGamut = false; -bool hdrToneMapping = false; -double sdrWhiteLevel = 80.0; - -// === MONITOR EMULATION SETTINGS === -bool monitorEmulationEnabled = false; -bool emulatePhysicalDimensions = false; -int physicalWidthMm = 510; -int physicalHeightMm = 287; -bool manufacturerEmulationEnabled = false; -wstring manufacturerName = L"Generic"; -wstring modelName = L"Virtual Display"; -wstring serialNumber = L"VDD001"; - -std::map> SettingsQueryMap = { - {L"LoggingEnabled", {L"LOGS", L"logging"}}, - {L"DebugLoggingEnabled", {L"DEBUGLOGS", L"debuglogging"}}, - {L"CustomEdidEnabled", {L"CUSTOMEDID", L"CustomEdid"}}, - - {L"PreventMonitorSpoof", {L"PREVENTMONITORSPOOF", L"PreventSpoof"}}, - {L"EdidCeaOverride", {L"EDIDCEAOVERRIDE", L"EdidCeaOverride"}}, - {L"SendLogsThroughPipe", {L"SENDLOGSTHROUGHPIPE", L"SendLogsThroughPipe"}}, - - //Cursor Begin - {L"HardwareCursorEnabled", {L"HARDWARECURSOR", L"HardwareCursor"}}, - {L"AlphaCursorSupport", {L"ALPHACURSORSUPPORT", L"AlphaCursorSupport"}}, - {L"CursorMaxX", {L"CURSORMAXX", L"CursorMaxX"}}, - {L"CursorMaxY", {L"CURSORMAXY", L"CursorMaxY"}}, - {L"XorCursorSupportLevel", {L"XORCURSORSUPPORTLEVEL", L"XorCursorSupportLevel"}}, - //Cursor End - - //Colour Begin - {L"HDRPlusEnabled", {L"HDRPLUS", L"HDRPlus"}}, - {L"SDR10Enabled", {L"SDR10BIT", L"SDR10bit"}}, - {L"ColourFormat", {L"COLOURFORMAT", L"ColourFormat"}}, - //Colour End - - //EDID Integration Begin - {L"EdidIntegrationEnabled", {L"EDIDINTEGRATION", L"enabled"}}, - {L"AutoConfigureFromEdid", {L"AUTOCONFIGFROMEDID", L"auto_configure_from_edid"}}, - {L"EdidProfilePath", {L"EDIDPROFILEPATH", L"edid_profile_path"}}, - {L"OverrideManualSettings", {L"OVERRIDEMANUALSETTINGS", L"override_manual_settings"}}, - {L"FallbackOnError", {L"FALLBACKONERROR", L"fallback_on_error"}}, - //EDID Integration End - - //HDR Advanced Begin - {L"Hdr10StaticMetadataEnabled", {L"HDR10STATICMETADATA", L"enabled"}}, - {L"MaxDisplayMasteringLuminance", {L"MAXLUMINANCE", L"max_display_mastering_luminance"}}, - {L"MinDisplayMasteringLuminance", {L"MINLUMINANCE", L"min_display_mastering_luminance"}}, - {L"MaxContentLightLevel", {L"MAXCONTENTLIGHT", L"max_content_light_level"}}, - {L"MaxFrameAvgLightLevel", {L"MAXFRAMEAVGLIGHT", L"max_frame_avg_light_level"}}, - {L"ColorPrimariesEnabled", {L"COLORPRIMARIES", L"enabled"}}, - {L"RedX", {L"REDX", L"red_x"}}, - {L"RedY", {L"REDY", L"red_y"}}, - {L"GreenX", {L"GREENX", L"green_x"}}, - {L"GreenY", {L"GREENY", L"green_y"}}, - {L"BlueX", {L"BLUEX", L"blue_x"}}, - {L"BlueY", {L"BLUEY", L"blue_y"}}, - {L"WhiteX", {L"WHITEX", L"white_x"}}, - {L"WhiteY", {L"WHITEY", L"white_y"}}, - {L"ColorSpaceEnabled", {L"COLORSPACE", L"enabled"}}, - {L"GammaCorrection", {L"GAMMA", L"gamma_correction"}}, - {L"PrimaryColorSpace", {L"PRIMARYCOLORSPACE", L"primary_color_space"}}, - {L"EnableMatrixTransform", {L"MATRIXTRANSFORM", L"enable_matrix_transform"}}, - //HDR Advanced End - - //Auto Resolutions Begin - {L"AutoResolutionsEnabled", {L"AUTORESOLUTIONS", L"enabled"}}, - {L"SourcePriority", {L"SOURCEPRIORITY", L"source_priority"}}, - {L"MinRefreshRate", {L"MINREFRESHRATE", L"min_refresh_rate"}}, - {L"MaxRefreshRate", {L"MAXREFRESHRATE", L"max_refresh_rate"}}, - {L"ExcludeFractionalRates", {L"EXCLUDEFRACTIONAL", L"exclude_fractional_rates"}}, - {L"MinResolutionWidth", {L"MINWIDTH", L"min_resolution_width"}}, - {L"MinResolutionHeight", {L"MINHEIGHT", L"min_resolution_height"}}, - {L"MaxResolutionWidth", {L"MAXWIDTH", L"max_resolution_width"}}, - {L"MaxResolutionHeight", {L"MAXHEIGHT", L"max_resolution_height"}}, - {L"UseEdidPreferred", {L"USEEDIDPREFERRED", L"use_edid_preferred"}}, - {L"FallbackWidth", {L"FALLBACKWIDTH", L"fallback_width"}}, - {L"FallbackHeight", {L"FALLBACKHEIGHT", L"fallback_height"}}, - {L"FallbackRefresh", {L"FALLBACKREFRESH", L"fallback_refresh"}}, - //Auto Resolutions End - - //Color Advanced Begin - {L"AutoSelectFromColorSpace", {L"AUTOSELECTCOLOR", L"auto_select_from_color_space"}}, - {L"ForceBitDepth", {L"FORCEBITDEPTH", L"force_bit_depth"}}, - {L"Fp16SurfaceSupport", {L"FP16SUPPORT", L"fp16_surface_support"}}, - {L"WideColorGamut", {L"WIDECOLORGAMUT", L"wide_color_gamut"}}, - {L"HdrToneMapping", {L"HDRTONEMAPPING", L"hdr_tone_mapping"}}, - {L"SdrWhiteLevel", {L"SDRWHITELEVEL", L"sdr_white_level"}}, - //Color Advanced End - - //Monitor Emulation Begin - {L"MonitorEmulationEnabled", {L"MONITOREMULATION", L"enabled"}}, - {L"EmulatePhysicalDimensions", {L"EMULATEPHYSICAL", L"emulate_physical_dimensions"}}, - {L"PhysicalWidthMm", {L"PHYSICALWIDTH", L"physical_width_mm"}}, - {L"PhysicalHeightMm", {L"PHYSICALHEIGHT", L"physical_height_mm"}}, - {L"ManufacturerEmulationEnabled", {L"MANUFACTUREREMULATION", L"enabled"}}, - {L"ManufacturerName", {L"MANUFACTURERNAME", L"manufacturer_name"}}, - {L"ModelName", {L"MODELNAME", L"model_name"}}, - {L"SerialNumber", {L"SERIALNUMBER", L"serial_number"}}, - //Monitor Emulation End -}; -const char* XorCursorSupportLevelToString(IDDCX_XOR_CURSOR_SUPPORT level) { - switch (level) { - case IDDCX_XOR_CURSOR_SUPPORT_UNINITIALIZED: - return "IDDCX_XOR_CURSOR_SUPPORT_UNINITIALIZED"; - case IDDCX_XOR_CURSOR_SUPPORT_NONE: - return "IDDCX_XOR_CURSOR_SUPPORT_NONE"; - case IDDCX_XOR_CURSOR_SUPPORT_FULL: - return "IDDCX_XOR_CURSOR_SUPPORT_FULL"; - case IDDCX_XOR_CURSOR_SUPPORT_EMULATION: - return "IDDCX_XOR_CURSOR_SUPPORT_EMULATION"; - default: - return "Unknown"; - } -} - - -vector Microsoft::IndirectDisp::IndirectDeviceContext::s_KnownMonitorEdid; //Changed to support static vector - -std::map, Microsoft::IndirectDisp::LuidComparator> Microsoft::IndirectDisp::IndirectDeviceContext::s_DeviceCache; -std::mutex Microsoft::IndirectDisp::IndirectDeviceContext::s_DeviceCacheMutex; - -struct IndirectDeviceContextWrapper +struct { + HANDLE hPipe; + IDDCX_ADAPTER adapter;//显示适配器 +}PipeWorkingContext; + + +vector displays; //显示器信息 +vector fpsList; //刷新率 +vector res; //分辨率 +vector> displayResFpsList={{1920,1080,60},{1920,1080,60}}; //没有使用edid时,默认使用此配置 + +UINT displayCount = 1; //默认初始化一个虚拟显示器 +int logLevel = 3; //默认初始化一个日志级别为信息级 +CRITICAL_SECTION logSec; +string logFile; +string logsPath; +int64_t logMaxSize = 1024 * 1024;//默认1m,太大会打不开 +bool useEdid = true; //默认使用edid +bool hardwareCursor = true;//默认使用硬件鼠标 + +const bool defaultDisplayRecordSteam = false; //是否启用流记录保存 +const int defaultDisplayRecordSize = 100 * 1024 * 1024; //流记录保存的大小 + +typedef BOOL(WINAPI* MINIDUMPWRITEDUMP)( + HANDLE hProcess, + DWORD dwPid, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam + ); + +LONG exception_handler(struct _EXCEPTION_POINTERS* apExceptionInfo) { - IndirectDeviceContext* pContext; - - void Cleanup() - { - delete pContext; - pContext = nullptr; - } -}; -void LogQueries(const char* severity, const std::wstring& xmlName) { - if (xmlName.find(L"logging") == std::wstring::npos) { - int size_needed = WideCharToMultiByte(CP_UTF8, 0, xmlName.c_str(), (int)xmlName.size(), NULL, 0, NULL, NULL); - if (size_needed > 0) { - std::string strMessage(size_needed, 0); - WideCharToMultiByte(CP_UTF8, 0, xmlName.c_str(), (int)xmlName.size(), &strMessage[0], size_needed, NULL, NULL); - vddlog(severity, strMessage.c_str()); - } - } -} - -string WStringToString(const wstring& wstr) { //basically just a function for converting strings since codecvt is depricated in c++ 17 - if (wstr.empty()) return ""; - - int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), NULL, 0, NULL, NULL); - string str(size_needed, 0); - WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &str[0], size_needed, NULL, NULL); - return str; -} + LOG_ERROR("dump"); + if (apExceptionInfo == nullptr || apExceptionInfo->ExceptionRecord == nullptr) { + LOG_ERROR("Exception info is null."); + return EXCEPTION_CONTINUE_SEARCH; + } + PEXCEPTION_RECORD pRecord = apExceptionInfo->ExceptionRecord; // 1. 获取异常记录 + // 2. 提取核心信息 + DWORD exceptionCode = pRecord->ExceptionCode; // 错误码(如 0xC0000005 是内存访问违例) + PVOID exceptionAddress = pRecord->ExceptionAddress; // 崩溃发生的内存地址 + // 3. 打印到日志 + LOG_ERROR("Crash Detected! ExceptionCode: 0x%X, ExceptionAddress: %p", + exceptionCode, exceptionAddress); + // 如果是内存访问违例(Access Violation),还可以抓取是读冲突还是写冲突 + if (exceptionCode == EXCEPTION_ACCESS_VIOLATION && pRecord->NumberParameters >= 2) { + LOG_ERROR("Access Violation Details: %s address %p", + (pRecord->ExceptionInformation[0] ? "Write to" : "Read from"), pRecord->ExceptionInformation[1]); + } +#ifdef UNICODE + const auto mhLib = LoadLibrary(L"dbghelp.dll"); +#else + const auto mhLib = LoadLibrary("dbghelp.dll"); +#endif // !UNICODE + if (NULL == mhLib) { + return EXCEPTION_CONTINUE_SEARCH; + } + const auto pDump = reinterpret_cast(GetProcAddress(mhLib, "MiniDumpWriteDump")); + if (NULL == pDump) { + return EXCEPTION_CONTINUE_SEARCH; + } -bool EnabledQuery(const std::wstring& settingKey) { - auto it = SettingsQueryMap.find(settingKey); - if (it == SettingsQueryMap.end()) { - vddlog("e", "requested data not found in xml, consider updating xml!"); - return false; - } - - std::wstring regName = it->second.first; - std::wstring xmlName = it->second.second; - - std::wstring settingsname = confpath + L"\\vdd_settings.xml"; - HKEY hKey; - DWORD dwValue; - DWORD dwBufferSize = sizeof(dwValue); - LONG lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MikeTheTech\\VirtualDisplayDriver", 0, KEY_READ, &hKey); - - if (lResult == ERROR_SUCCESS) { - lResult = RegQueryValueExW(hKey, regName.c_str(), NULL, NULL, (LPBYTE)&dwValue, &dwBufferSize); - if (lResult == ERROR_SUCCESS) { - RegCloseKey(hKey); - if (dwValue == 1) { - LogQueries("d", xmlName + L" - is enabled (value = 1)."); - return true; - } - else if (dwValue == 0) { - goto check_xml; - } - } - else { - LogQueries("d", xmlName + L" - Failed to retrieve value from registry. Attempting to read as string."); - wchar_t path[MAX_PATH]; - dwBufferSize = sizeof(path); - lResult = RegQueryValueExW(hKey, regName.c_str(), NULL, NULL, (LPBYTE)path, &dwBufferSize); - if (lResult == ERROR_SUCCESS) { - std::wstring logValue(path); - RegCloseKey(hKey); - if (logValue == L"true" || logValue == L"1") { - LogQueries("d", xmlName + L" - is enabled (string value)."); - return true; - } - else if (logValue == L"false" || logValue == L"0") { - goto check_xml; - } - } - RegCloseKey(hKey); - LogQueries("d", xmlName + L" - Failed to retrieve string value from registry."); - } - } - -check_xml: - CComPtr pFileStream; - HRESULT hr = SHCreateStreamOnFileEx(settingsname.c_str(), STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &pFileStream); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to create file stream for XML settings."); - return false; - } - - CComPtr pReader; - hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, nullptr); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to create XML reader."); - return false; - } - - hr = pReader->SetInput(pFileStream); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to set input for XML reader."); - return false; - } - - XmlNodeType nodeType; - const wchar_t* pwszLocalName; - bool xmlLoggingValue = false; - - while (S_OK == pReader->Read(&nodeType)) { - if (nodeType == XmlNodeType_Element) { - pReader->GetLocalName(&pwszLocalName, nullptr); - if (pwszLocalName && wcscmp(pwszLocalName, xmlName.c_str()) == 0) { - pReader->Read(&nodeType); - if (nodeType == XmlNodeType_Text) { - const wchar_t* pwszValue; - pReader->GetValue(&pwszValue, nullptr); - if (pwszValue) { - xmlLoggingValue = (wcscmp(pwszValue, L"true") == 0); - } - LogQueries("i", xmlName + (xmlLoggingValue ? L" is enabled." : L" is disabled.")); - break; - } - } - } - } - - return xmlLoggingValue; -} + const auto hFile = CreateFileA( + IOHelper::pathCombine(configPath, DUMP_FILE_NAME).c_str(), + GENERIC_WRITE, + FILE_SHARE_WRITE, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr + ); + + const DWORD flags = MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | + MiniDumpWithUnloadedModules | MiniDumpWithProcessThreadData | + MiniDumpWithFullMemoryInfo | MiniDumpWithThreadInfo | + MiniDumpWithFullAuxiliaryState | MiniDumpIgnoreInaccessibleMemory | + MiniDumpWithTokenInformation; + + if (hFile != INVALID_HANDLE_VALUE) + { + _MINIDUMP_EXCEPTION_INFORMATION ExInfo; + ExInfo.ThreadId = GetCurrentThreadId(); + ExInfo.ExceptionPointers = apExceptionInfo; + ExInfo.ClientPointers = FALSE; + + pDump( + GetCurrentProcess(), + GetCurrentProcessId(), + hFile, + (MINIDUMP_TYPE)flags, + &ExInfo, + nullptr, + nullptr + ); + CloseHandle(hFile); + } -int GetIntegerSetting(const std::wstring& settingKey) { - auto it = SettingsQueryMap.find(settingKey); - if (it == SettingsQueryMap.end()) { - vddlog("e", "requested data not found in xml, consider updating xml!"); - return -1; - } - - std::wstring regName = it->second.first; - std::wstring xmlName = it->second.second; - - std::wstring settingsname = confpath + L"\\vdd_settings.xml"; - HKEY hKey; - DWORD dwValue; - DWORD dwBufferSize = sizeof(dwValue); - LONG lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MikeTheTech\\VirtualDisplayDriver", 0, KEY_READ, &hKey); - - if (lResult == ERROR_SUCCESS) { - lResult = RegQueryValueExW(hKey, regName.c_str(), NULL, NULL, (LPBYTE)&dwValue, &dwBufferSize); - if (lResult == ERROR_SUCCESS) { - RegCloseKey(hKey); - LogQueries("d", xmlName + L" - Retrieved integer value: " + std::to_wstring(dwValue)); - return static_cast(dwValue); - } - else { - LogQueries("d", xmlName + L" - Failed to retrieve integer value from registry. Attempting to read as string."); - wchar_t path[MAX_PATH]; - dwBufferSize = sizeof(path); - lResult = RegQueryValueExW(hKey, regName.c_str(), NULL, NULL, (LPBYTE)path, &dwBufferSize); - RegCloseKey(hKey); - if (lResult == ERROR_SUCCESS) { - try { - int logValue = std::stoi(path); - LogQueries("d", xmlName + L" - Retrieved string value: " + std::to_wstring(logValue)); - return logValue; - } - catch (const std::exception&) { - LogQueries("d", xmlName + L" - Failed to convert registry string value to integer."); - } - } - } - } - - CComPtr pFileStream; - HRESULT hr = SHCreateStreamOnFileEx(settingsname.c_str(), STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &pFileStream); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to create file stream for XML settings."); - return -1; - } - - CComPtr pReader; - hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, nullptr); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to create XML reader."); - return -1; - } - - hr = pReader->SetInput(pFileStream); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to set input for XML reader."); - return -1; - } - - XmlNodeType nodeType; - const wchar_t* pwszLocalName; - int xmlLoggingValue = -1; - - while (S_OK == pReader->Read(&nodeType)) { - if (nodeType == XmlNodeType_Element) { - pReader->GetLocalName(&pwszLocalName, nullptr); - if (pwszLocalName && wcscmp(pwszLocalName, xmlName.c_str()) == 0) { - pReader->Read(&nodeType); - if (nodeType == XmlNodeType_Text) { - const wchar_t* pwszValue; - pReader->GetValue(&pwszValue, nullptr); - if (pwszValue) { - try { - xmlLoggingValue = std::stoi(pwszValue); - LogQueries("i", xmlName + L" - Retrieved from XML: " + std::to_wstring(xmlLoggingValue)); - } - catch (const std::exception&) { - LogQueries("d", xmlName + L" - Failed to convert XML string value to integer."); - } - } - break; - } - } - } - } - - return xmlLoggingValue; + return EXCEPTION_CONTINUE_SEARCH; } -std::wstring GetStringSetting(const std::wstring& settingKey) { - auto it = SettingsQueryMap.find(settingKey); - if (it == SettingsQueryMap.end()) { - vddlog("e", "requested data not found in xml, consider updating xml!"); - return L""; - } - - std::wstring regName = it->second.first; - std::wstring xmlName = it->second.second; - - std::wstring settingsname = confpath + L"\\vdd_settings.xml"; - HKEY hKey; - DWORD dwBufferSize = MAX_PATH; - wchar_t buffer[MAX_PATH]; - - LONG lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MikeTheTech\\VirtualDisplayDriver", 0, KEY_READ, &hKey); - if (lResult == ERROR_SUCCESS) { - lResult = RegQueryValueExW(hKey, regName.c_str(), NULL, NULL, (LPBYTE)buffer, &dwBufferSize); - RegCloseKey(hKey); - - if (lResult == ERROR_SUCCESS) { - LogQueries("d", xmlName + L" - Retrieved string value from registry: " + buffer); - return std::wstring(buffer); - } - else { - LogQueries("d", xmlName + L" - Failed to retrieve string value from registry. Attempting to read as XML."); - } - } - - CComPtr pFileStream; - HRESULT hr = SHCreateStreamOnFileEx(settingsname.c_str(), STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &pFileStream); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to create file stream for XML settings."); - return L""; - } - - CComPtr pReader; - hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, nullptr); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to create XML reader."); - return L""; - } - - hr = pReader->SetInput(pFileStream); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to set input for XML reader."); - return L""; - } - - XmlNodeType nodeType; - const wchar_t* pwszLocalName; - std::wstring xmlLoggingValue = L""; - - while (S_OK == pReader->Read(&nodeType)) { - if (nodeType == XmlNodeType_Element) { - pReader->GetLocalName(&pwszLocalName, nullptr); - if (pwszLocalName && wcscmp(pwszLocalName, xmlName.c_str()) == 0) { - pReader->Read(&nodeType); - if (nodeType == XmlNodeType_Text) { - const wchar_t* pwszValue; - pReader->GetValue(&pwszValue, nullptr); - if (pwszValue) { - xmlLoggingValue = pwszValue; - } - LogQueries("i", xmlName + L" - Retrieved from XML: " + xmlLoggingValue); - break; - } - } - } - } - - return xmlLoggingValue; -} +enum { + STREAM_TYPE_HEAD = 1, + STREAM_TYPE_CONFIG = 2, + STREAM_TYPE_POS = 3, + STREAM_TYPE_BUFFER = 4, + STREAM_TYPE_LOG = 5, +}; -double GetDoubleSetting(const std::wstring& settingKey) { - auto it = SettingsQueryMap.find(settingKey); - if (it == SettingsQueryMap.end()) { - vddlog("e", "requested data not found in xml, consider updating xml!"); - return 0.0; - } - - std::wstring regName = it->second.first; - std::wstring xmlName = it->second.second; - - std::wstring settingsname = confpath + L"\\vdd_settings.xml"; - HKEY hKey; - DWORD dwBufferSize = MAX_PATH; - wchar_t buffer[MAX_PATH]; - - LONG lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MikeTheTech\\VirtualDisplayDriver", 0, KEY_READ, &hKey); - if (lResult == ERROR_SUCCESS) { - lResult = RegQueryValueExW(hKey, regName.c_str(), NULL, NULL, (LPBYTE)buffer, &dwBufferSize); - if (lResult == ERROR_SUCCESS) { - RegCloseKey(hKey); - try { - double regValue = std::stod(buffer); - LogQueries("d", xmlName + L" - Retrieved from registry: " + std::to_wstring(regValue)); - return regValue; - } - catch (const std::exception&) { - LogQueries("d", xmlName + L" - Failed to convert registry value to double."); - } - } - else { - RegCloseKey(hKey); - } - } - - CComPtr pFileStream; - HRESULT hr = SHCreateStreamOnFileEx(settingsname.c_str(), STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &pFileStream); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to create file stream for XML settings."); - return 0.0; - } - - CComPtr pReader; - hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, nullptr); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to create XML reader."); - return 0.0; - } - - hr = pReader->SetInput(pFileStream); - if (FAILED(hr)) { - LogQueries("d", xmlName + L" - Failed to set input for XML reader."); - return 0.0; - } - - XmlNodeType nodeType; - const wchar_t* pwszLocalName; - double xmlLoggingValue = 0.0; - - while (S_OK == pReader->Read(&nodeType)) { - if (nodeType == XmlNodeType_Element) { - pReader->GetLocalName(&pwszLocalName, nullptr); - if (pwszLocalName && wcscmp(pwszLocalName, xmlName.c_str()) == 0) { - pReader->Read(&nodeType); - if (nodeType == XmlNodeType_Text) { - const wchar_t* pwszValue; - pReader->GetValue(&pwszValue, nullptr); - if (pwszValue) { - try { - xmlLoggingValue = std::stod(pwszValue); - LogQueries("i", xmlName + L" - Retrieved from XML: " + std::to_wstring(xmlLoggingValue)); - } - catch (const std::exception&) { - LogQueries("d", xmlName + L" - Failed to convert XML value to double."); - } - } - break; - } - } - } - } - - return xmlLoggingValue; -} +const int SIGN_HEAD_LEN = 1024; +const int SIGN_INFO_CNT = 3; -// === EDID PROFILE LOADING FUNCTION === -struct EdidProfileData { - vector> modes; - bool hdr10Supported = false; - bool dolbyVisionSupported = false; - bool hdr10PlusSupported = false; - double maxLuminance = 0.0; - double minLuminance = 0.0; - wstring primaryColorSpace = L"sRGB"; - double gamma = 2.2; - double redX = 0.64, redY = 0.33; - double greenX = 0.30, greenY = 0.60; - double blueX = 0.15, blueY = 0.06; - double whiteX = 0.3127, whiteY = 0.3290; - int preferredWidth = 1920; - int preferredHeight = 1080; - double preferredRefresh = 60.0; -}; +const int STREAM_HEAD_LEN = 1024; +const int STREAM_CONFIG_LEN = 2048; +const int STREAM_POS_LEN = 1024; +const int FRAME_POS_CNT = 8; +const int STREAM_LOG_LEN = 4096; +const int STREAM_LOG_INFO_CNT = 30; -// === COLOR SPACE AND GAMMA STRUCTURES === -struct VddColorMatrix { - FLOAT matrix[3][4] = {}; // 3x4 color space transformation matrix - zero initialized - bool isValid = false; +struct SignInfo { + int64_t display_cnt; + int64_t ts; + uint8_t sign[32]; }; -struct VddGammaRamp { - FLOAT gamma = 2.2f; - wstring colorSpace; - VddColorMatrix matrix = {}; - bool useMatrix = false; - bool isValid = false; -}; +struct Sign { + int32_t typ; + int32_t len; -// === GAMMA AND COLOR SPACE STORAGE === -std::map g_GammaRampStore; + int32_t sign_cnt; + int32_t sign_len; + int64_t sign_idx; -// === COLOR SPACE AND GAMMA CONVERSION FUNCTIONS === + SignInfo sign[3]; +}; -// Convert gamma value to 3x4 color space transformation matrix -VddColorMatrix ConvertGammaToMatrix(double gamma, const wstring& colorSpace) { - VddColorMatrix matrix = {}; - - // Identity matrix as base - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 4; j++) { - matrix.matrix[i][j] = (i == j) ? 1.0f : 0.0f; - } +int64_t g_freq_per_us; +uint64_t getUs() { + LARGE_INTEGER ticks; + if (!QueryPerformanceCounter(&ticks)) + { + return 0; } - - // Apply gamma correction to diagonal elements - float gammaValue = static_cast(gamma); - - if (colorSpace == L"sRGB") { - // sRGB gamma correction (2.2) - matrix.matrix[0][0] = gammaValue / 2.2f; // Red - matrix.matrix[1][1] = gammaValue / 2.2f; // Green - matrix.matrix[2][2] = gammaValue / 2.2f; // Blue + //auto a = ticks.QuadPart; + //LOG_INFO("a=%lld, freq=%lld tt=%lld", a, g_freq_per_us, a / g_freq_per_us); + return ticks.QuadPart / g_freq_per_us; + //return GetTickCount64(); +} + +HANDLE initResetEvent(void** addr) { + SECURITY_DESCRIPTOR secutityDese; + InitializeSecurityDescriptor(&secutityDese, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&secutityDese, TRUE, NULL, FALSE); + SECURITY_ATTRIBUTES securityAttr; + securityAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + securityAttr.bInheritHandle = FALSE; + securityAttr.lpSecurityDescriptor = &secutityDese; + + auto sign_name = DISPLAY_SIGN_NAME; + auto sign_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, &securityAttr, PAGE_READWRITE | SEC_COMMIT, 0, SIGN_HEAD_LEN, sign_name); + if (!sign_handle) { + LOG_ERROR("create sign memory %s error: 0x%08x", sign_name, GetLastError()); + return NULL; } - else if (colorSpace == L"DCI-P3") { - // DCI-P3 color space transformation with gamma - // P3 to sRGB matrix with gamma correction - matrix.matrix[0][0] = 1.2249f * (gammaValue / 2.4f); - matrix.matrix[0][1] = -0.2247f; - matrix.matrix[0][2] = 0.0f; - matrix.matrix[1][0] = -0.0420f; - matrix.matrix[1][1] = 1.0419f * (gammaValue / 2.4f); - matrix.matrix[1][2] = 0.0f; - matrix.matrix[2][0] = -0.0196f; - matrix.matrix[2][1] = -0.0786f; - matrix.matrix[2][2] = 1.0982f * (gammaValue / 2.4f); + auto shmen_addr = MapViewOfFile(sign_handle, FILE_MAP_ALL_ACCESS, 0, 0, SIGN_HEAD_LEN); + if (!shmen_addr) { + LOG_ERROR("map sign memory %s error: 0x%08x", sign_name, GetLastError()); + return NULL; } - else if (colorSpace == L"Rec.2020") { - // Rec.2020 to sRGB matrix with gamma correction - matrix.matrix[0][0] = 1.7347f * (gammaValue / 2.4f); - matrix.matrix[0][1] = -0.7347f; - matrix.matrix[0][2] = 0.0f; - matrix.matrix[1][0] = -0.1316f; - matrix.matrix[1][1] = 1.1316f * (gammaValue / 2.4f); - matrix.matrix[1][2] = 0.0f; - matrix.matrix[2][0] = -0.0241f; - matrix.matrix[2][1] = -0.1289f; - matrix.matrix[2][2] = 1.1530f * (gammaValue / 2.4f); + memset(shmen_addr, 0, SIGN_HEAD_LEN); + *addr = shmen_addr; + + auto sign = (Sign*)shmen_addr; + sign->typ = 1; + sign->len = SIGN_HEAD_LEN; + sign->sign_cnt = SIGN_INFO_CNT; + sign->sign_len = sizeof(SignInfo); + + return CreateEventA(&securityAttr, false, false, RESET_EVENT_NAME); +} + +void getSignKey(unsigned char key[32]) { + unsigned char key1[8] = { '2', '0', '2', '6', '0', '7', '1', '0' }; + unsigned char key2[8] = { 'N', 'z', 'T', 'V', 'd', 'd', 'N', 'z' }; + unsigned char key3[8] = { 't', 'T', 'e', 'a', 'm', 'w', 'o', 'r' }; + unsigned char key4[8] = { 'k', '!', '%', '&', '*', '$', '@', '^' }; + for (int i = 0; i < 8; i++) { + auto idx = i * 4; + key[idx + 0] = key1[i]; + key[idx + 1] = key2[i]; + key[idx + 2] = key3[i]; + key[idx + 3] = key4[i]; } - else if (colorSpace == L"Adobe_RGB") { - // Adobe RGB with gamma correction - matrix.matrix[0][0] = 1.0f * (gammaValue / 2.2f); - matrix.matrix[1][1] = 1.0f * (gammaValue / 2.2f); - matrix.matrix[2][2] = 1.0f * (gammaValue / 2.2f); - } - else { - // Default to sRGB for unknown color spaces - matrix.matrix[0][0] = gammaValue / 2.2f; - matrix.matrix[1][1] = gammaValue / 2.2f; - matrix.matrix[2][2] = gammaValue / 2.2f; - } - - matrix.isValid = true; - return matrix; } -// Convert EDID profile to gamma ramp -VddGammaRamp ConvertEdidToGammaRamp(const EdidProfileData& profile) { - VddGammaRamp gammaRamp = {}; - - gammaRamp.gamma = static_cast(profile.gamma); - gammaRamp.colorSpace = profile.primaryColorSpace; - - // Generate matrix if matrix transforms are enabled - if (enableMatrixTransform) { - gammaRamp.matrix = ConvertGammaToMatrix(profile.gamma, profile.primaryColorSpace); - gammaRamp.useMatrix = gammaRamp.matrix.isValid; +UINT getSignDisplayCount(void* addr) { + auto sign = (Sign*)addr; + auto idx = sign->sign_idx % SIGN_INFO_CNT; + auto sign_info = sign->sign[idx]; + if (sign_info.display_cnt < 0) { + return 0; } - - gammaRamp.isValid = colorSpaceEnabled; - - return gammaRamp; -} + LOG_INFO("idx=%d cnt=%d, ts=%lld", idx, sign_info.display_cnt, sign_info.ts); -// Convert manual settings to gamma ramp -VddGammaRamp ConvertManualToGammaRamp() { - VddGammaRamp gammaRamp = {}; - - gammaRamp.gamma = static_cast(gammaCorrection); - gammaRamp.colorSpace = primaryColorSpace; - - // Generate matrix if matrix transforms are enabled - if (enableMatrixTransform) { - gammaRamp.matrix = ConvertGammaToMatrix(gammaCorrection, primaryColorSpace); - gammaRamp.useMatrix = gammaRamp.matrix.isValid; + auto ts = _time64(NULL); + if (sign_info.ts < (ts - 60) || sign_info.ts >(ts + 60)) { + //LOG_INFO("sign ts invalid"); + return 0; } - - gammaRamp.isValid = colorSpaceEnabled; - - return gammaRamp; + + return (UINT)sign_info.display_cnt; } +#pragma region log +const char* debug = "DEBUG"; +const char* info = "INFO"; +const char* warn = "WARN"; +const char* error = "ERROR"; -// Enhanced color format selection based on color space -IDDCX_BITS_PER_COMPONENT SelectBitDepthFromColorSpace(const wstring& colorSpace) { - if (autoSelectFromColorSpace) { - if (colorSpace == L"Rec.2020") { - return IDDCX_BITS_PER_COMPONENT_10; // HDR10 - 10-bit for wide color gamut - } else if (colorSpace == L"DCI-P3") { - return IDDCX_BITS_PER_COMPONENT_10; // Wide color gamut - 10-bit - } else if (colorSpace == L"Adobe_RGB") { - return IDDCX_BITS_PER_COMPONENT_10; // Professional - 10-bit - } else { - return IDDCX_BITS_PER_COMPONENT_8; // sRGB - 8-bit +void write_log(const char* level, const char* func, int line, const char* format, ...) +{ + if (level == debug && logLevel < (int)LogLevel::Debug) + return; + else if (level == info && logLevel < (int)LogLevel::Info) + return; + else if (level == warn && logLevel < (int)LogLevel::Warning) + return; + else if (level == error && logLevel < (int)LogLevel::Error) + return; + + FILE* fp = NULL; + EnterCriticalSection(&logSec); + if (fopen_s(&fp, logFile.c_str(), "ab+, ccs=UTF-8") == 0 && fp != NULL) { +// if (_wfopen_s(&fp, logFile.c_str(), L"ab+, ccs=UTF-8") == 0 && fp != NULL) { + char msg[1024]; + va_list args; + va_start(args, format); + vsnprintf_s(msg, sizeof(msg) - 1, format, args); + va_end(args); + + // 获取当前时间点 + auto currentTime = std::chrono::system_clock::now(); + // 2. 将时间点转换为 time_t(精确到秒) + std::time_t now = std::chrono::system_clock::to_time_t(currentTime); + struct tm t; + localtime_s(&t, &now); + + auto ts = currentTime.time_since_epoch(); + auto ms = static_cast(std::chrono::duration_cast(ts).count() % 1000); + fprintf_s(fp, "[%04d-%02d-%02d %02d:%02d:%02d,%03d][%s][proId:%d][threadId:%d][func:%s][line:%d]: %s\r\n", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, ms, + level, GetCurrentProcessId(), GetCurrentThreadId(), func, line, msg); + auto file_len = ftell(fp); + fclose(fp); + if (logMaxSize > 0 && logMaxSize <= file_len) { + IOHelper::backupFile(logFile, logsPath); +// IOHelper::backupFile(WStringToString(logFile), logsPath); } } - - // Manual bit depth override - if (forceBitDepth == L"8") { - return IDDCX_BITS_PER_COMPONENT_8; - } else if (forceBitDepth == L"10") { - return IDDCX_BITS_PER_COMPONENT_10; - } else if (forceBitDepth == L"12") { - return IDDCX_BITS_PER_COMPONENT_12; - } - - // Default to existing color depth logic - return HDRPlus ? IDDCX_BITS_PER_COMPONENT_12 : - (SDR10 ? IDDCX_BITS_PER_COMPONENT_10 : IDDCX_BITS_PER_COMPONENT_8); + LeaveCriticalSection(&logSec); } -// === SMPTE ST.2086 HDR METADATA STRUCTURE === -struct VddHdrMetadata { - // SMPTE ST.2086 Display Primaries (scaled 0-50000) - zero initialized - UINT16 display_primaries_x[3] = {}; // R, G, B chromaticity x coordinates - UINT16 display_primaries_y[3] = {}; // R, G, B chromaticity y coordinates - UINT16 white_point_x = 0; // White point x coordinate - UINT16 white_point_y = 0; // White point y coordinate - - // Luminance values (0.0001 cd/m² units for SMPTE ST.2086) - UINT32 max_display_mastering_luminance = 0; - UINT32 min_display_mastering_luminance = 0; - - // Content light level (nits) - UINT16 max_content_light_level = 0; - UINT16 max_frame_avg_light_level = 0; - - // Validation flag - bool isValid = false; -}; - -// === HDR METADATA STORAGE === -std::map g_HdrMetadataStore; - -// === HDR METADATA CONVERSION FUNCTIONS === +#pragma endregion -// Convert EDID chromaticity (0.0-1.0) to SMPTE ST.2086 format (0-50000) -UINT16 ConvertChromaticityToSmpte(double edidValue) { - // Clamp to valid range - if (edidValue < 0.0) edidValue = 0.0; - if (edidValue > 1.0) edidValue = 1.0; - - return static_cast(edidValue * 50000.0); -} +#pragma region init +/* +// 1. 定义一个标准的 128 字节基础 EDID 模板(这里以一个通用 1080p 显示器为例) +// 注意:第 127 字节是校验和,我们在代码里动态计算,这里先填 0x00 +unsigned char g_BaseEdidTemplate[128] = { +0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, // 0-7: 固定的 EDID 头部 +0x4C, 0x2D, // 8-9: 厂商代码 (样例: SEC) +0x00, 0x00, // 10-11: 产品代码 (将在代码中动态修改) +0x00, 0x00, 0x00, 0x00, // 12-15: 32位序列号 (将在代码中动态修改) +0x01, 0x1E, // 16-17: 制造周、年份 +0x01, 0x04, // 18-19: EDID 版本版本号 (1.4) +0xA5, 0x34, 0x20, 0x78, 0x22, 0xEE, 0x95, 0xA3, // 20-27: 图像显示参数与特性 +0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, 0xA1, // 28-35: 颜色特征与基础时序 +0x08, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 36-43: 标准时序识别码 +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 44-51: 标准时序识别码 +0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, // 52-59: 详细时序描述符 (1920x1080 60Hz) +0x58, 0x2C, 0x45, 0x00, 0xDD, 0x0C, 0x11, 0x00, // 60-67: +0x00, 0x1E, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x32, // 68-75: 显示器范围限制 +0x3C, 0x1F, 0x50, 0x11, 0x00, 0x0A, 0x20, 0x20, // 76-83: +0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, // 84-91: 显示器名称描述符 +0x00, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6C, // 92-99: "Virtual" +0x44, 0x69, 0x73, 0x70, 0x0A, 0x20, 0x00, 0x00, // 100-107: "Disp" +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 108-115: 空白描述符 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 116-123: +0x00, 0x00, 0x00, // 124-126: +0x00 // 127: 校验和占位符 (将在代码中动态计算 🔴) +}; +*/ +const BYTE defaultEdid[256] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, // 0-7: 固定的 EDID 头部 + 0x0C, 0x64, // 8-9: 厂商代码 (样例: SEC) + 0x37, 0x1A, // 10-11: 产品代码 (将在代码中动态修改) + 0x15, 0xcd, 0x5b, 0x07, // 12-15: 32位序列号 (将在代码中动态修改) + 0x18, 0x1A, // 16-17: 制造周、年份 + 0x01, 0x04, // 18-19: EDID 版本版本号 (1.4) + 0xb2, 0x3c, 0x22, 0x78, 0x04, 0xe6, 0xc5, 0xad, // 20-27: 图像显示参数与特性 + 0x4e, 0x43, 0xaa, 0x26, 0x0b, 0x50, 0x54, 0x01, // 28-35: 颜色特征与基础时序 + 0x40, 0x00, 0xd1, 0xc0, 0x95, 0xc0, 0xd1, 0xcf, // 36-43: 标准时序识别码 + 0x95, 0xcf, 0xd1, 0xde, 0x95, 0xde, 0xd1, 0xfc, // 44-51: 标准时序识别码 + 0x95, 0xfc, 0x4d, 0xd0, 0x00, 0xa0, 0xf0, 0x70, // 52-59: 详细时序描述符 (1920x1080 60Hz) + 0x3e, 0x80, 0x30, 0x20, 0x35, 0x00, 0x80, 0x88, // 60-67: + 0x42, 0x00, 0x00, 0x18, 0x5a, 0x87, 0x80, 0xa0, // 68-75: 显示器范围限制 + 0x70, 0x38, 0x4d, 0x40, 0x30, 0x20, 0x35, 0x00, // 76-83: + 0x40, 0x44, 0x21, 0x00, 0x00, 0x18, 0x09, 0xec, // 84-91: 显示器名称描述符 + 0x00, 0xa0, 0xa0, 0xa0, 0x67, 0x50, 0x30, 0x20, // 92-99: "Virtual" + 0x35, 0x00, 0x00, 0xb0, 0x31, 0x00, 0x00, 0x18, // 100-107: "Disp" + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x43, 'g', 'T', // 108-115: 空白描述符 + 'w', 'V', 'd', 'd', 0x0a, 0x20, 0x20, 0x20, // 116-123: + 0x20, 0x20, 0x00, + 0x00, +}; -// Convert EDID luminance (nits) to SMPTE ST.2086 format (0.0001 cd/m² units) -UINT32 ConvertLuminanceToSmpte(double nits) { - // Clamp to reasonable range (0.0001 to 10000 nits) - if (nits < 0.0001) nits = 0.0001; - if (nits > 10000.0) nits = 10000.0; - - return static_cast(nits * 10000.0); +//void modifyEdid(char* edid,UINT displayIndex) { +// edid[8] = 0x0C; +// edid[9] = 0x64; +// edid[10] = 0x37+displayIndex ;//product_id 低位 +// edid[11] = 0x1A; //product_id 高位 +//} + +BYTE calculateChecksum(const char* edid) { + int sum = 0; + for (int i = 0; i < 127; ++i) { + sum += edid[i]; + } + sum %= 256; + if (sum != 0) { + sum = 256 - sum; + } + return static_cast(sum); } -// Convert EDID profile data to SMPTE ST.2086 HDR metadata -VddHdrMetadata ConvertEdidToSmpteMetadata(const EdidProfileData& profile) { - VddHdrMetadata metadata = {}; - - // Convert chromaticity coordinates - metadata.display_primaries_x[0] = ConvertChromaticityToSmpte(profile.redX); // Red - metadata.display_primaries_y[0] = ConvertChromaticityToSmpte(profile.redY); - metadata.display_primaries_x[1] = ConvertChromaticityToSmpte(profile.greenX); // Green - metadata.display_primaries_y[1] = ConvertChromaticityToSmpte(profile.greenY); - metadata.display_primaries_x[2] = ConvertChromaticityToSmpte(profile.blueX); // Blue - metadata.display_primaries_y[2] = ConvertChromaticityToSmpte(profile.blueY); - - // Convert white point - metadata.white_point_x = ConvertChromaticityToSmpte(profile.whiteX); - metadata.white_point_y = ConvertChromaticityToSmpte(profile.whiteY); - - // Convert luminance values - metadata.max_display_mastering_luminance = ConvertLuminanceToSmpte(profile.maxLuminance); - metadata.min_display_mastering_luminance = ConvertLuminanceToSmpte(profile.minLuminance); - - // Use configured content light levels (from vdd_settings.xml) - metadata.max_content_light_level = static_cast(maxContentLightLevel); - metadata.max_frame_avg_light_level = static_cast(maxFrameAvgLightLevel); - - // Mark as valid if we have HDR10 support - metadata.isValid = profile.hdr10Supported && hdr10StaticMetadataEnabled; - - return metadata; -} +/// +/// 默认分辨率 +/// +const SIZE defaultDisplayResolution[] = +{ + // 8k + //{ 7680, 5760 }, // 4:3 WHUSXGA + //{ 7680, 4800 }, // 16:10 WHUXGA + //{ 7680, 4320 }, // 16:9 8K UHD + // 5k + //{ 5120, 4096 }, // 5:4 HSXGA + //{ 5120, 3840 }, // 4:3 WHSXGA + //{ 5120, 3200 }, // 16:10 WHXGA + //{ 5120, 2880 }, // 16:9 UHD++ + //{ 5120, 2160 }, // 21:9 5K UHD + // 4k + //{ 4096, 3072 }, // 4:3 HXGA + //{ 4096, 2160 }, // 19:10 DCI 4K + //{ 3840, 2880 }, // 4:3 WQUSXGA + //{ 3840, 2400 }, // 16:10 WQUXGA + { 3840, 2160 }, // 16:9 4K UHD +// { 3840, 1600 }, // 12:5 UW4K + // 2.5k + //{ 3000, 2000 }, // 3:2 : matebook x pro + //{ 2560, 2048 }, // 5:4 USXGA +// { 2560, 1600 }, // 16:10 UWXGA + { 2560, 1440 }, // 16:9 QHD/WQHD: retina macbook 13 pro +// { 2560, 1080 }, // 21:9 CINEMA 21:9 + //{ 2160, 1440 }, // 3:2 : matebook 13/14 + // 2k +// { 2048, 1536 }, // 4:3 SUVGA(QXGA) +// { 2048, 1280 }, // 16:10 DCI 2K +// { 2048, 1152 }, // 16:9 QWXGA +// { 1920, 1440 }, // 4:3 WUSXGA +// { 1920, 1200 }, // 16:10 WUXGA + { 1920, 1080 }, // 16:9 WSUVGA+(WSUGA/HDTV) + // 1600 +// { 1680, 1050 }, // 16:10 WSXGA+ +// { 1600, 1200 }, // 4:3 USVGA/UXGA/UGA +// { 1600, 1024 }, // 25:16 WSXGA +// { 1600, 900 }, // 16:9 HD+ + // 1440 +// { 1440, 960 }, // 3:2 FWXGA+ +// { 1440, 900 }, // 16:10 WXGA+ : macbook 13 air + { 1366, 768 }, // 16:9 FWXGA + // 1280 +// { 1280, 1024 }, // 5:4 SXGA +// { 1280, 960 }, // 4:3 SXGA-/UVGA +// { 1280, 800 }, // 16:10 WXGA +// { 1280, 768 }, // 15:9 WXGA + { 1280, 720 }, // 16:9 WXGA/HD + { 1024, 768 }, // 4:3 XGA + { 800, 600 } // 4:3 SVGA +}; -// Convert manual settings to SMPTE ST.2086 HDR metadata -VddHdrMetadata ConvertManualToSmpteMetadata() { - VddHdrMetadata metadata = {}; - - // Convert manual chromaticity coordinates - metadata.display_primaries_x[0] = ConvertChromaticityToSmpte(redX); // Red - metadata.display_primaries_y[0] = ConvertChromaticityToSmpte(redY); - metadata.display_primaries_x[1] = ConvertChromaticityToSmpte(greenX); // Green - metadata.display_primaries_y[1] = ConvertChromaticityToSmpte(greenY); - metadata.display_primaries_x[2] = ConvertChromaticityToSmpte(blueX); // Blue - metadata.display_primaries_y[2] = ConvertChromaticityToSmpte(blueY); - - // Convert manual white point - metadata.white_point_x = ConvertChromaticityToSmpte(whiteX); - metadata.white_point_y = ConvertChromaticityToSmpte(whiteY); - - // Convert manual luminance values - metadata.max_display_mastering_luminance = ConvertLuminanceToSmpte(maxDisplayMasteringLuminance); - metadata.min_display_mastering_luminance = ConvertLuminanceToSmpte(minDisplayMasteringLuminance); - - // Use configured content light levels - metadata.max_content_light_level = static_cast(maxContentLightLevel); - metadata.max_frame_avg_light_level = static_cast(maxFrameAvgLightLevel); - - // Mark as valid if HDR10 metadata is enabled and color primaries are enabled - metadata.isValid = hdr10StaticMetadataEnabled && colorPrimariesEnabled; - - return metadata; -} -// === ENHANCED MODE MANAGEMENT FUNCTIONS === +/// +/// 默认的显示刷新率 +/// +const UINT defaultDisplayFps[] = +{ + 60 +}; -// Generate modes from EDID with advanced filtering and optimization -vector> GenerateModesFromEdid(const EdidProfileData& profile) { - vector> generatedModes; - - if (!autoResolutionsEnabled) { - vddlog("i", "Auto resolutions disabled, skipping EDID mode generation"); - return generatedModes; +bool parseSizePerLine(const char* line, SIZE& size) +{ + size.cx = atoi(line); + if (size.cx == 0) { + return false; } - - for (const auto& mode : profile.modes) { - int width = get<0>(mode); - int height = get<1>(mode); - int refreshRateMultiplier = get<2>(mode); - int nominalRefreshRate = get<3>(mode); - - // Apply comprehensive filtering - bool passesFilter = true; - - // Resolution range filtering - if (width < minResolutionWidth || width > maxResolutionWidth || - height < minResolutionHeight || height > maxResolutionHeight) { - passesFilter = false; - } - - // Refresh rate filtering - if (nominalRefreshRate < minRefreshRate || nominalRefreshRate > maxRefreshRate) { - passesFilter = false; - } - - // Fractional rate filtering - if (excludeFractionalRates && refreshRateMultiplier != 1000) { - passesFilter = false; - } - - // Add custom quality filtering - if (passesFilter) { - // Prefer standard aspect ratios for better compatibility - double aspectRatio = static_cast(width) / height; - bool isStandardAspect = (abs(aspectRatio - 16.0/9.0) < 0.01) || // 16:9 - (abs(aspectRatio - 16.0/10.0) < 0.01) || // 16:10 - (abs(aspectRatio - 4.0/3.0) < 0.01) || // 4:3 - (abs(aspectRatio - 21.0/9.0) < 0.01); // 21:9 - - // Log non-standard aspect ratios for information - if (!isStandardAspect) { - stringstream ss; - ss << "Including non-standard aspect ratio mode: " << width << "x" << height - << " (ratio: " << fixed << setprecision(2) << aspectRatio << ")"; - vddlog("d", ss.str().c_str()); - } - - generatedModes.push_back(mode); - } + line = strchr(line, 'x'); + if (line == NULL) { + return false; + } + line++; + size.cy = atoi(line); + if (size.cy == 0) { + return false; } - - // Sort modes by preference (resolution, then refresh rate) - sort(generatedModes.begin(), generatedModes.end(), - [](const tuple& a, const tuple& b) { - // Primary sort: resolution (area) - int areaA = get<0>(a) * get<1>(a); - int areaB = get<0>(b) * get<1>(b); - if (areaA != areaB) return areaA > areaB; // Larger resolution first - - // Secondary sort: refresh rate - return get<3>(a) > get<3>(b); // Higher refresh rate first - }); - - stringstream ss; - ss << "Generated " << generatedModes.size() << " modes from EDID (filtered from " << profile.modes.size() << " total)"; - vddlog("i", ss.str().c_str()); - - return generatedModes; + return true; } -// Find and validate preferred mode from EDID -tuple FindPreferredModeFromEdid(const EdidProfileData& profile, - const vector>& availableModes) { - // Default fallback mode - tuple preferredMode = make_tuple(fallbackWidth, fallbackHeight, 1000, fallbackRefresh); - - if (!useEdidPreferred) { - vddlog("i", "EDID preferred mode disabled, using fallback"); - return preferredMode; - } - - // Look for EDID preferred mode in available modes - for (const auto& mode : availableModes) { - if (get<0>(mode) == profile.preferredWidth && - get<1>(mode) == profile.preferredHeight) { - // Found matching resolution, use it - preferredMode = mode; - - stringstream ss; - ss << "Found EDID preferred mode: " << profile.preferredWidth << "x" << profile.preferredHeight - << "@" << get<3>(mode) << "Hz"; - vddlog("i", ss.str().c_str()); - break; +void init() { + InitializeCriticalSection(&logSec); + int level=readFromRegWord(L"level"); + if (level != -1) + logLevel = level; + auto path = readFromRegString(L"path"); + if (!path.empty()) { + IOHelper::checkAndCreateDirectory(configPath);//创建旧路径,可以用于甄别驱动是否进入加载逻辑 + configPath = WStringToString(path); + LOG_DEBUG("从注册表配置读取到新的配置路径===>%s", configPath.c_str()); + if (IOHelper::checkAndCreateDirectory(path)) {//如果目录不存在,则创建目录 + LOG_DEBUG("新路径创建成功!"); + logFile = IOHelper::pathCombine(configPath, LOG_FILE_NAME); } } - - return preferredMode; + IOHelper::checkAndCreateDirectory(configPath);//如果目录不存在,则创建目录 + //logFile = StringToWString( IOHelper::pathCombine(configPath, logFilename)); + logFile = IOHelper::pathCombine(configPath, LOG_FILE_NAME); + LOG_DEBUG("日志创建成功!"); + + logsPath = IOHelper::pathCombine(configPath, "logs"); + IOHelper::checkAndCreateDirectory(logsPath);//生成日志的备份目录 + LOG_DEBUG("日志备份目录创建成功!"); + + //todo:因为好几个地方都需要按这个最大数初始化,在没有明确新方案前,暂时不提供修改最大连接数的支持 +// auto maxCount = readFromRegWord(L"MaxCount"); +// if (maxCount > 0) +// monitorMaxCount = maxCount; } -// Merge and optimize mode lists -vector> MergeAndOptimizeModes(const vector>& manualModes, - const vector>& edidModes) { - vector> mergedModes; - - if (sourcePriority == L"edid") { - mergedModes = edidModes; - vddlog("i", "Using EDID-only mode list"); +void initDisplay(UINT displayIndex, DisplayInfo& display) +{ + LOG_INFO("正在初始化第[%d]个虚拟显示器,useEdid:%s",displayIndex,useEdid?"true":"false"); + display.use_edid = useEdid; + display.hw_cursor = hardwareCursor; + //LOG_INFO("正在初始化第[%d]个虚拟显示器,step0", displayIndex); + if(displayResFpsList.size()<=displayIndex){ //手动配置的配置可能存在问题,这边进行自动修正。 + int index = (int)displayResFpsList.size() - 1; + if (index < 0) + index = 0; + displayIndex=(UINT)index; + LOG_WARN("检测到当前显示器索引[%d],实际配置数量[%d],默认采用最后一个有效配置",displayIndex,displayResFpsList.size()) } - else if (sourcePriority == L"manual") { - mergedModes = manualModes; - vddlog("i", "Using manual-only mode list"); + auto config=displayResFpsList[displayIndex]; + display.prefer_width = get<0>(config);// res[0].cx; + display.prefer_height =get<1>(config);// res[0].cy; + display.prefer_fps = get<2>(config);//fpsList[0]; + //todo:流保存的逻辑,主要用于测试 + display.record_stream =defaultDisplayRecordSteam; // GetPrivateProfileIntA(INI_DISPLAY, INI_DISPLAY_RECORD_STREAM, kDisplayRecordSteam, ini_path); + display.record_size =defaultDisplayRecordSize;// GetPrivateProfileIntA(INI_DISPLAY, INI_DISPLAY_RECORD_SIZE, kDisplayRecordSize, ini_path); + if (display.record_stream)//暂时不启用这个 + { + string format = IOHelper::pathCombine(configPath, "display%d.stream"); + snprintf(display.record_path, sizeof(display.record_path) - 1, format.c_str(), displayIndex + 1); } - else if (sourcePriority == L"combined") { - // Start with manual modes - mergedModes = manualModes; - - // Add EDID modes that don't duplicate manual modes - for (const auto& edidMode : edidModes) { - bool isDuplicate = false; - for (const auto& manualMode : manualModes) { - if (get<0>(edidMode) == get<0>(manualMode) && - get<1>(edidMode) == get<1>(manualMode) && - get<3>(edidMode) == get<3>(manualMode)) { - isDuplicate = true; - break; - } + //todo:读取edid的逻辑,主要用于自定义显示器 + //LOG_INFO("正在初始化第[%d]个虚拟显示器,step1", displayIndex); + display.edid_len = 0; + string edidFile=IOHelper::pathCombine(configPath,EDID_FILE_NAME); + if (IOHelper::exists(edidFile)) {//如果存在外部配置 + vector vec; + if (IOHelper::readEdIdData(edidFile.c_str(), vec)) { + if (vec.size() == 128 || vec.size() == 256) { + memcpy(display.edid_data, &vec[0], vec.size()); + display.edid_len = vec.size() / sizeof(display.edid_data[0]); + LOG_INFO("%s use %s edid: count=%d", configPath.c_str(), EDID_FILE_NAME, display.edid_len); } - if (!isDuplicate) { - mergedModes.push_back(edidMode); + else { + LOG_ERROR("%s read %s length=%d", configPath.c_str(), EDID_FILE_NAME, vec.size()); } } - - stringstream ss; - ss << "Combined modes: " << manualModes.size() << " manual + " - << (mergedModes.size() - manualModes.size()) << " unique EDID = " << mergedModes.size() << " total"; - vddlog("i", ss.str().c_str()); } - - return mergedModes; -} - -// Optimize mode list for performance and compatibility -vector> OptimizeModeList(const vector>& modes, - const tuple& preferredMode) { - vector> optimizedModes = modes; - - // Remove preferred mode from list if it exists, we'll add it at the front - optimizedModes.erase( - remove_if(optimizedModes.begin(), optimizedModes.end(), - [&preferredMode](const tuple& mode) { - return get<0>(mode) == get<0>(preferredMode) && - get<1>(mode) == get<1>(preferredMode) && - get<3>(mode) == get<3>(preferredMode); - }), - optimizedModes.end()); - - // Insert preferred mode at the beginning - optimizedModes.insert(optimizedModes.begin(), preferredMode); - - // Remove duplicate modes (same resolution and refresh rate) - sort(optimizedModes.begin(), optimizedModes.end()); - optimizedModes.erase(unique(optimizedModes.begin(), optimizedModes.end(), - [](const tuple& a, const tuple& b) { - return get<0>(a) == get<0>(b) && - get<1>(a) == get<1>(b) && - get<3>(a) == get<3>(b); - }), - optimizedModes.end()); - - // Limit total number of modes for performance (Windows typically supports 20-50 modes) - const size_t maxModes = 32; - if (optimizedModes.size() > maxModes) { - optimizedModes.resize(maxModes); - stringstream ss; - ss << "Limited mode list to " << maxModes << " modes for optimal performance"; - vddlog("i", ss.str().c_str()); - } - - return optimizedModes; -} - -// Enhanced mode validation with detailed reporting -bool ValidateModeList(const vector>& modes) { - if (modes.empty()) { - vddlog("e", "Mode list is empty - this will cause display driver failure"); - return false; + // 读取不到则使用默认配置 + if (display.edid_len == 0) { + display.edid_len = _countof(defaultEdid); + memcpy(display.edid_data, defaultEdid, sizeof(display.edid_data[0]) * display.edid_len); + LOG_INFO("使用默认的edid: %d", display.edid_len); } - - stringstream validationReport; - validationReport << "=== MODE LIST VALIDATION REPORT ===\n" - << "Total modes: " << modes.size() << "\n"; - - // Analyze resolution distribution - map, int> resolutionCount; - map refreshRateCount; - - for (const auto& mode : modes) { - pair resolution = {get<0>(mode), get<1>(mode)}; - resolutionCount[resolution]++; - refreshRateCount[get<3>(mode)]++; + //LOG_INFO("正在初始化第[%d]个虚拟显示器,step2", displayIndex); + // edid 的厂商改成CPY,产品id的最后一位改成对应的驱动实例, + // 这样屏幕的ID类似 MONITOR\CPYD0C0\{4d36e96e-e325-11ce-bfc1-08002be10318}\0050 +// auto new_product_id = (display.edid_data[0x0A] & 0xF0) + displayIndex; +// auto check_sum = (int)display.edid_data[0x7F]; +// check_sum += (display.edid_data[0x08] - 0x0E); +// check_sum += (display.edid_data[0x09] - 0x19); +// check_sum += (display.edid_data[0x0A] - new_product_id); +// display.edid_data[0x08] = 0x0E; +// display.edid_data[0x09] = 0x19; +// display.edid_data[0x0A] = (BYTE)new_product_id; +// display.edid_data[0x7F] = (BYTE)check_sum; +// modifyEdid(reinterpret_cast(display.edid_data),displayIndex); + display.edid_data[10] =(BYTE) (55 + displayIndex); //0x37+displayIndex + LOG_INFO("使用默认的edid 的 product_id: %d", display.edid_data[10]*256+display.edid_data[11]); + display.edid_data[127]= calculateChecksum(reinterpret_cast(display.edid_data)); + //LOG_INFO("正在初始化第[%d]个虚拟显示器,step3", displayIndex); + display.size_cnt = res.size();//最多256个 + for (int i = 0; i < res.size(); i++) + display.size_list[i] = res[i]; + //LOG_INFO("正在初始化第[%d]个虚拟显示器,step4", displayIndex); + display.fps_cnt = fpsList.size();//最多16个 + std::copy(fpsList.begin(), fpsList.end(), display.fps_list); +// for(int i=0;i(modes[0]) << "x" << get<1>(modes[0]) - << "@" << get<3>(modes[0]) << "Hz"; - - vddlog("i", validationReport.str().c_str()); - - return true; -} - -bool LoadEdidProfile(const wstring& profilePath, EdidProfileData& profile) { - wstring fullPath = confpath + L"\\" + profilePath; - - // Check if file exists - if (!PathFileExistsW(fullPath.c_str())) { - vddlog("w", ("EDID profile not found: " + WStringToString(fullPath)).c_str()); - return false; - } - - CComPtr pStream; - CComPtr pReader; - HRESULT hr = SHCreateStreamOnFileW(fullPath.c_str(), STGM_READ, &pStream); - if (FAILED(hr)) { - vddlog("e", "LoadEdidProfile: Failed to create file stream."); - return false; - } - - hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, NULL); - if (FAILED(hr)) { - vddlog("e", "LoadEdidProfile: Failed to create XmlReader."); - return false; - } - - hr = pReader->SetInput(pStream); - if (FAILED(hr)) { - vddlog("e", "LoadEdidProfile: Failed to set input stream."); - return false; - } - - XmlNodeType nodeType; - const WCHAR* pwszLocalName; - const WCHAR* pwszValue; - UINT cwchLocalName; - UINT cwchValue; - wstring currentElement; - wstring currentSection; - - // Temporary mode data - int tempWidth = 0, tempHeight = 0, tempRefreshRateMultiplier = 1000, tempNominalRefreshRate = 60; - - while (S_OK == (hr = pReader->Read(&nodeType))) { - switch (nodeType) { - case XmlNodeType_Element: - hr = pReader->GetLocalName(&pwszLocalName, &cwchLocalName); - if (FAILED(hr)) return false; - currentElement = wstring(pwszLocalName, cwchLocalName); - - // Track sections for context - if (currentElement == L"MonitorModes" || currentElement == L"HDRCapabilities" || - currentElement == L"ColorProfile" || currentElement == L"PreferredMode") { - currentSection = currentElement; - } - break; - - case XmlNodeType_Text: - hr = pReader->GetValue(&pwszValue, &cwchValue); - if (FAILED(hr)) return false; - - wstring value = wstring(pwszValue, cwchValue); - - // Parse monitor modes - if (currentSection == L"MonitorModes") { - if (currentElement == L"Width") { - tempWidth = stoi(value); - } - else if (currentElement == L"Height") { - tempHeight = stoi(value); - } - else if (currentElement == L"RefreshRateMultiplier") { - tempRefreshRateMultiplier = stoi(value); - } - else if (currentElement == L"NominalRefreshRate") { - tempNominalRefreshRate = stoi(value); - // Complete mode entry - if (tempWidth > 0 && tempHeight > 0) { - profile.modes.push_back(make_tuple(tempWidth, tempHeight, tempRefreshRateMultiplier, tempNominalRefreshRate)); - stringstream ss; - ss << "EDID Mode: " << tempWidth << "x" << tempHeight << " @ " << tempRefreshRateMultiplier << "/" << tempNominalRefreshRate << "Hz"; - vddlog("d", ss.str().c_str()); - } - } - } - // Parse HDR capabilities - else if (currentSection == L"HDRCapabilities") { - if (currentElement == L"HDR10Supported") { - profile.hdr10Supported = (value == L"true"); - } - else if (currentElement == L"DolbyVisionSupported") { - profile.dolbyVisionSupported = (value == L"true"); - } - else if (currentElement == L"HDR10PlusSupported") { - profile.hdr10PlusSupported = (value == L"true"); - } - else if (currentElement == L"MaxLuminance") { - profile.maxLuminance = stod(value); - } - else if (currentElement == L"MinLuminance") { - profile.minLuminance = stod(value); - } - } - // Parse color profile - else if (currentSection == L"ColorProfile") { - if (currentElement == L"PrimaryColorSpace") { - profile.primaryColorSpace = value; - } - else if (currentElement == L"Gamma") { - profile.gamma = stod(value); - } - else if (currentElement == L"RedX") { - profile.redX = stod(value); - } - else if (currentElement == L"RedY") { - profile.redY = stod(value); - } - else if (currentElement == L"GreenX") { - profile.greenX = stod(value); - } - else if (currentElement == L"GreenY") { - profile.greenY = stod(value); - } - else if (currentElement == L"BlueX") { - profile.blueX = stod(value); - } - else if (currentElement == L"BlueY") { - profile.blueY = stod(value); - } - else if (currentElement == L"WhiteX") { - profile.whiteX = stod(value); - } - else if (currentElement == L"WhiteY") { - profile.whiteY = stod(value); - } - } - // Parse preferred mode - else if (currentSection == L"PreferredMode") { - if (currentElement == L"Width") { - profile.preferredWidth = stoi(value); - } - else if (currentElement == L"Height") { - profile.preferredHeight = stoi(value); - } - else if (currentElement == L"RefreshRate") { - profile.preferredRefresh = stod(value); - } - } - break; - } - } - - stringstream ss; - ss << "EDID Profile loaded: " << profile.modes.size() << " modes, HDR10: " << (profile.hdr10Supported ? "Yes" : "No") - << ", Color space: " << WStringToString(profile.primaryColorSpace); - vddlog("i", ss.str().c_str()); - - return true; -} - -bool ApplyEdidProfile(const EdidProfileData& profile) { - if (!edidIntegrationEnabled) { - return false; - } - - // === ENHANCED MODE MANAGEMENT === - if (autoResolutionsEnabled) { - // Store original manual modes - vector> originalModes = monitorModes; - - // Generate optimized modes from EDID - vector> edidModes = GenerateModesFromEdid(profile); - - // Find preferred mode from EDID - tuple preferredMode = FindPreferredModeFromEdid(profile, edidModes); - - // Merge and optimize mode lists - vector> finalModes = MergeAndOptimizeModes(originalModes, edidModes); - - // Optimize final mode list with preferred mode priority - finalModes = OptimizeModeList(finalModes, preferredMode); - - // Validate the final mode list - if (ValidateModeList(finalModes)) { - monitorModes = finalModes; - RebuildKnownMonitorModesCache(); - - stringstream ss; - ss << "Enhanced mode management completed:\n" - << " Original manual modes: " << originalModes.size() << "\n" - << " Generated EDID modes: " << edidModes.size() << "\n" - << " Final optimized modes: " << finalModes.size() << "\n" - << " Preferred mode: " << get<0>(preferredMode) << "x" << get<1>(preferredMode) - << "@" << get<3>(preferredMode) << "Hz\n" - << " Source priority: " << WStringToString(sourcePriority); - vddlog("i", ss.str().c_str()); - } else { - vddlog("e", "Mode list validation failed, keeping original modes"); - } - } - - // Apply HDR settings if configured - if (hdr10StaticMetadataEnabled && profile.hdr10Supported) { - if (overrideManualSettings || maxDisplayMasteringLuminance == 1000.0) { // Default value - maxDisplayMasteringLuminance = profile.maxLuminance; - } - if (overrideManualSettings || minDisplayMasteringLuminance == 0.05) { // Default value - minDisplayMasteringLuminance = profile.minLuminance; - } - } - - // Apply color primaries if configured - if (colorPrimariesEnabled && (overrideManualSettings || redX == 0.708)) { // Default Rec.2020 values - redX = profile.redX; - redY = profile.redY; - greenX = profile.greenX; - greenY = profile.greenY; - blueX = profile.blueX; - blueY = profile.blueY; - whiteX = profile.whiteX; - whiteY = profile.whiteY; - } - - // Apply color space settings - if (colorSpaceEnabled && (overrideManualSettings || primaryColorSpace == L"sRGB")) { // Default value - primaryColorSpace = profile.primaryColorSpace; - gammaCorrection = profile.gamma; - } - - // Generate and store HDR metadata for all monitors if HDR is enabled - if (hdr10StaticMetadataEnabled && profile.hdr10Supported) { - VddHdrMetadata hdrMetadata = ConvertEdidToSmpteMetadata(profile); - - if (hdrMetadata.isValid) { - // Store metadata for future monitor creation - // Note: We don't have monitor handles yet at this point, so we'll store it as a template - // The actual association will happen when monitors are created or HDR metadata is requested - - stringstream ss; - ss << "Generated SMPTE ST.2086 HDR metadata from EDID profile:\n" - << " Red: (" << hdrMetadata.display_primaries_x[0] << ", " << hdrMetadata.display_primaries_y[0] << ") " - << "→ (" << profile.redX << ", " << profile.redY << ")\n" - << " Green: (" << hdrMetadata.display_primaries_x[1] << ", " << hdrMetadata.display_primaries_y[1] << ") " - << "→ (" << profile.greenX << ", " << profile.greenY << ")\n" - << " Blue: (" << hdrMetadata.display_primaries_x[2] << ", " << hdrMetadata.display_primaries_y[2] << ") " - << "→ (" << profile.blueX << ", " << profile.blueY << ")\n" - << " White Point: (" << hdrMetadata.white_point_x << ", " << hdrMetadata.white_point_y << ") " - << "→ (" << profile.whiteX << ", " << profile.whiteY << ")\n" - << " Max Luminance: " << hdrMetadata.max_display_mastering_luminance - << " (" << profile.maxLuminance << " nits)\n" - << " Min Luminance: " << hdrMetadata.min_display_mastering_luminance - << " (" << profile.minLuminance << " nits)"; - vddlog("i", ss.str().c_str()); - - // Store as template metadata - will be applied to monitors during HDR metadata events - // We use a special key (nullptr converted to uintptr_t) to indicate template metadata - g_HdrMetadataStore[reinterpret_cast(0)] = hdrMetadata; - } else { - vddlog("w", "Generated HDR metadata is not valid, skipping storage"); - } - } - - // Generate and store gamma ramp for color space processing if enabled - if (colorSpaceEnabled) { - VddGammaRamp gammaRamp = ConvertEdidToGammaRamp(profile); - - if (gammaRamp.isValid) { - // Store gamma ramp as template for future monitor creation - stringstream ss; - ss << "Generated Gamma Ramp from EDID profile:\n" - << " Gamma: " << gammaRamp.gamma << " (from " << profile.gamma << ")\n" - << " Color Space: " << WStringToString(gammaRamp.colorSpace) << "\n" - << " Matrix Transform: " << (gammaRamp.useMatrix ? "Enabled" : "Disabled"); - - if (gammaRamp.useMatrix) { - ss << "\n 3x4 Matrix:\n" - << " [" << gammaRamp.matrix.matrix[0][0] << ", " << gammaRamp.matrix.matrix[0][1] << ", " << gammaRamp.matrix.matrix[0][2] << ", " << gammaRamp.matrix.matrix[0][3] << "]\n" - << " [" << gammaRamp.matrix.matrix[1][0] << ", " << gammaRamp.matrix.matrix[1][1] << ", " << gammaRamp.matrix.matrix[1][2] << ", " << gammaRamp.matrix.matrix[1][3] << "]\n" - << " [" << gammaRamp.matrix.matrix[2][0] << ", " << gammaRamp.matrix.matrix[2][1] << ", " << gammaRamp.matrix.matrix[2][2] << ", " << gammaRamp.matrix.matrix[2][3] << "]"; - } - - vddlog("i", ss.str().c_str()); - - // Store as template gamma ramp - will be applied to monitors during gamma ramp events - // We use a special key (nullptr converted to uintptr_t) to indicate template gamma ramp - g_GammaRampStore[reinterpret_cast(0)] = gammaRamp; - } else { - vddlog("w", "Generated gamma ramp is not valid, skipping storage"); - } - } - - return true; -} - -int gcd(int a, int b) { - while (b != 0) { - int temp = b; - b = a % b; - a = temp; - } - return a; + LOG_INFO("显示配置初始化完成!"); } -void float_to_vsync(float refresh_rate, int& num, int& den) { - den = 10000; - - num = static_cast(round(refresh_rate * den)); - - int divisor = gcd(num, den); - num /= divisor; - den /= divisor; -} - -void SendToPipe(const std::string& logMessage) { - if (g_pipeHandle != INVALID_HANDLE_VALUE) { - DWORD bytesWritten; - DWORD logMessageSize = static_cast(logMessage.size()); - WriteFile(g_pipeHandle, logMessage.c_str(), logMessageSize, &bytesWritten, NULL); - } -} -void vddlog(const char* type, const char* message) { - if (!logsEnabled) { - return; - } - - if (type != nullptr && type[0] == 'd' && !debugLogs) { - return; - } - - FILE* logFile; - wstring logsDir = confpath + L"\\Logs"; - - auto now = chrono::system_clock::now(); - auto in_time_t = chrono::system_clock::to_time_t(now); - tm tm_buf; - localtime_s(&tm_buf, &in_time_t); - wchar_t date_str[11]; - wcsftime(date_str, sizeof(date_str) / sizeof(wchar_t), L"%Y-%m-%d", &tm_buf); - - wstring logPath = logsDir + L"\\log_" + date_str + L".txt"; - - if (!CreateDirectoryW(logsDir.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { - // Best effort only. - } - - string narrow_logPath = WStringToString(logPath); - const char* mode = "a"; - errno_t err = fopen_s(&logFile, narrow_logPath.c_str(), mode); - if (err == 0 && logFile != nullptr) { - stringstream ss; - ss << put_time(&tm_buf, "%Y-%m-%d %X"); - - const char logTypeCode = (type != nullptr) ? type[0] : '\0'; - string logType; - switch (logTypeCode) { - case 'e': - logType = "ERROR"; - break; - case 'i': - logType = "INFO"; - break; - case 'p': - logType = "PIPE"; - break; - case 'd': - logType = "DEBUG"; - break; - case 'w': - logType = "WARNING"; - break; - case 't': - logType = "TESTING"; - break; - case 'c': - logType = "COMPANION"; - break; - default: - logType = "UNKNOWN"; - break; - } - - fprintf(logFile, "[%s] [%s] %s\n", ss.str().c_str(), logType.c_str(), message); - - fclose(logFile); - - if (sendLogsThroughPipe && g_pipeHandle != INVALID_HANDLE_VALUE) { - string logMessage = ss.str() + " [" + logType + "] " + message + "\n"; - SendToPipe(logMessage); - } - } +void loadSettings() { + auto configFile = IOHelper::pathCombine(configPath, CONFIG_FILE); + auto configs = IOHelper::readAllLine(configFile); + LOG_INFO("开始加载配置,读取到的配置条数:%d",configs.size()); + res.clear();//清空数据 + fpsList.clear();//清空数据 + hardwareCursor = true;//默认为true + displayCount = 1;//默认一个 + if (configs.size() > 0) { + for (int i = 0; i < configs.size(); i++) + { + /* 分析配置的主要逻辑 + * 1.如果存在注释符,注释之后,都不要 + * 2.如果存在=,则说明是指定属性 + * 3.如果不存在=,则说明是可用分辨率,可用分辨率用,号隔开 + * 4.如果存在多级数组,则第二级使用;号隔开 + */ + //parseSetting(config[i]); + auto data = trimString(configs[i]); + //string content = "正在处理配置0:" + data; + //LOG_DEBUG(content.c_str()); + auto index = data.find_first_of('#'); + //LOG_DEBUG("正在处理配置注释:%d",index); + if (index != 0) {//如果是0,说明整行配置都是注释,直接跳过此行 + if (index != std::string::npos) { //大于0,说明存在注释 + //LOG_DEBUG("正在处理注释!"); + data = trimString(data.erase(index));//去掉#之后的数据 + } + //LOG_DEBUG("正在处理配置2"); + if (!data.empty()) { + string content1 = "正在处理配置:" + data; + LOG_DEBUG(content1.c_str()); + index = data.find_first_of('='); + if (index != std::string::npos) { //属性配置 + if (index > 0) { + auto setting = split(data, '='); + auto key = trimString(setting[0]); + if (key == "DisplayCount") { + auto count = stoi(setting[1]); + if (count >= 0) { //允许配置0个虚拟显示器 + displayCount = count; + } + if (count > monitorMaxCount) { + displayCount = monitorMaxCount; + } + } + else if (key == "Displays") { + auto displaySetting = trimString(setting[1]); + if (!displaySetting.empty()) { + displayResFpsList.clear(); + vector strvec = split(setting[1], ';'); + for (int j = 0; j < strvec.size(); j++) { + vector temp = split(strvec[j], ','); + if (temp.size() == 3) { + displayResFpsList.push_back(make_tuple(stoi(temp[0]), stoi(temp[1]), stoi(temp[2]))); + } + } + } + } + else if (key == "Rates") { + vector strvec = split(setting[1], ','); + for (int j = 0; j < strvec.size(); j++) + { + fpsList.push_back(stoi(strvec[j])); + } + } + else if (key == "LogLevel") { + auto option = trimString(setting[1]); + option = toLowerString(option); + if (option == "debug" || option == "4") { + logLevel = 4; + } + else if (option == "info" || option == "3") { + logLevel = 3; + } + else if (option == "warn" || option == "2") { + logLevel = 2; + } + else if (option == "error" || option == "1") { + logLevel = 1; + } + else { + logLevel = 0; + } + } + else if (key == "HardwareCursor") { + auto option = trimString(setting[1]); + option = toLowerString(option); + hardwareCursor = option == "true"; + } + else if (key == "UseEdid") { + auto option = trimString(setting[1]); + option = toLowerString(option); + useEdid = option == "true"; + } + } + }else { //分辨率配置 + vector strvec = split(data, ','); + if (strvec.size() == 2) { + res.push_back({ stoi(strvec[0]), stoi(strvec[1]) }); + } + } + } + } + } + LOG_INFO("使用option.txt配置的内容!"); + }else { + LOG_INFO("使用默认配置的内容!"); + } + if (fpsList.empty()) {//加入默认 + LOG_INFO("使用默认配置的刷新率!"); + fpsList.insert(fpsList.end(), begin(defaultDisplayFps),end(defaultDisplayFps)); + }else{ + LOG_INFO("使用自定义的刷新率配置,共%d条!",fpsList.size()); + } + if (res.empty()) { + LOG_INFO("使用默认配置的分辨率!"); + res.insert(res.end(), begin(defaultDisplayResolution), end(defaultDisplayResolution)); + }else{ + LOG_INFO("使用自定义的分辨率配置,共%d条!",res.size()); + } + initDisplays(); } +#pragma endregion +#pragma region SampleMonitors +// Default modes reported for edid-less monitors. The first mode is set as preferred +//static const struct IndirectSampleMonitor::SampleMonitorMode s_SampleDefaultModes[] = +//{ +// { 1920, 1080, 60 }, +// { 1600, 900, 60 }, +// { 1024, 768, 75 }, +//}; +// +//// FOR SAMPLE PURPOSES ONLY, Static info about monitors that will be reported to OS +//static const struct IndirectSampleMonitor s_SampleMonitors[] = +// { +// // Modified EDID from Dell S2719DGF +// { +// { +// 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x10,0xAC,0xE6,0xD0,0x55,0x5A,0x4A,0x30,0x24,0x1D,0x01, +// 0x04,0xA5,0x3C,0x22,0x78,0xFB,0x6C,0xE5,0xA5,0x55,0x50,0xA0,0x23,0x0B,0x50,0x54,0x00,0x02,0x00, +// 0xD1,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x58,0xE3,0x00, +// 0xA0,0xA0,0xA0,0x29,0x50,0x30,0x20,0x35,0x00,0x55,0x50,0x21,0x00,0x00,0x1A,0x00,0x00,0x00,0xFF, +// 0x00,0x37,0x4A,0x51,0x58,0x42,0x59,0x32,0x0A,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,0x00, +// 0x53,0x32,0x37,0x31,0x39,0x44,0x47,0x46,0x0A,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFD,0x00,0x28, +// 0x9B,0xFA,0xFA,0x40,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x2C +// }, +// { +// { 2560, 1440, 144 }, +// { 1920, 1080, 60 }, +// { 1024, 768, 60 }, +// }, +// 0 +// }, +// // Modified EDID from Lenovo Y27fA +// { +// { +// 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x30,0xAE,0xBF,0x65,0x01,0x01,0x01,0x01,0x20,0x1A,0x01, +// 0x04,0xA5,0x3C,0x22,0x78,0x3B,0xEE,0xD1,0xA5,0x55,0x48,0x9B,0x26,0x12,0x50,0x54,0x00,0x08,0x00, +// 0xA9,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x68,0xD8,0x00, +// 0x18,0xF1,0x70,0x2D,0x80,0x58,0x2C,0x45,0x00,0x53,0x50,0x21,0x00,0x00,0x1E,0x00,0x00,0x00,0x10, +// 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFD,0x00, +// 0x30,0x92,0xB4,0xB4,0x22,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,0x00,0x4C, +// 0x45,0x4E,0x20,0x59,0x32,0x37,0x66,0x41,0x0A,0x20,0x20,0x20,0x00,0x11 +// }, +// { +// { 3840, 2160, 60 }, +// { 1600, 900, 60 }, +// { 1024, 768, 60 }, +// }, +// 0 +// } +// }; -void LogIddCxVersion() { - IDARG_OUT_GETVERSION outArgs; - NTSTATUS status = IddCxGetVersion(&outArgs); - - if (NT_SUCCESS(status)) { - char versionStr[16]; - sprintf_s(versionStr, "0x%lx", outArgs.IddCxVersion); - string logMessage = "IDDCX Version: " + string(versionStr); - vddlog("i", logMessage.c_str()); - } - else { - vddlog("i", "Failed to get IDDCX version"); - } - vddlog("d", "Testing Debug Log"); -} - -void InitializeD3DDeviceAndLogGPU() { - ComPtr d3dDevice; - ComPtr d3dContext; - HRESULT hr = D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_HARDWARE, - nullptr, - 0, - nullptr, - 0, - D3D11_SDK_VERSION, - &d3dDevice, - nullptr, - &d3dContext); - - if (FAILED(hr)) { - vddlog("e", "Retrieving D3D Device GPU: Failed to create D3D11 device"); - return; - } - - ComPtr dxgiDevice; - hr = d3dDevice.As(&dxgiDevice); - if (FAILED(hr)) { - vddlog("e", "Retrieving D3D Device GPU: Failed to get DXGI device"); - return; - } - - ComPtr dxgiAdapter; - hr = dxgiDevice->GetAdapter(&dxgiAdapter); - if (FAILED(hr)) { - vddlog("e", "Retrieving D3D Device GPU: Failed to get DXGI adapter"); - return; - } - - DXGI_ADAPTER_DESC desc; - hr = dxgiAdapter->GetDesc(&desc); - if (FAILED(hr)) { - vddlog("e", "Retrieving D3D Device GPU: Failed to get GPU description"); - return; - } - - d3dDevice.Reset(); - d3dContext.Reset(); - - wstring wdesc(desc.Description); - string utf8_desc; - try { - utf8_desc = WStringToString(wdesc); - } - catch (const exception& e) { - vddlog("e", ("Retrieving D3D Device GPU: Conversion error: " + string(e.what())).c_str()); - return; - } - - string logtext = "Retrieving D3D Device GPU: " + utf8_desc; - vddlog("i", logtext.c_str()); -} - +#pragma endregion -// This macro creates the methods for accessing an IndirectDeviceContextWrapper as a context for a WDF object -WDF_DECLARE_CONTEXT_TYPE(IndirectDeviceContextWrapper); +#pragma region helpers -extern "C" BOOL WINAPI DllMain( - _In_ HINSTANCE hInstance, - _In_ UINT dwReason, - _In_opt_ LPVOID lpReserved) +static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& Mode, DWORD Width, DWORD Height, DWORD VSync, bool bMonitorMode) { - UNREFERENCED_PARAMETER(hInstance); - UNREFERENCED_PARAMETER(lpReserved); - UNREFERENCED_PARAMETER(dwReason); + Mode.totalSize.cx = Mode.activeSize.cx = Width; + Mode.totalSize.cy = Mode.activeSize.cy = Height; - return TRUE; -} + // See https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-displayconfig_video_signal_info + Mode.AdditionalSignalInfo.vSyncFreqDivider = bMonitorMode ? 0 : 1; + Mode.AdditionalSignalInfo.videoStandard = 255; + Mode.vSyncFreq.Numerator = VSync; + Mode.vSyncFreq.Denominator = 1; + Mode.hSyncFreq.Numerator = VSync * Height; + Mode.hSyncFreq.Denominator = 1; -bool UpdateXmlToggleSetting(bool toggle, const wchar_t* variable) { - const wstring settingsname = confpath + L"\\vdd_settings.xml"; - CComPtr pFileStream; - HRESULT hr = SHCreateStreamOnFileEx(settingsname.c_str(), STGM_READWRITE, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &pFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: XML file could not be opened."); - return false; - } - - CComPtr pReader; - hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, nullptr); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to create XML reader."); - return false; - } - hr = pReader->SetInput(pFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to set XML reader input."); - return false; - } - - CComPtr pOutFileStream; - wstring tempFileName = settingsname + L".temp"; - hr = SHCreateStreamOnFileEx(tempFileName.c_str(), STGM_CREATE | STGM_WRITE, FILE_ATTRIBUTE_NORMAL, TRUE, nullptr, &pOutFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to create output file stream."); - return false; - } - - CComPtr pWriter; - hr = CreateXmlWriter(__uuidof(IXmlWriter), (void**)&pWriter, nullptr); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to create XML writer."); - return false; - } - hr = pWriter->SetOutput(pOutFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to set XML writer output."); - return false; - } - hr = pWriter->WriteStartDocument(XmlStandalone_Omit); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to write start of the document."); - return false; - } - - XmlNodeType nodeType; - const wchar_t* pwszLocalName; - const wchar_t* pwszValue; - bool variableElementFound = false; - - while (S_OK == pReader->Read(&nodeType)) { - switch (nodeType) { - case XmlNodeType_Element: - pReader->GetLocalName(&pwszLocalName, nullptr); - pWriter->WriteStartElement(nullptr, pwszLocalName, nullptr); - break; - - case XmlNodeType_EndElement: - pReader->GetLocalName(&pwszLocalName, nullptr); - pWriter->WriteEndElement(); - break; - - case XmlNodeType_Text: - pReader->GetValue(&pwszValue, nullptr); - if (variableElementFound) { - pWriter->WriteString(toggle ? L"true" : L"false"); - variableElementFound = false; - } - else { - pWriter->WriteString(pwszValue); - } - break; - - case XmlNodeType_Whitespace: - pReader->GetValue(&pwszValue, nullptr); - pWriter->WriteWhitespace(pwszValue); - break; - - case XmlNodeType_Comment: - pReader->GetValue(&pwszValue, nullptr); - pWriter->WriteComment(pwszValue); - break; - } - - if (nodeType == XmlNodeType_Element) { - pReader->GetLocalName(&pwszLocalName, nullptr); - if (pwszLocalName != nullptr && wcscmp(pwszLocalName, variable) == 0) { - variableElementFound = true; - } - } - } - - if (variableElementFound) { - pWriter->WriteStartElement(nullptr, variable, nullptr); - pWriter->WriteString(toggle ? L"true" : L"false"); - pWriter->WriteEndElement(); - } - - hr = pWriter->WriteEndDocument(); - if (FAILED(hr)) { - return false; - } - - pFileStream.Release(); - pOutFileStream.Release(); - pWriter.Release(); - pReader.Release(); - - if (!MoveFileExW(tempFileName.c_str(), settingsname.c_str(), MOVEFILE_REPLACE_EXISTING)) { - return false; - } - return true; -} + Mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; - -bool UpdateXmlGpuSetting(const wchar_t* gpuName) { - const std::wstring settingsname = confpath + L"\\vdd_settings.xml"; - CComPtr pFileStream; - HRESULT hr = SHCreateStreamOnFileEx(settingsname.c_str(), STGM_READWRITE, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &pFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: XML file could not be opened."); - return false; - } - - CComPtr pReader; - hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, nullptr); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to create XML reader."); - return false; - } - hr = pReader->SetInput(pFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to set XML reader input."); - return false; - } - - CComPtr pOutFileStream; - std::wstring tempFileName = settingsname + L".temp"; - hr = SHCreateStreamOnFileEx(tempFileName.c_str(), STGM_CREATE | STGM_WRITE, FILE_ATTRIBUTE_NORMAL, TRUE, nullptr, &pOutFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to create output file stream."); - return false; - } - - CComPtr pWriter; - hr = CreateXmlWriter(__uuidof(IXmlWriter), (void**)&pWriter, nullptr); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to create XML writer."); - return false; - } - hr = pWriter->SetOutput(pOutFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to set XML writer output."); - return false; - } - hr = pWriter->WriteStartDocument(XmlStandalone_Omit); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to write start of the document."); - return false; - } - - XmlNodeType nodeType; - const wchar_t* pwszLocalName; - const wchar_t* pwszValue; - bool gpuElementFound = false; - - while (S_OK == pReader->Read(&nodeType)) { - switch (nodeType) { - case XmlNodeType_Element: - pReader->GetLocalName(&pwszLocalName, nullptr); - pWriter->WriteStartElement(nullptr, pwszLocalName, nullptr); - break; - - case XmlNodeType_EndElement: - pReader->GetLocalName(&pwszLocalName, nullptr); - pWriter->WriteEndElement(); - break; - - case XmlNodeType_Text: - pReader->GetValue(&pwszValue, nullptr); - if (gpuElementFound) { - pWriter->WriteString(gpuName); - gpuElementFound = false; - } - else { - pWriter->WriteString(pwszValue); - } - break; - - case XmlNodeType_Whitespace: - pReader->GetValue(&pwszValue, nullptr); - pWriter->WriteWhitespace(pwszValue); - break; - - case XmlNodeType_Comment: - pReader->GetValue(&pwszValue, nullptr); - pWriter->WriteComment(pwszValue); - break; - } - - if (nodeType == XmlNodeType_Element) { - pReader->GetLocalName(&pwszLocalName, nullptr); - if (wcscmp(pwszLocalName, L"gpu") == 0) { - gpuElementFound = true; - } - } - } - hr = pWriter->WriteEndDocument(); - if (FAILED(hr)) { - return false; - } - - pFileStream.Release(); - pOutFileStream.Release(); - pWriter.Release(); - pReader.Release(); - - if (!MoveFileExW(tempFileName.c_str(), settingsname.c_str(), MOVEFILE_REPLACE_EXISTING)) { - return false; - } - return true; -} - -bool UpdateXmlDisplayCountSetting(int displayCount) { - const std::wstring settingsname = confpath + L"\\vdd_settings.xml"; - CComPtr pFileStream; - HRESULT hr = SHCreateStreamOnFileEx(settingsname.c_str(), STGM_READWRITE, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &pFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: XML file could not be opened."); - return false; - } - - CComPtr pReader; - hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, nullptr); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to create XML reader."); - return false; - } - hr = pReader->SetInput(pFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to set XML reader input."); - return false; - } - - CComPtr pOutFileStream; - std::wstring tempFileName = settingsname + L".temp"; - hr = SHCreateStreamOnFileEx(tempFileName.c_str(), STGM_CREATE | STGM_WRITE, FILE_ATTRIBUTE_NORMAL, TRUE, nullptr, &pOutFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to create output file stream."); - return false; - } - - CComPtr pWriter; - hr = CreateXmlWriter(__uuidof(IXmlWriter), (void**)&pWriter, nullptr); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to create XML writer."); - return false; - } - hr = pWriter->SetOutput(pOutFileStream); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to set XML writer output."); - return false; - } - hr = pWriter->WriteStartDocument(XmlStandalone_Omit); - if (FAILED(hr)) { - vddlog("e", "UpdatingXML: Failed to write start of the document."); - return false; - } - - XmlNodeType nodeType; - const wchar_t* pwszLocalName; - const wchar_t* pwszValue; - bool displayCountElementFound = false; - - while (S_OK == pReader->Read(&nodeType)) { - switch (nodeType) { - case XmlNodeType_Element: - pReader->GetLocalName(&pwszLocalName, nullptr); - pWriter->WriteStartElement(nullptr, pwszLocalName, nullptr); - break; - - case XmlNodeType_EndElement: - pReader->GetLocalName(&pwszLocalName, nullptr); - pWriter->WriteEndElement(); - break; - - case XmlNodeType_Text: - pReader->GetValue(&pwszValue, nullptr); - if (displayCountElementFound) { - pWriter->WriteString(std::to_wstring(displayCount).c_str()); - displayCountElementFound = false; - } - else { - pWriter->WriteString(pwszValue); - } - break; - - case XmlNodeType_Whitespace: - pReader->GetValue(&pwszValue, nullptr); - pWriter->WriteWhitespace(pwszValue); - break; - - case XmlNodeType_Comment: - pReader->GetValue(&pwszValue, nullptr); - pWriter->WriteComment(pwszValue); - break; - } - - if (nodeType == XmlNodeType_Element) { - pReader->GetLocalName(&pwszLocalName, nullptr); - if (wcscmp(pwszLocalName, L"count") == 0) { - displayCountElementFound = true; - } - } - } - - hr = pWriter->WriteEndDocument(); - if (FAILED(hr)) { - return false; - } - - pFileStream.Release(); - pOutFileStream.Release(); - pWriter.Release(); - pReader.Release(); - - if (!MoveFileExW(tempFileName.c_str(), settingsname.c_str(), MOVEFILE_REPLACE_EXISTING)) { - return false; - } - return true; + Mode.pixelRate = ((UINT64)VSync) * ((UINT64)Width) * ((UINT64)Height); } +static IDDCX_MONITOR_MODE CreateIddCxMonitorMode(DWORD Width, DWORD Height, DWORD VSync, IDDCX_MONITOR_MODE_ORIGIN Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER) +{ + IDDCX_MONITOR_MODE Mode = {}; -LUID getSetAdapterLuid() { - AdapterOption& adapterOption = Options.Adapter; - - if (!adapterOption.hasTargetAdapter) { - vddlog("e","No Gpu Found/Selected"); - } + Mode.Size = sizeof(Mode); + Mode.Origin = Origin; + FillSignalInfo(Mode.MonitorVideoSignalInfo, Width, Height, VSync, true); - return adapterOption.adapterLuid; + return Mode; } - -void GetGpuInfo() +static IDDCX_TARGET_MODE CreateIddCxTargetMode(DWORD Width, DWORD Height, DWORD VSync) { - AdapterOption& adapterOption = Options.Adapter; - - if (!adapterOption.hasTargetAdapter) { - vddlog("e", "No GPU found or set."); - return; - } - - try { - string utf8_desc = WStringToString(adapterOption.target_name); - LUID luid = getSetAdapterLuid(); - string logtext = "ASSIGNED GPU: " + utf8_desc + - " (LUID: " + std::to_string(luid.LowPart) + "-" + std::to_string(luid.HighPart) + ")"; - vddlog("i", logtext.c_str()); - } - catch (const exception& e) { - vddlog("e", ("Error: " + string(e.what())).c_str()); - } -} - -void logAvailableGPUs() { - vector gpus; - ComPtr factory; - if (!SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) { - return; - } - for (UINT i = 0;; i++) { - ComPtr adapter; - if (!SUCCEEDED(factory->EnumAdapters(i, &adapter))) { - break; - } - DXGI_ADAPTER_DESC desc; - if (!SUCCEEDED(adapter->GetDesc(&desc))) { - continue; - } - GPUInfo info{ desc.Description, adapter, desc }; - gpus.push_back(info); - } - for (const auto& gpu : gpus) { - wstring logMessage = L"GPU Name: "; - logMessage += gpu.desc.Description; - wstring memorySize = L" Memory: "; - memorySize += std::to_wstring(gpu.desc.DedicatedVideoMemory / (1024 * 1024)) + L" MB"; - wstring logText = logMessage + memorySize; - int bufferSize = WideCharToMultiByte(CP_UTF8, 0, logText.c_str(), -1, nullptr, 0, nullptr, nullptr); - if (bufferSize > 0) { - std::string logTextA(bufferSize - 1, '\0'); - WideCharToMultiByte(CP_UTF8, 0, logText.c_str(), -1, &logTextA[0], bufferSize, nullptr, nullptr); - vddlog("c", logTextA.c_str()); - } - } -} - - - - - -void ReloadDriver(HANDLE hPipe) { - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(hPipe); - if (pContext && pContext->pContext) { - pContext->pContext->InitAdapter(); - vddlog("i", "Adapter reinitialized"); - } -} + IDDCX_TARGET_MODE Mode = {}; + Mode.Size = sizeof(Mode); + FillSignalInfo(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSync, false); -void HandleClient(HANDLE hPipe) { - g_pipeHandle = hPipe; - vddlog("p", "Client Handling Enabled"); - wchar_t buffer[128]; - DWORD bytesRead; - BOOL result = ReadFile(hPipe, buffer, sizeof(buffer) - sizeof(wchar_t), &bytesRead, NULL); - if (result && bytesRead != 0) { - buffer[bytesRead / sizeof(wchar_t)] = L'\0'; - wstring bufferwstr(buffer); - int bufferSize = WideCharToMultiByte(CP_UTF8, 0, bufferwstr.c_str(), -1, nullptr, 0, nullptr, nullptr); - string bufferstr(bufferSize, 0); - WideCharToMultiByte(CP_UTF8, 0, bufferwstr.c_str(), -1, &bufferstr[0], bufferSize, nullptr, nullptr); - vddlog("p", bufferstr.c_str()); - if (wcsncmp(buffer, L"RELOAD_DRIVER", 13) == 0) { - vddlog("c", "Reloading the driver"); - ReloadDriver(hPipe); - - } - else if (wcsncmp(buffer, L"LOG_DEBUG", 9) == 0) { - wchar_t* param = buffer + 10; - if (wcsncmp(param, L"true", 4) == 0) { - UpdateXmlToggleSetting(true, L"debuglogging"); - debugLogs = true; - vddlog("c", "Pipe debugging enabled"); - vddlog("d", "Debug Logs Enabled"); - } - else if (wcsncmp(param, L"false", 5) == 0) { - UpdateXmlToggleSetting(false, L"debuglogging"); - debugLogs = false; - vddlog("c", "Debugging disabled"); - } - } - else if (wcsncmp(buffer, L"LOGGING", 7) == 0) { - wchar_t* param = buffer + 8; - if (wcsncmp(param, L"true", 4) == 0) { - UpdateXmlToggleSetting(true, L"logging"); - logsEnabled = true; - vddlog("c", "Logging Enabled"); - } - else if (wcsncmp(param, L"false", 5) == 0) { - UpdateXmlToggleSetting(false, L"logging"); - logsEnabled = false; - vddlog("c", "Logging disabled"); // We can keep this here just to make it delete the logs on disable - } - } - else if (wcsncmp(buffer, L"HDRPLUS", 7) == 0) { - wchar_t* param = buffer + 8; - if (wcsncmp(param, L"true", 4) == 0) { - UpdateXmlToggleSetting(true, L"HDRPlus"); - vddlog("c", "HDR+ Enabled"); - ReloadDriver(hPipe); - } - else if (wcsncmp(param, L"false", 5) == 0) { - UpdateXmlToggleSetting(false, L"HDRPlus"); - vddlog("c", "HDR+ Disabled"); - ReloadDriver(hPipe); - } - } - else if (wcsncmp(buffer, L"SDR10", 5) == 0) { - wchar_t* param = buffer + 6; - if (wcsncmp(param, L"true", 4) == 0) { - UpdateXmlToggleSetting(true, L"SDR10bit"); - vddlog("c", "SDR 10 Bit Enabled"); - ReloadDriver(hPipe); - } - else if (wcsncmp(param, L"false", 5) == 0) { - UpdateXmlToggleSetting(false, L"SDR10bit"); - vddlog("c", "SDR 10 Bit Disabled"); - ReloadDriver(hPipe); - } - } - else if (wcsncmp(buffer, L"CUSTOMEDID", 10) == 0) { - wchar_t* param = buffer + 11; - if (wcsncmp(param, L"true", 4) == 0) { - UpdateXmlToggleSetting(true, L"CustomEdid"); - vddlog("c", "Custom Edid Enabled"); - ReloadDriver(hPipe); - } - else if (wcsncmp(param, L"false", 5) == 0) { - UpdateXmlToggleSetting(false, L"CustomEdid"); - vddlog("c", "Custom Edid Disabled"); - ReloadDriver(hPipe); - } - } - else if (wcsncmp(buffer, L"PREVENTSPOOF", 12) == 0) { - wchar_t* param = buffer + 13; - if (wcsncmp(param, L"true", 4) == 0) { - UpdateXmlToggleSetting(true, L"PreventSpoof"); - vddlog("c", "Prevent Spoof Enabled"); - ReloadDriver(hPipe); - } - else if (wcsncmp(param, L"false", 5) == 0) { - UpdateXmlToggleSetting(false, L"PreventSpoof"); - vddlog("c", "Prevent Spoof Disabled"); - ReloadDriver(hPipe); - } - } - else if (wcsncmp(buffer, L"CEAOVERRIDE", 11) == 0) { - wchar_t* param = buffer + 12; - if (wcsncmp(param, L"true", 4) == 0) { - UpdateXmlToggleSetting(true, L"EdidCeaOverride"); - vddlog("c", "Cea override Enabled"); - ReloadDriver(hPipe); - } - else if (wcsncmp(param, L"false", 5) == 0) { - UpdateXmlToggleSetting(false, L"EdidCeaOverride"); - vddlog("c", "Cea override Disabled"); - ReloadDriver(hPipe); - } - } - else if (wcsncmp(buffer, L"HARDWARECURSOR", 14) == 0) { - wchar_t* param = buffer + 15; - if (wcsncmp(param, L"true", 4) == 0) { - UpdateXmlToggleSetting(true, L"HardwareCursor"); - vddlog("c", "Hardware Cursor Enabled"); - ReloadDriver(hPipe); - } - else if (wcsncmp(param, L"false", 5) == 0) { - UpdateXmlToggleSetting(false, L"HardwareCursor"); - vddlog("c", "Hardware Cursor Disabled"); - ReloadDriver(hPipe); - } - } - else if (wcsncmp(buffer, L"D3DDEVICEGPU", 12) == 0) { - vddlog("c", "Retrieving D3D GPU (This information may be inaccurate without reloading the driver first)"); - InitializeD3DDeviceAndLogGPU(); - vddlog("c", "Retrieved D3D GPU"); - } - else if (wcsncmp(buffer, L"IDDCXVERSION", 12) == 0) { - vddlog("c", "Logging iddcx version"); - LogIddCxVersion(); - } - else if (wcsncmp(buffer, L"GETASSIGNEDGPU", 14) == 0) { - vddlog("c", "Retrieving Assigned GPU"); - GetGpuInfo(); - vddlog("c", "Retrieved Assigned GPU"); - } - else if (wcsncmp(buffer, L"GETALLGPUS", 10) == 0) { - vddlog("c", "Logging all GPUs"); - vddlog("i", "Any GPUs which show twice but you only have one, will most likely be the GPU the driver is attached to"); - logAvailableGPUs(); - vddlog("c", "Logged all GPUs"); - } - else if (wcsncmp(buffer, L"SETGPU", 6) == 0) { - std::wstring gpuName = buffer + 7; - gpuName = gpuName.substr(1, gpuName.size() - 2); - - int size_needed = WideCharToMultiByte(CP_UTF8, 0, gpuName.c_str(), static_cast(gpuName.length()), nullptr, 0, nullptr, nullptr); - std::string gpuNameNarrow(size_needed, 0); - WideCharToMultiByte(CP_UTF8, 0, gpuName.c_str(), static_cast(gpuName.length()), &gpuNameNarrow[0], size_needed, nullptr, nullptr); - - vddlog("c", ("Setting GPU to: " + gpuNameNarrow).c_str()); - if (UpdateXmlGpuSetting(gpuName.c_str())) { - vddlog("c", "Gpu Changed, Restarting Driver"); - } - else { - vddlog("e", "Failed to update GPU setting in XML. Restarting anyway"); - } - ReloadDriver(hPipe); - } - else if (wcsncmp(buffer, L"SETDISPLAYCOUNT", 15) == 0) { - vddlog("i", "Setting Display Count"); - - int newDisplayCount = 1; - swscanf_s(buffer + 15, L"%d", &newDisplayCount); - - std::wstring displayLog = L"Setting display count to " + std::to_wstring(newDisplayCount); - vddlog("c", WStringToString(displayLog).c_str()); - - if (UpdateXmlDisplayCountSetting(newDisplayCount)){ - vddlog("c", "Display Count Changed, Restarting Driver"); - } - else { - vddlog("e", "Failed to update display count setting in XML. Restarting anyway"); - } - ReloadDriver(hPipe); - } - else if (wcsncmp(buffer, L"GETSETTINGS", 11) == 0) { - //query and return settings - bool debugEnabled = EnabledQuery(L"DebugLoggingEnabled"); - bool loggingEnabled = EnabledQuery(L"LoggingEnabled"); - - wstring settingsResponse = L"SETTINGS "; - settingsResponse += debugEnabled ? L"DEBUG=true " : L"DEBUG=false "; - settingsResponse += loggingEnabled ? L"LOG=true" : L"LOG=false"; - - DWORD bytesWritten; - DWORD bytesToWrite = static_cast((settingsResponse.length() + 1) * sizeof(wchar_t)); - WriteFile(hPipe, settingsResponse.c_str(), bytesToWrite, &bytesWritten, NULL); - - } - else if (wcsncmp(buffer, L"PING", 4) == 0) { - SendToPipe("PONG"); - vddlog("p", "Heartbeat Ping"); - } - else { - vddlog("e", "Unknown command"); - - size_t size_needed; - wcstombs_s(&size_needed, nullptr, 0, buffer, 0); - std::string narrowString(size_needed, 0); - wcstombs_s(nullptr, &narrowString[0], size_needed, buffer, size_needed); - vddlog("e", narrowString.c_str()); - } - } - DisconnectNamedPipe(hPipe); - CloseHandle(hPipe); - g_pipeHandle = INVALID_HANDLE_VALUE; // This value determines whether or not all data gets sent back through the pipe or just the handling pipe data + return Mode; } +#pragma endregion -DWORD WINAPI NamedPipeServer(LPVOID lpParam) { - UNREFERENCED_PARAMETER(lpParam); - - SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = FALSE; - const wchar_t* sddl = L"D:(A;;GA;;;WD)"; - vddlog("d", "Starting pipe with parameters: D:(A;;GA;;;WD)"); - if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( - sddl, SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL)) { - DWORD ErrorCode = GetLastError(); - string errorMessage = to_string(ErrorCode); - vddlog("e", errorMessage.c_str()); - return 1; - } - HANDLE hPipe; - while (g_Running) { - hPipe = CreateNamedPipeW( - PIPE_NAME, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - 512, 512, - 0, - &sa); - - if (hPipe == INVALID_HANDLE_VALUE) { - DWORD ErrorCode = GetLastError(); - string errorMessage = to_string(ErrorCode); - vddlog("e", errorMessage.c_str()); - LocalFree(sa.lpSecurityDescriptor); - return 1; - } - - BOOL connected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); - if (connected) { - vddlog("p", "Client Connected"); - HandleClient(hPipe); - } - else { - CloseHandle(hPipe); - } - } - LocalFree(sa.lpSecurityDescriptor); - return 0; -} +extern "C" DRIVER_INITIALIZE DriverEntry; -void StartNamedPipeServer() { - vddlog("p", "Starting Pipe"); - hPipeThread = CreateThread(NULL, 0, NamedPipeServer, NULL, 0, NULL); - if (hPipeThread == NULL) { - DWORD ErrorCode = GetLastError(); - string errorMessage = to_string(ErrorCode); - vddlog("e", errorMessage.c_str()); - } - else { - vddlog("p", "Pipe created"); - } -} +EVT_WDF_DRIVER_DEVICE_ADD VirtualDisplayDeviceAdd; +EVT_WDF_DEVICE_D0_ENTRY VirtualDisplayDeviceD0Entry; -void StopNamedPipeServer() { - vddlog("p", "Stopping Pipe"); - { - lock_guard lock(g_Mutex); - g_Running = false; - } - if (hPipeThread) { - HANDLE hPipe = CreateFileW( - PIPE_NAME, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL); - - if (hPipe != INVALID_HANDLE_VALUE) { - DisconnectNamedPipe(hPipe); - CloseHandle(hPipe); - } - - WaitForSingleObject(hPipeThread, INFINITE); - CloseHandle(hPipeThread); - hPipeThread = NULL; - vddlog("p", "Stopped Pipe"); - } -} - -bool initpath() { - HKEY hKey; - wchar_t szPath[MAX_PATH]; - DWORD dwBufferSize = sizeof(szPath); - LONG lResult; - //vddlog("i", "Reading reg: Computer\\HKEY_LOCAL_MACHINE\\SOFTWARE\\MikeTheTech\\VirtualDisplayDriver"); Remove this due to the fact, if reg key exists, this is called before reading - lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MikeTheTech\\VirtualDisplayDriver", 0, KEY_READ, &hKey); - if (lResult != ERROR_SUCCESS) { - ostringstream oss; - oss << "Failed to open registry key for path. Error code: " << lResult; - //vddlog("w", oss.str().c_str()); // These are okay to call though since they're only called if the reg doesnt exist - return false; - } - - lResult = RegQueryValueExW(hKey, L"VDDPATH", NULL, NULL, (LPBYTE)szPath, &dwBufferSize); - if (lResult != ERROR_SUCCESS) { - ostringstream oss; - oss << "Failed to open registry key for path. Error code: " << lResult; - //vddlog("w", oss.str().c_str()); Prevent these from being called since no longer checks before logging, only on startup whether it should - RegCloseKey(hKey); - return false; - } - - confpath = szPath; - - RegCloseKey(hKey); - - return true; -} +//EVT_IDD_CX_DEVICE_IO_CONTROL VirtualDisplayIoDeviceControl; +EVT_IDD_CX_ADAPTER_INIT_FINISHED VirtualDisplayAdapterInitFinished; +EVT_IDD_CX_ADAPTER_COMMIT_MODES VirtualDisplayAdapterCommitModes; +EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION VirtualDisplayParseMonitorDescription; +EVT_IDD_CX_MONITOR_GET_DEFAULT_DESCRIPTION_MODES VirtualDisplayMonitorGetDefaultModes; +EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES VirtualDisplayMonitorQueryModes; -extern "C" EVT_WDF_DRIVER_UNLOAD EvtDriverUnload; +EVT_IDD_CX_MONITOR_ASSIGN_SWAPCHAIN VirtualDisplayMonitorAssignSwapChain; +EVT_IDD_CX_MONITOR_UNASSIGN_SWAPCHAIN VirtualDisplayMonitorUnassignSwapChain; +EVT_IDD_CX_MONITOR_SET_GAMMA_RAMP VirtualDisplayMonitorSetGammaRamp; -VOID -EvtDriverUnload( - _In_ WDFDRIVER Driver -) +struct IndirectDeviceContextWrapper { - UNREFERENCED_PARAMETER(Driver); - StopNamedPipeServer(); - vddlog("i", "Driver Unloaded"); -} + IndirectDeviceContext* pContext; -_Use_decl_annotations_ -extern "C" NTSTATUS DriverEntry( - PDRIVER_OBJECT pDriverObject, - PUNICODE_STRING pRegistryPath -) -{ - WDF_DRIVER_CONFIG Config; - NTSTATUS Status; - - WDF_OBJECT_ATTRIBUTES Attributes; - WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); - - WDF_DRIVER_CONFIG_INIT(&Config, VirtualDisplayDriverDeviceAdd); - - Config.EvtDriverUnload = EvtDriverUnload; - initpath(); - logsEnabled = EnabledQuery(L"LoggingEnabled"); - debugLogs = EnabledQuery(L"DebugLoggingEnabled"); - - customEdid = EnabledQuery(L"CustomEdidEnabled"); - preventManufacturerSpoof = EnabledQuery(L"PreventMonitorSpoof"); - edidCeaOverride = EnabledQuery(L"EdidCeaOverride"); - sendLogsThroughPipe = EnabledQuery(L"SendLogsThroughPipe"); - - - //colour - HDRPlus = EnabledQuery(L"HDRPlusEnabled"); - SDR10 = EnabledQuery(L"SDR10Enabled"); - HDRCOLOUR = HDRPlus ? IDDCX_BITS_PER_COMPONENT_12 : IDDCX_BITS_PER_COMPONENT_10; - SDRCOLOUR = SDR10 ? IDDCX_BITS_PER_COMPONENT_10 : IDDCX_BITS_PER_COMPONENT_8; - ColourFormat = GetStringSetting(L"ColourFormat"); - - //Cursor - hardwareCursor = EnabledQuery(L"HardwareCursorEnabled"); - alphaCursorSupport = EnabledQuery(L"AlphaCursorSupport"); - CursorMaxX = GetIntegerSetting(L"CursorMaxX"); - CursorMaxY = GetIntegerSetting(L"CursorMaxY"); - - int xorCursorSupportLevelInt = GetIntegerSetting(L"XorCursorSupportLevel"); - std::string xorCursorSupportLevelName; - - if (xorCursorSupportLevelInt < 0 || xorCursorSupportLevelInt > 3) { - vddlog("w", "Selected Xor Level unsupported, defaulting to IDDCX_XOR_CURSOR_SUPPORT_FULL"); - XorCursorSupportLevel = IDDCX_XOR_CURSOR_SUPPORT_FULL; - } - else { - XorCursorSupportLevel = static_cast(xorCursorSupportLevelInt); - } - - // === LOAD NEW EDID INTEGRATION SETTINGS === - edidIntegrationEnabled = EnabledQuery(L"EdidIntegrationEnabled"); - autoConfigureFromEdid = EnabledQuery(L"AutoConfigureFromEdid"); - edidProfilePath = GetStringSetting(L"EdidProfilePath"); - overrideManualSettings = EnabledQuery(L"OverrideManualSettings"); - fallbackOnError = EnabledQuery(L"FallbackOnError"); - - // === LOAD HDR ADVANCED SETTINGS === - hdr10StaticMetadataEnabled = EnabledQuery(L"Hdr10StaticMetadataEnabled"); - maxDisplayMasteringLuminance = GetDoubleSetting(L"MaxDisplayMasteringLuminance"); - minDisplayMasteringLuminance = GetDoubleSetting(L"MinDisplayMasteringLuminance"); - maxContentLightLevel = GetIntegerSetting(L"MaxContentLightLevel"); - maxFrameAvgLightLevel = GetIntegerSetting(L"MaxFrameAvgLightLevel"); - - colorPrimariesEnabled = EnabledQuery(L"ColorPrimariesEnabled"); - redX = GetDoubleSetting(L"RedX"); - redY = GetDoubleSetting(L"RedY"); - greenX = GetDoubleSetting(L"GreenX"); - greenY = GetDoubleSetting(L"GreenY"); - blueX = GetDoubleSetting(L"BlueX"); - blueY = GetDoubleSetting(L"BlueY"); - whiteX = GetDoubleSetting(L"WhiteX"); - whiteY = GetDoubleSetting(L"WhiteY"); - - colorSpaceEnabled = EnabledQuery(L"ColorSpaceEnabled"); - gammaCorrection = GetDoubleSetting(L"GammaCorrection"); - primaryColorSpace = GetStringSetting(L"PrimaryColorSpace"); - enableMatrixTransform = EnabledQuery(L"EnableMatrixTransform"); - - // === LOAD AUTO RESOLUTIONS SETTINGS === - autoResolutionsEnabled = EnabledQuery(L"AutoResolutionsEnabled"); - sourcePriority = GetStringSetting(L"SourcePriority"); - minRefreshRate = GetIntegerSetting(L"MinRefreshRate"); - maxRefreshRate = GetIntegerSetting(L"MaxRefreshRate"); - excludeFractionalRates = EnabledQuery(L"ExcludeFractionalRates"); - minResolutionWidth = GetIntegerSetting(L"MinResolutionWidth"); - minResolutionHeight = GetIntegerSetting(L"MinResolutionHeight"); - maxResolutionWidth = GetIntegerSetting(L"MaxResolutionWidth"); - maxResolutionHeight = GetIntegerSetting(L"MaxResolutionHeight"); - useEdidPreferred = EnabledQuery(L"UseEdidPreferred"); - fallbackWidth = GetIntegerSetting(L"FallbackWidth"); - fallbackHeight = GetIntegerSetting(L"FallbackHeight"); - fallbackRefresh = GetIntegerSetting(L"FallbackRefresh"); - - // === LOAD COLOR ADVANCED SETTINGS === - autoSelectFromColorSpace = EnabledQuery(L"AutoSelectFromColorSpace"); - forceBitDepth = GetStringSetting(L"ForceBitDepth"); - fp16SurfaceSupport = EnabledQuery(L"Fp16SurfaceSupport"); - wideColorGamut = EnabledQuery(L"WideColorGamut"); - hdrToneMapping = EnabledQuery(L"HdrToneMapping"); - sdrWhiteLevel = GetDoubleSetting(L"SdrWhiteLevel"); - - // === LOAD MONITOR EMULATION SETTINGS === - monitorEmulationEnabled = EnabledQuery(L"MonitorEmulationEnabled"); - emulatePhysicalDimensions = EnabledQuery(L"EmulatePhysicalDimensions"); - physicalWidthMm = GetIntegerSetting(L"PhysicalWidthMm"); - physicalHeightMm = GetIntegerSetting(L"PhysicalHeightMm"); - manufacturerEmulationEnabled = EnabledQuery(L"ManufacturerEmulationEnabled"); - manufacturerName = GetStringSetting(L"ManufacturerName"); - modelName = GetStringSetting(L"ModelName"); - serialNumber = GetStringSetting(L"SerialNumber"); - - xorCursorSupportLevelName = XorCursorSupportLevelToString(XorCursorSupportLevel); - - vddlog("i", ("Selected Xor Cursor Support Level: " + xorCursorSupportLevelName).c_str()); - - - - vddlog("i", "Driver Starting"); - string utf8_confpath = WStringToString(confpath); - string logtext = "VDD Path: " + utf8_confpath; - vddlog("i", logtext.c_str()); - LogIddCxVersion(); - - Status = WdfDriverCreate(pDriverObject, pRegistryPath, &Attributes, &Config, WDF_NO_HANDLE); - if (!NT_SUCCESS(Status)) - { - return Status; - } - - StartNamedPipeServer(); - - return Status; -} + void Cleanup() + { + if (pContext) { + delete pContext; + pContext = nullptr; + } + } +}; -vector split(string& input, char delimiter) +struct IndirectMonitorContextWrapper { - istringstream stream(input); - string field; - vector result; - while (getline(stream, field, delimiter)) { - result.push_back(field); - } - return result; -} - - -void loadSettings() { - const wstring settingsname = confpath + L"\\vdd_settings.xml"; - const wstring& filename = settingsname; - if (PathFileExistsW(filename.c_str())) { - CComPtr pStream; - CComPtr pReader; - HRESULT hr = SHCreateStreamOnFileW(filename.c_str(), STGM_READ, &pStream); - if (FAILED(hr)) { - vddlog("e", "Loading Settings: Failed to create file stream."); - return; - } - hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, NULL); - if (FAILED(hr)) { - vddlog("e", "Loading Settings: Failed to create XmlReader."); - return; - } - hr = pReader->SetInput(pStream); - if (FAILED(hr)) { - vddlog("e", "Loading Settings: Failed to set input stream."); - return; - } - - XmlNodeType nodeType; - const WCHAR* pwszLocalName; - const WCHAR* pwszValue; - UINT cwchLocalName; - UINT cwchValue; - wstring currentElement; - wstring width, height, refreshRate; - vector> res; - wstring gpuFriendlyName; - UINT monitorcount = 1; - set> resolutions; - vector globalRefreshRates; - - while (S_OK == (hr = pReader->Read(&nodeType))) { - switch (nodeType) { - case XmlNodeType_Element: - hr = pReader->GetLocalName(&pwszLocalName, &cwchLocalName); - if (FAILED(hr)) { - return; - } - currentElement = wstring(pwszLocalName, cwchLocalName); - break; - case XmlNodeType_Text: - hr = pReader->GetValue(&pwszValue, &cwchValue); - if (FAILED(hr)) { - return; - } - if (currentElement == L"count") { - monitorcount = stoi(wstring(pwszValue, cwchValue)); - if (monitorcount == 0) { - monitorcount = 1; - vddlog("i", "Loading singular monitor (Monitor Count is not valid)"); - } - } - else if (currentElement == L"friendlyname") { - gpuFriendlyName = wstring(pwszValue, cwchValue); - } - else if (currentElement == L"width") { - width = wstring(pwszValue, cwchValue); - if (width.empty()) { - width = L"800"; - } - } - else if (currentElement == L"height") { - height = wstring(pwszValue, cwchValue); - if (height.empty()) { - height = L"600"; - } - resolutions.insert(make_tuple(stoi(width), stoi(height))); - } - else if (currentElement == L"refresh_rate") { - refreshRate = wstring(pwszValue, cwchValue); - if (refreshRate.empty()) { - refreshRate = L"30"; - } - int vsync_num, vsync_den; - float_to_vsync(stof(refreshRate), vsync_num, vsync_den); - - res.push_back(make_tuple(stoi(width), stoi(height), vsync_num, vsync_den)); - stringstream ss; - ss << "Added: " << stoi(width) << "x" << stoi(height) << " @ " << vsync_num << "/" << vsync_den << "Hz"; - vddlog("d", ss.str().c_str()); - } - else if (currentElement == L"g_refresh_rate") { - globalRefreshRates.push_back(stoi(wstring(pwszValue, cwchValue))); - } - break; - } - } - - /* - * This is for res testing, stores each resolution then iterates through each global adding a res for each one - * - - for (const auto& resTuple : resolutions) { - stringstream ss; - ss << get<0>(resTuple) << "x" << get<1>(resTuple); - vddlog("t", ss.str().c_str()); - } - - for (const auto& globalRate : globalRefreshRates) { - stringstream ss; - ss << globalRate << " Hz"; - vddlog("t", ss.str().c_str()); - } - */ - - for (int globalRate : globalRefreshRates) { - for (const auto& resTuple : resolutions) { - int global_width = get<0>(resTuple); - int global_height = get<1>(resTuple); - - int vsync_num, vsync_den; - float_to_vsync(static_cast(globalRate), vsync_num, vsync_den); - res.push_back(make_tuple(global_width, global_height, vsync_num, vsync_den)); - } - } - - /* - * logging all resolutions after added global - * - for (const auto& tup : res) { - stringstream ss; - ss << "(" - << get<0>(tup) << ", " - << get<1>(tup) << ", " - << get<2>(tup) << ", " - << get<3>(tup) << ")"; - vddlog("t", ss.str().c_str()); - } - - */ - - - numVirtualDisplays = monitorcount; - gpuname = gpuFriendlyName; - monitorModes = res; - RebuildKnownMonitorModesCache(); - - // === APPLY EDID INTEGRATION === - if (edidIntegrationEnabled && autoConfigureFromEdid) { - EdidProfileData edidProfile; - if (LoadEdidProfile(edidProfilePath, edidProfile)) { - if (ApplyEdidProfile(edidProfile)) { - vddlog("i", "EDID profile applied successfully"); - } else { - vddlog("w", "EDID profile loaded but not applied (integration disabled)"); - } - } else { - if (fallbackOnError) { - vddlog("w", "EDID profile loading failed, using manual settings"); - } else { - vddlog("e", "EDID profile loading failed and fallback disabled"); - } - } - } - - vddlog("i","Using vdd_settings.xml"); - return; - } - const wstring optionsname = confpath + L"\\option.txt"; - ifstream ifs(optionsname); - if (ifs.is_open()) { - string line; - if (getline(ifs, line) && !line.empty()) { - numVirtualDisplays = stoi(line); - vector> res; - - while (getline(ifs, line)) { - vector strvec = split(line, ','); - if (strvec.size() == 3 && strvec[0].substr(0, 1) != "#") { - int vsync_num, vsync_den; - float_to_vsync(stof(strvec[2]), vsync_num, vsync_den); - res.push_back({ stoi(strvec[0]), stoi(strvec[1]), vsync_num, vsync_den }); - } - } + IndirectMonitorContext* pContext; - vddlog("i", "Using option.txt"); - monitorModes = res; - RebuildKnownMonitorModesCache(); - for (const auto& mode : res) { - int width, height, vsync_num, vsync_den; - tie(width, height, vsync_num, vsync_den) = mode; - stringstream ss; - ss << "Resolution: " << width << "x" << height << " @ " << vsync_num << "/" << vsync_den << "Hz"; - vddlog("d", ss.str().c_str()); + void Cleanup() + { + if (pContext) { + delete pContext; + pContext = nullptr; } - return; - } else { - vddlog("w", "option.txt is empty or the first line is invalid. Enabling Fallback"); } -} +}; +// This macro creates the methods for accessing an IndirectDeviceContextWrapper as a context for a WDF object +WDF_DECLARE_CONTEXT_TYPE(IndirectDeviceContextWrapper); - numVirtualDisplays = 1; - vector> res; - vector> fallbackRes = { - {800, 600, 30.0f}, - {800, 600, 60.0f}, - {800, 600, 90.0f}, - {800, 600, 120.0f}, - {800, 600, 144.0f}, - {800, 600, 165.0f}, - {1280, 720, 30.0f}, - {1280, 720, 60.0f}, - {1280, 720, 90.0f}, - {1280, 720, 130.0f}, - {1280, 720, 144.0f}, - {1280, 720, 165.0f}, - {1366, 768, 30.0f}, - {1366, 768, 60.0f}, - {1366, 768, 90.0f}, - {1366, 768, 120.0f}, - {1366, 768, 144.0f}, - {1366, 768, 165.0f}, - {1920, 1080, 30.0f}, - {1920, 1080, 60.0f}, - {1920, 1080, 90.0f}, - {1920, 1080, 120.0f}, - {1920, 1080, 144.0f}, - {1920, 1080, 165.0f}, - {2560, 1440, 30.0f}, - {2560, 1440, 60.0f}, - {2560, 1440, 90.0f}, - {2560, 1440, 120.0f}, - {2560, 1440, 144.0f}, - {2560, 1440, 165.0f}, - {3840, 2160, 30.0f}, - {3840, 2160, 60.0f}, - {3840, 2160, 90.0f}, - {3840, 2160, 120.0f}, - {3840, 2160, 144.0f}, - {3840, 2160, 165.0f} - }; - - vddlog("i", "Loading Fallback - no settings found"); - - for (const auto& mode : fallbackRes) { - int width, height; - float refreshRate; - tie(width, height, refreshRate) = mode; - - int vsync_num, vsync_den; - float_to_vsync(refreshRate, vsync_num, vsync_den); - - stringstream ss; - res.push_back(make_tuple(width, height, vsync_num, vsync_den)); - - - ss << "Resolution: " << width << "x" << height << " @ " << vsync_num << "/" << vsync_den << "Hz"; - vddlog("d", ss.str().c_str()); - } - - monitorModes = res; - RebuildKnownMonitorModesCache(); - return; +WDF_DECLARE_CONTEXT_TYPE(IndirectMonitorContextWrapper); -} +UINT g_CurConnectorIndex = 0; -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT pDeviceInit) +extern "C" BOOL WINAPI DllMain(_In_ HINSTANCE hInstance,_In_ UINT dwReason,_In_opt_ LPVOID lpReserved) { - NTSTATUS Status = STATUS_SUCCESS; - WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks; - stringstream logStream; - - UNREFERENCED_PARAMETER(Driver); - - logStream << "Initializing device:" - << "\n DeviceInit Pointer: " << static_cast(pDeviceInit); - vddlog("d", logStream.str().c_str()); - - // Register for power callbacks - in this sample only power-on is needed - WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks); - PnpPowerCallbacks.EvtDeviceD0Entry = VirtualDisplayDriverDeviceD0Entry; - WdfDeviceInitSetPnpPowerEventCallbacks(pDeviceInit, &PnpPowerCallbacks); - - IDD_CX_CLIENT_CONFIG IddConfig; - IDD_CX_CLIENT_CONFIG_INIT(&IddConfig); - - logStream.str(""); - logStream << "Configuring IDD_CX client:" - << "\n EvtIddCxAdapterInitFinished: " << (IddConfig.EvtIddCxAdapterInitFinished ? "Set" : "Not Set") - << "\n EvtIddCxMonitorGetDefaultDescriptionModes: " << (IddConfig.EvtIddCxMonitorGetDefaultDescriptionModes ? "Set" : "Not Set") - << "\n EvtIddCxMonitorAssignSwapChain: " << (IddConfig.EvtIddCxMonitorAssignSwapChain ? "Set" : "Not Set") - << "\n EvtIddCxMonitorUnassignSwapChain: " << (IddConfig.EvtIddCxMonitorUnassignSwapChain ? "Set" : "Not Set"); - vddlog("d", logStream.str().c_str()); - - // If the driver wishes to handle custom IoDeviceControl requests, it's necessary to use this callback since IddCx - // redirects IoDeviceControl requests to an internal queue. This sample does not need this. - // IddConfig.EvtIddCxDeviceIoControl = VirtualDisplayDriverIoDeviceControl; - - loadSettings(); - logStream.str(""); - if (gpuname.empty() || gpuname == L"default") { - const wstring adaptername = confpath + L"\\adapter.txt"; - Options.Adapter.load(adaptername.c_str()); - logStream << "Attempting to Load GPU from adapter.txt"; - } - else { - Options.Adapter.xmlprovide(gpuname); - logStream << "Loading GPU from vdd_settings.xml"; - } - vddlog("i", logStream.str().c_str()); - GetGpuInfo(); - - - - IddConfig.EvtIddCxAdapterInitFinished = VirtualDisplayDriverAdapterInitFinished; - - IddConfig.EvtIddCxMonitorGetDefaultDescriptionModes = VirtualDisplayDriverMonitorGetDefaultModes; - IddConfig.EvtIddCxMonitorAssignSwapChain = VirtualDisplayDriverMonitorAssignSwapChain; - IddConfig.EvtIddCxMonitorUnassignSwapChain = VirtualDisplayDriverMonitorUnassignSwapChain; - - if (IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxAdapterQueryTargetInfo)) - { - IddConfig.EvtIddCxAdapterQueryTargetInfo = VirtualDisplayDriverEvtIddCxAdapterQueryTargetInfo; - IddConfig.EvtIddCxMonitorSetDefaultHdrMetaData = VirtualDisplayDriverEvtIddCxMonitorSetDefaultHdrMetadata; - IddConfig.EvtIddCxParseMonitorDescription2 = VirtualDisplayDriverEvtIddCxParseMonitorDescription2; - IddConfig.EvtIddCxMonitorQueryTargetModes2 = VirtualDisplayDriverEvtIddCxMonitorQueryTargetModes2; - IddConfig.EvtIddCxAdapterCommitModes2 = VirtualDisplayDriverEvtIddCxAdapterCommitModes2; - IddConfig.EvtIddCxMonitorSetGammaRamp = VirtualDisplayDriverEvtIddCxMonitorSetGammaRamp; - } - else { - IddConfig.EvtIddCxParseMonitorDescription = VirtualDisplayDriverParseMonitorDescription; - IddConfig.EvtIddCxMonitorQueryTargetModes = VirtualDisplayDriverMonitorQueryModes; - IddConfig.EvtIddCxAdapterCommitModes = VirtualDisplayDriverAdapterCommitModes; - } - - Status = IddCxDeviceInitConfig(pDeviceInit, &IddConfig); - if (!NT_SUCCESS(Status)) - { - logStream.str(""); - logStream << "IddCxDeviceInitConfig failed with status: " << Status; - vddlog("e", logStream.str().c_str()); - return Status; - } - - WDF_OBJECT_ATTRIBUTES Attr; - WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); - Attr.EvtCleanupCallback = [](WDFOBJECT Object) - { - // Automatically cleanup the context when the WDF object is about to be deleted - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Object); - if (pContext) - { - pContext->Cleanup(); - } - }; - - logStream.str(""); - logStream << "Creating device with WdfDeviceCreate:"; - vddlog("d", logStream.str().c_str()); - - WDFDEVICE Device = nullptr; - Status = WdfDeviceCreate(&pDeviceInit, &Attr, &Device); - if (!NT_SUCCESS(Status)) - { - logStream.str(""); - logStream << "WdfDeviceCreate failed with status: " << Status; - vddlog("e", logStream.str().c_str()); - return Status; - } - - Status = IddCxDeviceInitialize(Device); - if (!NT_SUCCESS(Status)) - { - logStream.str(""); - logStream << "IddCxDeviceInitialize failed with status: " << Status; - vddlog("e", logStream.str().c_str()); - return Status; - } - - // Create a new device context object and attach it to the WDF device object - /* - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); - pContext->pContext = new IndirectDeviceContext(Device); - */ // code to return uncase the device context wrapper isnt found (Most likely insufficient resources) - - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); - if (pContext) - { - pContext->pContext = new IndirectDeviceContext(Device); - logStream.str(""); - logStream << "Device context initialized and attached to WDF device."; - vddlog("d", logStream.str().c_str()); - } - else - { - logStream.str(""); - logStream << "Failed to get device context wrapper."; - vddlog("e", logStream.str().c_str()); - return STATUS_INSUFFICIENT_RESOURCES; - } - return Status; -} + //LOG_INFO(""); + UNREFERENCED_PARAMETER(hInstance); + UNREFERENCED_PARAMETER(lpReserved); + UNREFERENCED_PARAMETER(dwReason); + + return TRUE; +} + +//_Use_decl_annotations_ +//extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath) +//{ +// SetUnhandledExceptionFilter(exception_handler); +// init(); +// LOG_INFO("call WdfDriverCreate"); +// +// WDF_DRIVER_CONFIG Config; +// NTSTATUS Status; +// +// WDF_OBJECT_ATTRIBUTES Attributes; +// WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); +// +// WDF_DRIVER_CONFIG_INIT(&Config,VirtualDisplayDeviceAdd); +// +// Config.EvtDriverUnload = EvtDriverUnload; +// +// Status = WdfDriverCreate(pDriverObject, pRegistryPath, &Attributes, &Config, WDF_NO_HANDLE); +// if (!NT_SUCCESS(Status)) +// { +// LOG_ERROR("WdfDriverCreate error: 0x%08x", Status); +// return Status; +// } +// +// return Status; +//} _Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverDeviceD0Entry(WDFDEVICE Device, WDF_POWER_DEVICE_STATE PreviousState) +NTSTATUS VirtualDisplayDeviceD0Entry(WDFDEVICE Device, WDF_POWER_DEVICE_STATE PreviousState) { - //UNREFERENCED_PARAMETER(PreviousState); - - stringstream logStream; - - // Log the entry into D0 state - logStream << "Entering D0 power state:" - << "\n Device Handle: " << static_cast(Device) - << "\n Previous State: " << PreviousState; - vddlog("d", logStream.str().c_str()); - - // This function is called by WDF to start the device in the fully-on power state. - - /* - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); - pContext->pContext->InitAdapter(); - */ //Added error handling incase fails to get device context - - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); - if (pContext && pContext->pContext) - { - logStream.str(""); - logStream << "Initializing adapter..."; - vddlog("d", logStream.str().c_str()); - - - pContext->pContext->InitAdapter(); - logStream.str(""); - logStream << "InitAdapter called successfully."; - vddlog("d", logStream.str().c_str()); - } - else - { - logStream.str(""); - logStream << "Failed to get device context."; - vddlog("e", logStream.str().c_str()); - return STATUS_INSUFFICIENT_RESOURCES; - } - - - return STATUS_SUCCESS; +// UNREFERENCED_PARAMETER(Device); + //UNREFERENCED_PARAMETER(PreviousState); + auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); + if (NULL == pContext || NULL == pContext->pContext) { + LOG_ERROR("Get DeviceObject=0x%p NULL", Device); + return STATUS_INVALID_PARAMETER; + } + + // This function is called by WDF to start the device in the fully-on power state. + + LOG_INFO("call pContext->InitAdapter start, PreviousState=%d", PreviousState); + pContext->pContext->InitAdapter(); + LOG_INFO("call pContext->InitAdapter end"); + return STATUS_SUCCESS; } #pragma region Direct3DDevice Direct3DDevice::Direct3DDevice(LUID AdapterLuid) : AdapterLuid(AdapterLuid) { + } -Direct3DDevice::Direct3DDevice() : AdapterLuid({}) +Direct3DDevice::Direct3DDevice() { + AdapterLuid = LUID{}; } HRESULT Direct3DDevice::Init() { - HRESULT hr; - stringstream logStream; - - // The DXGI factory could be cached, but if a new render adapter appears on the system, a new factory needs to be - // created. If caching is desired, check DxgiFactory->IsCurrent() each time and recreate the factory if !IsCurrent. - - logStream << "Initializing Direct3DDevice..."; - vddlog("d", logStream.str().c_str()); - - hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&DxgiFactory)); - if (FAILED(hr)) - { - logStream.str(""); - logStream << "Failed to create DXGI factory. HRESULT: " << hr; - vddlog("e", logStream.str().c_str()); - return hr; - } - logStream.str(""); - logStream << "DXGI factory created successfully."; - vddlog("d", logStream.str().c_str()); - - // Find the specified render adapter - hr = DxgiFactory->EnumAdapterByLuid(AdapterLuid, IID_PPV_ARGS(&Adapter)); - if (FAILED(hr)) - { - logStream.str(""); - logStream << "Failed to enumerate adapter by LUID. HRESULT: " << hr; - vddlog("e", logStream.str().c_str()); - return hr; - } - - DXGI_ADAPTER_DESC desc; - Adapter->GetDesc(&desc); - logStream.str(""); - logStream << "Adapter found: " << desc.Description << " (Vendor ID: " << desc.VendorId << ", Device ID: " << desc.DeviceId << ")"; - vddlog("i", logStream.str().c_str()); - - -#if 0 // Test code - { - FILE* file; - fopen_s(&file, "C:\\VirtualDisplayDriver\\desc_hdr.bin", "wb"); - - DXGI_ADAPTER_DESC desc; - Adapter->GetDesc(&desc); - - fwrite(&desc, 1, sizeof(desc), file); - fclose(file); - } -#endif - - D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 }; - D3D_FEATURE_LEVEL featureLevel; - - // Create a D3D device using the render adapter. BGRA support is required by the WHQL test suite. - hr = D3D11CreateDevice(Adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &Device, &featureLevel, &DeviceContext); - if (FAILED(hr)) - { - // If creating the D3D device failed, it's possible the render GPU was lost (e.g. detachable GPU) or else the - // system is in a transient state. - logStream.str(""); - logStream << "Failed to create Direct3D device. HRESULT: " << hr; - vddlog("e", logStream.str().c_str()); - logStream.str(""); - logStream << "If creating the D3D device failed, it's possible the render GPU was lost (e.g. detachable GPU) or else the system is in a transient state. " << hr; - vddlog("e", logStream.str().c_str()); - return hr; - } - - logStream.str(""); - logStream << "Direct3D device created successfully. Feature Level: " << featureLevel; - vddlog("i", logStream.str().c_str()); - - return S_OK; + // The DXGI factory could be cached, but if a new render adapter appears on the system, a new factory needs to be + // created. If caching is desired, check DxgiFactory->IsCurrent() each time and recreate the factory if !IsCurrent. + HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&DxgiFactory)); + if (FAILED(hr)) + { + LOG_ERROR("CreateDXGIFactory2 error: 0x%08x", hr); + return hr; + } + + // Find the specified render adapter + hr = DxgiFactory->EnumAdapterByLuid(AdapterLuid, IID_PPV_ARGS(&Adapter)); + if (FAILED(hr)) + { + LOG_ERROR("DxgiFactory->EnumAdapterByLuid error: 0x%08x", hr); + return hr; + } + + // Create a D3D device using the render adapter. BGRA support is required by the WHQL test suite. + hr = D3D11CreateDevice(Adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, + nullptr, 0, D3D11_SDK_VERSION, &Device, nullptr, &DeviceContext); + if (FAILED(hr)) + { + // If creating the D3D device failed, it's possible the render GPU was lost (e.g. detachable GPU) or else the + // system is in a transient state. + LOG_ERROR("D3D11CreateDevice error: 0x%08x", hr); + return hr; + } + + return S_OK; } #pragma endregion #pragma region SwapChainProcessor -SwapChainProcessor::SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, shared_ptr Device, HANDLE NewFrameEvent) - : m_hSwapChain(hSwapChain), m_Device(Device), m_hAvailableBufferEvent(NewFrameEvent) +SwapChainProcessor::SwapChainProcessor(IndirectMonitorContext* monitorContext, const InitChainParam& param, IDDCX_SWAPCHAIN hSwapChain, + shared_ptr Device, HANDLE NewFrameEvent) + : m_monitorContext(monitorContext),m_InitParam(param), m_hSwapChain(hSwapChain), m_Device(Device), m_hAvailableBufferEvent(NewFrameEvent) { - stringstream logStream; - - logStream << "Constructing SwapChainProcessor:" - << "\n SwapChain Handle: " << static_cast(hSwapChain) - << "\n Device Pointer: " << static_cast(Device.get()) - << "\n NewFrameEvent Handle: " << NewFrameEvent; - vddlog("d", logStream.str().c_str()); - - m_hTerminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); - if (!m_hTerminateEvent.Get()) - { - logStream.str(""); - logStream << "Failed to create terminate event. GetLastError: " << GetLastError(); - vddlog("e", logStream.str().c_str()); - } - else - { - logStream.str(""); - logStream << "Terminate event created successfully."; - vddlog("d", logStream.str().c_str()); - } - - // Immediately create and run the swap-chain processing thread, passing 'this' as the thread parameter - m_hThread.Attach(CreateThread(nullptr, 0, RunThread, this, 0, nullptr)); - if (!m_hThread.Get()) - { - logStream.str(""); - logStream << "Failed to create swap-chain processing thread. GetLastError: " << GetLastError(); - vddlog("e", logStream.str().c_str()); - } - else - { - logStream.str(""); - logStream << "Swap-chain processing thread created and started successfully."; - vddlog("d", logStream.str().c_str()); - } + // Immediately create and run the swap-chain processing thread, passing 'this' as the thread parameter + m_hThread.Attach(CreateThread(nullptr, 0, RunThread, this, 0, nullptr)); } SwapChainProcessor::~SwapChainProcessor() { - stringstream logStream; - - logStream << "Destructing SwapChainProcessor:"; - - vddlog("d", logStream.str().c_str()); - // Alert the swap-chain processing thread to terminate - //SetEvent(m_hTerminateEvent.Get()); changed for error handling + log purposes - - if (SetEvent(m_hTerminateEvent.Get())) - { - logStream.str(""); - logStream << "Terminate event signaled successfully."; - vddlog("d", logStream.str().c_str()); - } - else - { - logStream.str(""); - logStream << "Failed to signal terminate event. GetLastError: " << GetLastError(); - vddlog("e", logStream.str().c_str()); - } - - if (m_hThread.Get()) - { - // Wait for the thread to terminate - DWORD waitResult = WaitForSingleObject(m_hThread.Get(), INFINITE); - switch (waitResult) - { - case WAIT_OBJECT_0: - logStream.str(""); - logStream << "Thread terminated successfully."; - vddlog("d", logStream.str().c_str()); - break; - case WAIT_ABANDONED: - logStream.str(""); - logStream << "Thread wait was abandoned. GetLastError: " << GetLastError(); - vddlog("e", logStream.str().c_str()); - break; - case WAIT_TIMEOUT: - logStream.str(""); - logStream << "Thread wait timed out. This should not happen. GetLastError: " << GetLastError(); - vddlog("e", logStream.str().c_str()); - break; - default: - logStream.str(""); - logStream << "Unexpected result from WaitForSingleObject. GetLastError: " << GetLastError(); - vddlog("e", logStream.str().c_str()); - break; - } - } - else - { - logStream.str(""); - logStream << "No valid thread handle to wait for."; - vddlog("e", logStream.str().c_str()); - } + LOG_INFO("display[%d] SetEvent Terminate", m_InitParam.nConnectorIndex); + // Alert the swap-chain processing thread to terminate + isClosed=true; + if (m_hThread.Get()) + { + // Wait for the thread to terminate + WaitForSingleObject(m_hThread.Get(), INFINITE); + LOG_INFO("display[%d] Terminate success", m_InitParam.nConnectorIndex); + } + m_monitorContext=nullptr; } -DWORD CALLBACK SwapChainProcessor::RunThread(LPVOID Argument) +void SwapChainProcessor::ClearEncoder() { - stringstream logStream; - - logStream << "RunThread started. Argument: " << Argument; - vddlog("d", logStream.str().c_str()); +} - reinterpret_cast(Argument)->Run(); - return 0; +DWORD CALLBACK SwapChainProcessor::RunThread(LPVOID Argument) +{ + reinterpret_cast(Argument)->Run(); + return 0; } void SwapChainProcessor::Run() { - stringstream logStream; - - logStream << "Run method started."; - vddlog("d", logStream.str().c_str()); - - // For improved performance, make use of the Multimedia Class Scheduler Service, which will intelligently - // prioritize this thread for improved throughput in high CPU-load scenarios. - DWORD AvTask = 0; - HANDLE AvTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &AvTask); - - if (AvTaskHandle) - { - logStream.str(""); - logStream << "Multimedia thread characteristics set successfully. AvTask: " << AvTask; - vddlog("d", logStream.str().c_str()); - } - else - { - logStream.str(""); - logStream << "Failed to set multimedia thread characteristics. GetLastError: " << GetLastError(); - vddlog("e", logStream.str().c_str()); - } - - RunCore(); - - logStream.str(""); - logStream << "Core processing function RunCore() completed."; - vddlog("d", logStream.str().c_str()); - - // Always delete the swap-chain object when swap-chain processing loop terminates in order to kick the system to - // provide a new swap-chain if necessary. - /* - WdfObjectDelete((WDFOBJECT)m_hSwapChain); - m_hSwapChain = nullptr; added error handling in so its not called to delete swap chain if its not needed. - */ - if (m_hSwapChain) - { - WdfObjectDelete((WDFOBJECT)m_hSwapChain); - logStream.str(""); - logStream << "Swap-chain object deleted."; - vddlog("d", logStream.str().c_str()); - m_hSwapChain = nullptr; - } - else - { - logStream.str(""); - logStream << "No valid swap-chain object to delete."; - vddlog("w", logStream.str().c_str()); - } - /* - AvRevertMmThreadCharacteristics(AvTaskHandle); - */ //error handling when reversing multimedia thread characteristics - if (AvRevertMmThreadCharacteristics(AvTaskHandle)) - { - logStream.str(""); - logStream << "Multimedia thread characteristics reverted successfully."; - vddlog("d", logStream.str().c_str()); - } - else - { - logStream.str(""); - logStream << "Failed to revert multimedia thread characteristics. GetLastError: " << GetLastError(); - vddlog("e", logStream.str().c_str()); - } + // For improved performance, make use of the Multimedia Class Scheduler Service, which will intelligently + // prioritize this thread for improved throughput in high CPU-load scenarios. + DWORD AvTask = 0; + HANDLE AvTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &AvTask); + LOG_INFO("display[%d] RunCore start", m_InitParam.nConnectorIndex); + RunCore(); + LOG_INFO("display[%d] RunCore end", m_InitParam.nConnectorIndex); + ClearEncoder(); + + // Always delete the swap-chain object when swap-chain processing loop terminates in order to kick the system to + // provide a new swap-chain if necessary. + WdfObjectDelete((WDFOBJECT)m_hSwapChain); + m_hSwapChain = nullptr; + + AvRevertMmThreadCharacteristics(AvTaskHandle); } void SwapChainProcessor::RunCore() { - stringstream logStream; - DWORD retryDelay = 1; - const DWORD maxRetryDelay = 100; - int retryCount = 0; - const int maxRetries = 5; - - // Get the DXGI device interface - ComPtr DxgiDevice; - HRESULT hr = m_Device->Device.As(&DxgiDevice); - if (FAILED(hr)) - { - logStream << "Failed to get DXGI device interface. HRESULT: " << hr; - vddlog("e", logStream.str().c_str()); - return; - } - logStream << "DXGI device interface obtained successfully."; - //vddlog("d", logStream.str().c_str()); - - - // Validate that our device is still valid before setting it - if (!m_Device || !m_Device->Device) { - vddlog("e", "Direct3DDevice became invalid during SwapChain processing"); - return; - } - - IDARG_IN_SWAPCHAINSETDEVICE SetDevice = {}; - SetDevice.pDevice = DxgiDevice.Get(); - - hr = IddCxSwapChainSetDevice(m_hSwapChain, &SetDevice); - logStream.str(""); - if (FAILED(hr)) - { - logStream << "Failed to set device to swap chain. HRESULT: " << hr; - vddlog("e", logStream.str().c_str()); - return; - } - logStream << "Device set to swap chain successfully."; - //vddlog("d", logStream.str().c_str()); - - logStream.str(""); - logStream << "Starting buffer acquisition and release loop."; - //vddlog("d", logStream.str().c_str()); - - // Acquire and release buffers in a loop - for (;;) - { - ComPtr AcquiredBuffer; - - // Ask for the next buffer from the producer - IDARG_IN_RELEASEANDACQUIREBUFFER2 BufferInArgs = {}; - BufferInArgs.Size = sizeof(BufferInArgs); - IDXGIResource* pSurface; - - if (IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2)) { - IDARG_OUT_RELEASEANDACQUIREBUFFER2 Buffer = {}; - hr = IddCxSwapChainReleaseAndAcquireBuffer2(m_hSwapChain, &BufferInArgs, &Buffer); - pSurface = Buffer.MetaData.pSurface; - } - else - { - IDARG_OUT_RELEASEANDACQUIREBUFFER Buffer = {}; - hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &Buffer); - pSurface = Buffer.MetaData.pSurface; - } - // AcquireBuffer immediately returns STATUS_PENDING if no buffer is yet available - logStream.str(""); - if (hr == E_PENDING) - { - HANDLE waitHandles[2] = {}; - DWORD waitHandleCount = 0; - - if (m_hAvailableBufferEvent != nullptr && m_hAvailableBufferEvent != INVALID_HANDLE_VALUE) - { - waitHandles[waitHandleCount++] = m_hAvailableBufferEvent; - } - - if (m_hTerminateEvent.Get()) - { - waitHandles[waitHandleCount++] = m_hTerminateEvent.Get(); - } - - if (waitHandleCount == 0) - { - vddlog("e", "No valid wait handles available while waiting for the next frame."); - break; - } - - DWORD WaitResult = WaitForMultipleObjects(waitHandleCount, waitHandles, FALSE, INFINITE); - - logStream << "Buffer acquisition pending. WaitResult: " << WaitResult; - - if (WaitResult == WAIT_OBJECT_0) - { - continue; - } - else if (waitHandleCount > 1 && WaitResult == WAIT_OBJECT_0 + 1) - { - logStream << "Terminate event signaled. Exiting loop."; - break; - } - else if (waitHandleCount == 1 && waitHandles[0] == m_hTerminateEvent.Get() && WaitResult == WAIT_OBJECT_0) - { - logStream << "Terminate event signaled. Exiting loop."; - break; - } - else - { - hr = HRESULT_FROM_WIN32(WaitResult == WAIT_FAILED ? GetLastError() : WaitResult); - logStream << "Unexpected wait result. HRESULT: " << hr; - vddlog("e", logStream.str().c_str()); - break; - } - } - else if (SUCCEEDED(hr)) - { - // Reset retry delay and count on successful buffer acquisition - retryDelay = 1; - retryCount = 0; - - AcquiredBuffer.Attach(pSurface); - - // ============================== - // TODO: Process the frame here - // - // This is the most performance-critical section of code in an IddCx driver. It's important that whatever - // is done with the acquired surface be finished as quickly as possible. This operation could be: - // * a GPU copy to another buffer surface for later processing (such as a staging surface for mapping to CPU memory) - // * a GPU encode operation - // * a GPU VPBlt to another surface - // * a GPU custom compute shader encode operation - // ============================== - - AcquiredBuffer.Reset(); - //vddlog("d", "Reset buffer"); - hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); - if (FAILED(hr)) - { - break; - } - - // ============================== - // TODO: Report frame statistics once the asynchronous encode/send work is completed - // - // Drivers should report information about sub-frame timings, like encode time, send time, etc. - // ============================== - // IddCxSwapChainReportFrameStatistics(m_hSwapChain, ...); - } - else - { - logStream.str(""); // Clear the stream - if (hr == DXGI_ERROR_ACCESS_LOST && retryCount < maxRetries) - { - logStream << "DXGI_ERROR_ACCESS_LOST detected. Retry " << (retryCount + 1) << "/" << maxRetries << " after " << retryDelay << "ms delay."; - vddlog("w", logStream.str().c_str()); - Sleep(retryDelay); - retryDelay = min(retryDelay * 2, maxRetryDelay); - retryCount++; - continue; - } - else - { - if (hr == DXGI_ERROR_ACCESS_LOST) - { - logStream << "DXGI_ERROR_ACCESS_LOST: Maximum retries (" << maxRetries << ") reached. Exiting loop."; - } - else - { - logStream << "Failed to acquire buffer. Exiting loop. HRESULT: " << hr; - } - vddlog("e", logStream.str().c_str()); - // The swap-chain was likely abandoned, so exit the processing loop - break; - } - } - } + DWORD currentRetryDelay = 0; + const DWORD maxRetryDelay = 1000;//1秒内重试 + const DWORD retryDelay=10;//10毫秒一次 + int retryCount = 0; + const int maxRetries = 100;//最多100次 + + // Get the DXGI device interface + ComPtr DxgiDevice; + HRESULT hr = m_Device->Device.As(&DxgiDevice); + if (FAILED(hr)) + { + LOG_ERROR("display[%d] 转成dxgi设备失败。。。",m_InitParam.nConnectorIndex) + return; + } + DXGI_ADAPTER_DESC1 desc; + m_Device->Adapter->GetDesc1(&desc); + LOG_INFO("display[%d] Adapter desc: %S Luid:%04x-%04x VendorId=%04x DeviceId=%04x SubSysId=%04x" + , m_InitParam.nConnectorIndex, + desc.Description, desc.AdapterLuid.HighPart, desc.AdapterLuid.LowPart, + desc.VendorId, desc.DeviceId, desc.SubSysId); + + IDARG_IN_SWAPCHAINSETDEVICE SetDevice = {}; + SetDevice.pDevice = DxgiDevice.Get(); + + hr = IddCxSwapChainSetDevice(m_hSwapChain, &SetDevice); + if (FAILED(hr)) + { + switch (hr) + { + case DXGI_ERROR_ACCESS_LOST: + LOG_INFO("display[%d] IddCxSwapChainSetDevice return DXGI_ERROR_ACCESS_LOST", m_InitParam.nConnectorIndex); + break; + default: + LOG_ERROR("display[%d] IddCxSwapChainSetDevice error: 0x%08x", m_InitParam.nConnectorIndex, hr); + break; + } + return; + } + LOG_INFO("display[%d] IddCxSwapChainSetDevice suc", m_InitParam.nConnectorIndex); + if (displays[m_InitParam.nConnectorIndex].hw_cursor) { + m_monitorContext->SetHwCursorMode();/* 设置硬件鼠标模式 */ + } + for (;;) + { + // Ask for the next buffer from the producer + IDARG_OUT_RELEASEANDACQUIREBUFFER Buffer = {}; + hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &Buffer); + + // AcquireBuffer immediately returns STATUS_PENDING if no buffer is yet available + if (hr == E_PENDING) + { + HANDLE waitHandles[2] = {}; + DWORD waitHandleCount = 0; + if (m_hAvailableBufferEvent != nullptr && m_hAvailableBufferEvent != INVALID_HANDLE_VALUE){ + waitHandles[waitHandleCount++] = m_hAvailableBufferEvent; + } + if (waitHandleCount == 0){ + LOG_ERROR("No valid wait handles available while waiting for the next frame."); + break; + } + DWORD waitResult = WaitForMultipleObjects(waitHandleCount, waitHandles, FALSE, INFINITE); + if(isClosed) { + LOG_INFO("Terminate event signaled. Exiting loop."); + break; + } + if (waitResult == WAIT_OBJECT_0){ //简单条件要置于复杂条件后面 + continue; + } else { + hr = HRESULT_FROM_WIN32(waitResult == WAIT_FAILED ? GetLastError() : waitResult); + LOG_ERROR("Unexpected wait result. HRESULT:0x%08X", hr); + break; + } + } + else if (SUCCEEDED(hr)) + { +// LOG_INFO("display[%d] Swap Chain Success!", m_InitParam.nConnectorIndex); + IDXGIResource* pSurface = Buffer.MetaData.pSurface; + currentRetryDelay = 0; + retryCount = 0; + ComPtr AcquiredBuffer; + AcquiredBuffer.Attach(pSurface); + // ============================== + // TODO: Process the frame here + // + // This is the most performance-critical section of code in an IddCx driver. It's important that whatever + // is done with the acquired surface be finished as quickly as possible. This operation could be: + // * a GPU copy to another buffer surface for later processing (such as a staging surface for mapping to CPU memory) + // * a GPU encode operation + // * a GPU VPBlt to another surface + // * a GPU custom compute shader encode operation + // ============================== + + // ========================================================= + AcquiredBuffer.Reset(); + hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); + if (FAILED(hr)) + { + break; + } + } + else + { + if (hr == DXGI_ERROR_ACCESS_LOST && retryCount < maxRetries) + { + currentRetryDelay = min(currentRetryDelay + retryDelay, maxRetryDelay); +// LOG_WARN("display[%d] DXGI_ERROR_ACCESS_LOST detected. Retry %d/%d after %dms delay.", +// m_InitParam.nConnectorIndex,retryCount + 1, maxRetries, currentRetryDelay); + Sleep(currentRetryDelay); + retryCount++; + continue; + } + else + { + if (hr == DXGI_ERROR_ACCESS_LOST) + { + LOG_ERROR("display[%d] DXGI_ERROR_ACCESS_LOST: Maximum retries (%d) reached. Exiting loop.", m_InitParam.nConnectorIndex, maxRetries); + } + else + { + LOG_ERROR("display[%d] Failed to acquire buffer. Exiting loop. HRESULT:0x%08x", m_InitParam.nConnectorIndex, hr); + } + break; + } + break; + } + } } #pragma endregion #pragma region IndirectDeviceContext -const UINT64 MHZ = 1000000; -const UINT64 KHZ = 1000; - -constexpr DISPLAYCONFIG_VIDEO_SIGNAL_INFO dispinfo(UINT32 h, UINT32 v, UINT32 rn, UINT32 rd) { - const UINT32 clock_rate = rn * (v + 4) * (v + 4) / rd + 1000; - return { - clock_rate, // pixel clock rate [Hz] - { clock_rate, v + 4 }, // fractional horizontal refresh rate [Hz] - { clock_rate, (v + 4) * (v + 4) }, // fractional vertical refresh rate [Hz] - { h, v }, // (horizontal, vertical) active pixel resolution - { h + 4, v + 4 }, // (horizontal, vertical) total pixel resolution - { { 255, 0 }}, // video standard and vsync divider - DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE - }; +IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice) :m_WdfDevice(WdfDevice) +{ + m_Adapter = {}; } -vector hardcodedEdid = +IndirectDeviceContext::~IndirectDeviceContext() { -0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x36, 0x94, 0x37, 0x13, 0xe7, 0x1e, 0xe7, 0x1e, -0x1c, 0x22, 0x01, 0x03, 0x80, 0x32, 0x1f, 0x78, 0x07, 0xee, 0x95, 0xa3, 0x54, 0x4c, 0x99, 0x26, -0x0f, 0x50, 0x54, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, -0x45, 0x00, 0x63, 0xc8, 0x10, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x17, 0xf0, 0x0f, -0xff, 0x37, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, -0x00, 0x56, 0x44, 0x44, 0x20, 0x62, 0x79, 0x20, 0x4d, 0x54, 0x54, 0x0a, 0x20, 0x20, 0x01, 0xc2, -0x02, 0x03, 0x20, 0x40, 0xe6, 0x06, 0x0d, 0x01, 0xa2, 0xa2, 0x10, 0xe3, 0x05, 0xd8, 0x00, 0x67, -0xd8, 0x5d, 0xc4, 0x01, 0x6e, 0x80, 0x00, 0x68, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x0b, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c -}; - - -void modifyEdid(vector& edid) { - if (edid.size() < 12) { - return; - } - - edid[8] = 0x36; - edid[9] = 0x94; - edid[10] = 0x37; - edid[11] = 0x13; } - - -BYTE calculateChecksum(const std::vector& edid) { - int sum = 0; - for (int i = 0; i < 127; ++i) { - sum += edid[i]; - } - sum %= 256; - if (sum != 0) { - sum = 256 - sum; - } - return static_cast(sum); - // check sum calculations. We dont need to include old checksum in calculation, so we only read up to the byte before. - // Anything after the checksum bytes arent part of the checksum - a flaw with edid managment, not with us +void IndirectDeviceContext::InitAdapter() +{ + // ============================== + // TODO: Update the below diagnostic information in accordance with the target hardware. The strings and version + // numbers are used for telemetry and may be displayed to the user in some situations. + // + // This is also where static per-adapter capabilities are determined. + // ============================== + + IDDCX_ADAPTER_CAPS AdapterCaps = {}; + AdapterCaps.Size = sizeof(AdapterCaps); + //AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_REMOTE_SESSION_DRIVER | IDDCX_ADAPTER_FLAGS_CAN_USE_MOVE_REGIONS; +// AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_CAN_USE_MOVE_REGIONS; //矩形移动区域支持 //这个会导致二次连接远程桌面时,窗口内黑屏 +// AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_USE_SMALLEST_MODE;//使用最小模式 +// AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_REMOTE_SESSION_DRIVER;//远程会话标志 +// AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_PREFER_PHYSICAL_GPU;//偏好物理 GPU 渲染 iddcx 2.0版本以上支持 + //if (IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2)) {//开启HDR支持 iddcx 2.0版本以上api支持 + // AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16 ; + // logStream << "FP16 processing capability detected."; + //} +// AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_CAN_USE_MOVE_REGIONS ; + + // Declare basic feature support for the adapter (required) + AdapterCaps.MaxMonitorsSupported = monitorMaxCount; + AdapterCaps.EndPointDiagnostics.Size = sizeof(AdapterCaps.EndPointDiagnostics); + AdapterCaps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE; + AdapterCaps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_WIRED_OTHER; + + // Declare your device strings for telemetry (required) + AdapterCaps.EndPointDiagnostics.pEndPointFriendlyName = L"Virtual Display Device"; + AdapterCaps.EndPointDiagnostics.pEndPointManufacturerName = L"NztVdd"; + AdapterCaps.EndPointDiagnostics.pEndPointModelName = L"Virtual Display Model"; + + // Declare your hardware and firmware versions (required) + IDDCX_ENDPOINT_VERSION Version = {}; + Version.Size = sizeof(Version); + Version.MajorVer = 1; + Version.MinorVer = 0; + AdapterCaps.EndPointDiagnostics.pFirmwareVersion = &Version; + AdapterCaps.EndPointDiagnostics.pHardwareVersion = &Version; + + // Initialize a WDF context that can store a pointer to the device context object + WDF_OBJECT_ATTRIBUTES Attr; + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); + + IDARG_IN_ADAPTER_INIT AdapterInit = {}; + AdapterInit.WdfDevice = m_WdfDevice; + AdapterInit.pCaps = &AdapterCaps; + AdapterInit.ObjectAttributes = &Attr; + + // Start the initialization of the adapter, which will trigger the AdapterFinishInit callback later + IDARG_OUT_ADAPTER_INIT AdapterInitOut; + NTSTATUS Status = IddCxAdapterInitAsync(&AdapterInit, &AdapterInitOut); + + if (NT_SUCCESS(Status)) + { + LOG_INFO("Init AdapterObject=0x%p", AdapterInitOut.AdapterObject); + // Store a reference to the WDF adapter handle + m_Adapter = AdapterInitOut.AdapterObject; + + // Store the device context object into the WDF object context + auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterInitOut.AdapterObject); + pContext->pContext = this; + } + else + { + LOG_ERROR("IddCxAdapterInitAsync error: 0x%08x", Status); + return; + } } - -void updateCeaExtensionCount(vector& edid, int count) { - edid[126] = static_cast(count); +void IndirectDeviceContext::FinishInit(){ + Options.Adapter.apply(m_Adapter); + for (unsigned int i = 0; i < displayCount; i++) { + CreateMonitor(i); + } } -vector loadEdid(const string& filePath) { - if (customEdid) { - vddlog("i", "Attempting to use user Edid"); - } - else { - vddlog("i", "Using hardcoded edid"); - return hardcodedEdid; - } - - ifstream file(filePath, ios::binary | ios::ate); - if (!file) { - vddlog("i", "No custom edid found"); - vddlog("i", "Using hardcoded edid"); - return hardcodedEdid; - } - - streamsize size = file.tellg(); - file.seekg(0, ios::beg); - - vector buffer(size); - if (file.read((char*)buffer.data(), size)) { - //calculate checksum and compare it to 127 byte, if false then return hardcoded if true then return buffer to prevent loading borked edid. - BYTE calculatedChecksum = calculateChecksum(buffer); - if (calculatedChecksum != buffer[127]) { - vddlog("e", "Custom edid failed due to invalid checksum"); - vddlog("i", "Using hardcoded edid"); - return hardcodedEdid; - } - - if (edidCeaOverride) { - if (buffer.size() == 256) { - for (int i = 128; i < 256; ++i) { - buffer[i] = hardcodedEdid[i]; - } - updateCeaExtensionCount(buffer, 1); - } - else if (buffer.size() == 128) { - buffer.insert(buffer.end(), hardcodedEdid.begin() + 128, hardcodedEdid.end()); - updateCeaExtensionCount(buffer, 1); - } - } - - vddlog("i", "Using custom edid"); - return buffer; - } - else { - vddlog("i", "Using hardcoded edid"); - return hardcodedEdid; - } -} -int maincalc() { - vector edid = loadEdid(WStringToString(confpath) + "\\user_edid.bin"); +void IndirectDeviceContext::CreateMonitor(UINT displayIndex) +{ + LOG_DEBUG("开始创建虚拟显示器,index:%d", displayIndex); + // ============================== + // TODO: In a real driver, the EDID should be retrieved dynamically from a connected physical monitor. The EDIDs + // provided here are purely for demonstration. + // Monitor manufacturers are required to correctly fill in physical monitor attributes in order to allow the OS + // to optimize settings like viewing distance and scale factor. Manufacturers should also use a unique serial + // number every single device to ensure the OS can tell the monitors apart. + // ============================== + g_CurConnectorIndex = displayIndex; + + WDF_OBJECT_ATTRIBUTES attr; + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, IndirectMonitorContextWrapper); + attr.EvtCleanupCallback = [](WDFOBJECT Object) + { + // Automatically cleanup the context when the WDF object is about to be deleted + auto* pContext = WdfObjectGet_IndirectMonitorContextWrapper(Object); + LOG_INFO("EvtCleanupCallback Monitor Object: 0x%p", Object); + if (pContext) + { + pContext->Cleanup(); + } + }; + + // In the sample driver, we report a monitor right away but a real driver would do this when a monitor connection event occurs + IDDCX_MONITOR_INFO monitorInfo = {}; + monitorInfo.Size = sizeof(monitorInfo); + monitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI; + monitorInfo.ConnectorIndex = displayIndex; + monitorInfo.MonitorDescription.Size = sizeof(monitorInfo.MonitorDescription); + monitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID; + /* if the datasize is 0, then the os will call VirtualDisplayMonitorGetDefaultModes instead of VirtualDisplayParseMonitorDescription */ + if (!displays[displayIndex].use_edid) + { + monitorInfo.MonitorDescription.DataSize = 0; + monitorInfo.MonitorDescription.pData = nullptr; + } + else + { + monitorInfo.MonitorDescription.DataSize = (UINT)displays[displayIndex].edid_len; + monitorInfo.MonitorDescription.pData = displays[displayIndex].edid_data; + } - if (!preventManufacturerSpoof) modifyEdid(edid); - BYTE checksum = calculateChecksum(edid); - edid[127] = checksum; - // Setting this variable is depricated, hardcoded edid is either returned or custom in loading edid function - IndirectDeviceContext::s_KnownMonitorEdid = edid; - return 0; + // ============================== + // TODO: The monitor's container ID should be distinct from "this" device's container ID if the monitor is not + // permanently attached to the display adapter device object. The container ID is typically made unique for each + // monitor and can be used to associate the monitor with other devices, like audio or input devices. In this + // sample we generate a random container ID GUID, but it's best practice to choose a stable container ID for a + // unique monitor or to use "this" device's container ID for a permanent/integrated monitor. + // ============================== + + // Create a container ID + CoCreateGuid(&monitorInfo.MonitorContainerId); + LOG_DEBUG("显示器[%d]的guid:%s",monitorInfo.ConnectorIndex, GuidToString(monitorInfo.MonitorContainerId).c_str()); + IDARG_IN_MONITORCREATE monitorCreate = {}; + monitorCreate.ObjectAttributes = &attr; + monitorCreate.pMonitorInfo = &monitorInfo; + // Create a monitor object with the specified monitor descriptor + IDARG_OUT_MONITORCREATE monitorCreateOut; + + NTSTATUS status = IddCxMonitorCreate(m_Adapter, &monitorCreate, &monitorCreateOut); + if (NT_SUCCESS(status)) + { + auto monitor= monitorCreateOut.MonitorObject; + auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(monitor); + pMonitorContextWrapper->pContext = new IndirectMonitorContext(monitorInfo.ConnectorIndex, monitor, m_Adapter); + + // Tell the OS that the monitor has been plugged in + IDARG_OUT_MONITORARRIVAL arrivalOut; + LOG_DEBUG("display[%d] IddCxMonitorArrival MonitorObject=0x%p start", monitorInfo.ConnectorIndex, monitor); + status = IddCxMonitorArrival(monitor, &arrivalOut); + LOG_DEBUG("display[%d] IddCxMonitorArrival MonitorObject=0x%p end luid:%s targetId:%d", monitorInfo.ConnectorIndex, monitorCreateOut.MonitorObject, + (std::to_string(arrivalOut.OsAdapterLuid.LowPart) + "-" + std::to_string(arrivalOut.OsAdapterLuid.HighPart)).c_str(), + arrivalOut.OsTargetId); + if (!NT_SUCCESS(status)) + { + switch (status) + { + case STATUS_INVALID_PARAMETER: + LOG_ERROR("display[%d] IddCxMonitorArrival error: 0x%08x(STATUS_INVALID_PARAMETER)", monitorInfo.ConnectorIndex, status); + break; + case STATUS_NOT_SUPPORTED: + LOG_ERROR("display[%d] IddCxMonitorArrival error: 0x%08x(STATUS_NOT_SUPPORTED)", monitorInfo.ConnectorIndex, status); + break; + default: + LOG_ERROR("display[%d] IddCxMonitorArrival error: 0x%08x", monitorInfo.ConnectorIndex, status); + break; + } + } +// m_iddMonitor[displayIndex] = monitor; + } + else + { + LOG_ERROR("display[%d] IddCxMonitorCreate error: 0x%08x", displayIndex, status); + } + LOG_DEBUG("创建虚拟显示器结束,index:%d", displayIndex); +} + +//void IndirectDeviceContext::DestroyMonitor(UINT displayIndex) { +// if(m_iddMonitor[displayIndex]){ +// IddCxMonitorDeparture(m_iddMonitor[displayIndex]); +// m_iddMonitor[displayIndex] = nullptr; +// } +//} +// +//void IndirectDeviceContext::DestroyMonitors() { +// for (int i = displayCount - 1; i >= 0; i--) +// DestroyMonitor(i); +//} +//void IndirectDeviceContext::CommitModes(_In_ const IDARG_IN_COMMITMODES* pInArgs) +//{ +// for (UINT i = 0; i < pInArgs->PathCount; i++) { +// auto path = &(pInArgs->pPaths[i]); +//// if (path->Flags == IDDCX_PATH_FLAGS_ACTIVE) { +// auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(path->MonitorObject); +// if (pMonitorContextWrapper && pMonitorContextWrapper->pContext) { +// pMonitorContextWrapper->pContext->CommitModes(&path->TargetVideoSignalInfo); +// } +// else { +// LOG_INFO("Get MonitorObject=0x%p NULL", path->MonitorObject); +// } +//// } +// } +//} + +IndirectMonitorContext::IndirectMonitorContext(_In_ UINT ConnectorIndex, _In_ IDDCX_MONITOR Monitor, _In_ IDDCX_ADAPTER Adapter) : +m_ConnectorIndex(ConnectorIndex), m_Monitor(Monitor), m_Adapter(Adapter) +{ + m_hCursorEvent = nullptr;// CreateEvent(nullptr, false, false, nullptr); } -std::shared_ptr IndirectDeviceContext::GetOrCreateDevice(LUID RenderAdapter) +IndirectMonitorContext::~IndirectMonitorContext() +{ + UnassignSwapChain(); +} + +//void IndirectMonitorContext::CommitModes(_In_ const DISPLAYCONFIG_VIDEO_SIGNAL_INFO* pInfo) +//{ +// LOG_INFO("display[%d] totalSize=%dx%d totalSize=%dx%d hSyncFreq=%d/%d vSyncFreq=%d/%d" +// " pixelRate=%d scanLineOrdering=%d vSyncFreqDivider=%d videoStandard=%d", m_ConnectorIndex, +// pInfo->totalSize.cx, pInfo->totalSize.cy, +// pInfo->activeSize.cx, pInfo->activeSize.cy, +// pInfo->hSyncFreq.Numerator, pInfo->hSyncFreq.Denominator, +// pInfo->vSyncFreq.Numerator, pInfo->vSyncFreq.Denominator, +// pInfo->pixelRate, pInfo->scanLineOrdering, +// pInfo->AdditionalSignalInfo.vSyncFreqDivider, pInfo->AdditionalSignalInfo.videoStandard +// ); +// if (pInfo->vSyncFreq.Denominator > 0) { +// m_nFrameRate = int((pInfo->vSyncFreq.Numerator * 1.0) / pInfo->vSyncFreq.Denominator); +// } +//} + +bool IndirectMonitorContext::SetHwCursorMode() { - std::shared_ptr Device; - stringstream logStream; - - logStream << "GetOrCreateDevice called for LUID: " << RenderAdapter.HighPart << "-" << RenderAdapter.LowPart; - vddlog("d", logStream.str().c_str()); - - { - std::lock_guard lock(s_DeviceCacheMutex); - - logStream.str(""); - logStream << "Device cache size: " << s_DeviceCache.size(); - vddlog("d", logStream.str().c_str()); - - auto it = s_DeviceCache.find(RenderAdapter); - if (it != s_DeviceCache.end()) { - Device = it->second; - if (Device) { - logStream.str(""); - logStream << "Reusing cached Direct3DDevice for LUID " << RenderAdapter.HighPart << "-" << RenderAdapter.LowPart; - vddlog("d", logStream.str().c_str()); - return Device; - } else { - logStream.str(""); - logStream << "Cached Direct3DDevice is null for LUID " << RenderAdapter.HighPart << "-" << RenderAdapter.LowPart << ", removing from cache"; - vddlog("d", logStream.str().c_str()); - s_DeviceCache.erase(it); - } - } - } - - logStream.str(""); - logStream << "Creating new Direct3DDevice for LUID " << RenderAdapter.HighPart << "-" << RenderAdapter.LowPart; - vddlog("d", logStream.str().c_str()); - - Device = make_shared(RenderAdapter); - if (FAILED(Device->Init())) { - vddlog("e", "Failed to initialize new Direct3DDevice"); - return nullptr; - } - - { - std::lock_guard lock(s_DeviceCacheMutex); - s_DeviceCache[RenderAdapter] = Device; - logStream.str(""); - logStream << "Created and cached new Direct3DDevice for LUID " << RenderAdapter.HighPart << "-" << RenderAdapter.LowPart << " (cache size now: " << s_DeviceCache.size() << ")"; - vddlog("d", logStream.str().c_str()); - } - - return Device; + if(m_hCursorEvent!=nullptr) + return false; + string name="VirtualDisplayMouse"+std::to_string(m_ConnectorIndex); + auto mouseEvent = CreateEventA(nullptr,false,false,name.c_str()); + if (!mouseEvent){ + LOG_ERROR("Failed to create mouse event. No hardware cursor supported!"); + return false; + } +// LOG_DEBUG("开始设置硬件鼠标,index:%d", m_ConnectorIndex); + IDARG_IN_SETUP_HWCURSOR hwCursor = {}; + hwCursor.hNewCursorDataAvailable = mouseEvent; + hwCursor.CursorInfo.Size = sizeof(IDDCX_CURSOR_CAPS); + hwCursor.CursorInfo.ColorXorCursorSupport = IDDCX_XOR_CURSOR_SUPPORT_FULL; + hwCursor.CursorInfo.AlphaCursorSupport = TRUE; + //在大多数情况下,128是可以的,但为了安全起见,我们将使用512,较旧的英特尔CPU可能限制为64x64 + hwCursor.CursorInfo.MaxX = 128; + hwCursor.CursorInfo.MaxY = 128; + + NTSTATUS Status = IddCxMonitorSetupHardwareCursor(m_Monitor, &hwCursor); + if (!NT_SUCCESS(Status)) { + LOG_ERROR("display[%d]IddCxMonitorSetupHardwareCursor error: 0x%08x", m_ConnectorIndex, Status); + return false; + } + m_hCursorEvent=mouseEvent; + LOG_DEBUG("设置硬件鼠标成功,index:%d", m_ConnectorIndex); + return true; } -void IndirectDeviceContext::CleanupExpiredDevices() +void IndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent) { - std::lock_guard lock(s_DeviceCacheMutex); - - int removed = 0; - for (auto it = s_DeviceCache.begin(); it != s_DeviceCache.end();) { - // With shared_ptr cache, we only remove null devices (shouldn't happen) - if (!it->second) { - it = s_DeviceCache.erase(it); - removed++; - } else { - ++it; - } - } - - if (removed > 0) { - stringstream logStream; - logStream << "Cleaned up " << removed << " null Direct3DDevice references from cache"; - vddlog("d", logStream.str().c_str()); - } + UnassignSwapChain(); + + auto Device = make_shared(RenderAdapter); + if (FAILED(Device->Init())) + { + // It's important to delete the swap-chain if D3D initialization fails, so that the OS knows to generate a new + // swap-chain and try again. + WdfObjectDelete(SwapChain); + } + else + { + // Create a new swap-chain processing thread + InitChainParam param; + param.hAdapter = m_Adapter; + param.hMonitor = m_Monitor; + param.nConnectorIndex = m_ConnectorIndex; +// param.nFramerate = m_nFrameRate; + m_ProcessingThread.reset(new SwapChainProcessor(this,param, SwapChain, Device, NewFrameEvent)); + } +// LOG_DEBUG("完成创建交换链=>%d",m_ConnectorIndex); } -IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice) : - m_WdfDevice(WdfDevice), - m_Adapter(nullptr), - m_Monitor(nullptr), - m_Monitor2(nullptr) +void IndirectMonitorContext::UnassignSwapChain() { - // Initialize Phase 5: Final Integration and Testing - NTSTATUS initStatus = InitializePhase5Integration(); - if (!NT_SUCCESS(initStatus)) { - vddlog("w", "Phase 5 integration initialization completed with warnings"); - } +// pauseStream(m_ConnectorIndex); + if(m_ProcessingThread){ + m_ProcessingThread.reset(); + } + if (m_hCursorEvent) { + CloseHandle(m_hCursorEvent); + m_hCursorEvent = nullptr; + } } -IndirectDeviceContext::~IndirectDeviceContext() -{ - stringstream logStream; - std::map> processingThreads; +#pragma endregion - logStream << "Destroying IndirectDeviceContext. Releasing per-monitor processing threads."; - vddlog("d", logStream.str().c_str()); +#pragma region DDI Callbacks - { - std::lock_guard lock(m_ProcessingThreadsMutex); - processingThreads.swap(m_ProcessingThreads); - } +_Use_decl_annotations_ +NTSTATUS VirtualDisplayAdapterInitFinished(IDDCX_ADAPTER AdapterObject, const IDARG_IN_ADAPTER_INIT_FINISHED* pInArgs) +{ + //UNREFERENCED_PARAMETER(AdapterObject); + //UNREFERENCED_PARAMETER(pInArgs); + auto* pDeviceContextWrapper = WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject); + if (NULL == pDeviceContextWrapper || NULL == pDeviceContextWrapper->pContext) { + LOG_ERROR("Get AdapterObject=0x%p NULL", AdapterObject); + return STATUS_INVALID_PARAMETER; + } - logStream.str(""); - logStream << "Released " << processingThreads.size() << " monitor processing thread(s)."; - vddlog("d", logStream.str().c_str()); + // This is called when the OS has finished setting up the adapter for use by the IddCx driver. It's now possible + // to report attached monitors. + + LOG_DEBUG("call pContext->FinishInit start"); + if (NT_SUCCESS(pInArgs->AdapterInitStatus)) + { + pDeviceContextWrapper->pContext->FinishInit(); + PipeWorkingContext.adapter=AdapterObject;//设置当前显示适配器 + LOG_DEBUG( "Adapter initialization finished successfully."); + }else { + LOG_ERROR("AdapterInitStatus=0x%08x", pInArgs->AdapterInitStatus); + } + LOG_DEBUG("call pContext->FinishInit end"); + return STATUS_SUCCESS; } -#define NUM_VIRTUAL_DISPLAYS 1 //What is this even used for ?? Its never referenced - -void IndirectDeviceContext::InitAdapter() +_Use_decl_annotations_ +NTSTATUS VirtualDisplayAdapterCommitModes(IDDCX_ADAPTER AdapterObject, const IDARG_IN_COMMITMODES* pInArgs) { - maincalc(); - stringstream logStream; - - // ============================== - // TODO: Update the below diagnostic information in accordance with the target hardware. The strings and version - // numbers are used for telemetry and may be displayed to the user in some situations. - // - // This is also where static per-adapter capabilities are determined. - // ============================== - - logStream << "Initializing adapter..."; - vddlog("d", logStream.str().c_str()); - logStream.str(""); - - IDDCX_ADAPTER_CAPS AdapterCaps = {}; - AdapterCaps.Size = sizeof(AdapterCaps); - - if (IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2)) { - AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16; - logStream << "FP16 processing capability detected."; - } - - // Declare basic feature support for the adapter (required) - AdapterCaps.MaxMonitorsSupported = numVirtualDisplays; - AdapterCaps.EndPointDiagnostics.Size = sizeof(AdapterCaps.EndPointDiagnostics); - AdapterCaps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE; - AdapterCaps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_WIRED_OTHER; - - // Declare your device strings for telemetry (required) - AdapterCaps.EndPointDiagnostics.pEndPointFriendlyName = L"VirtualDisplayDriver Device"; - AdapterCaps.EndPointDiagnostics.pEndPointManufacturerName = L"MikeTheTech"; - AdapterCaps.EndPointDiagnostics.pEndPointModelName = L"VirtualDisplayDriver Model"; - - // Declare your hardware and firmware versions (required) - IDDCX_ENDPOINT_VERSION Version = {}; - Version.Size = sizeof(Version); - Version.MajorVer = 1; - AdapterCaps.EndPointDiagnostics.pFirmwareVersion = &Version; - AdapterCaps.EndPointDiagnostics.pHardwareVersion = &Version; - - logStream << "Adapter Caps Initialized:" - << "\n Max Monitors Supported: " << AdapterCaps.MaxMonitorsSupported - << "\n Gamma Support: " << AdapterCaps.EndPointDiagnostics.GammaSupport - << "\n Transmission Type: " << AdapterCaps.EndPointDiagnostics.TransmissionType - << "\n Friendly Name: " << AdapterCaps.EndPointDiagnostics.pEndPointFriendlyName - << "\n Manufacturer Name: " << AdapterCaps.EndPointDiagnostics.pEndPointManufacturerName - << "\n Model Name: " << AdapterCaps.EndPointDiagnostics.pEndPointModelName - << "\n Firmware Version: " << Version.MajorVer - << "\n Hardware Version: " << Version.MajorVer; - - vddlog("d", logStream.str().c_str()); - logStream.str(""); - - // Initialize a WDF context that can store a pointer to the device context object - WDF_OBJECT_ATTRIBUTES Attr; - WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); - - IDARG_IN_ADAPTER_INIT AdapterInit = {}; - AdapterInit.WdfDevice = m_WdfDevice; - AdapterInit.pCaps = &AdapterCaps; - AdapterInit.ObjectAttributes = &Attr; - - // Start the initialization of the adapter, which will trigger the AdapterFinishInit callback later - IDARG_OUT_ADAPTER_INIT AdapterInitOut; - NTSTATUS Status = IddCxAdapterInitAsync(&AdapterInit, &AdapterInitOut); - - logStream << "Adapter Initialization Status: " << Status; - vddlog("d", logStream.str().c_str()); - logStream.str(""); - - if (NT_SUCCESS(Status)) - { - // Store a reference to the WDF adapter handle - m_Adapter = AdapterInitOut.AdapterObject; - logStream << "Adapter handle stored successfully."; - vddlog("d", logStream.str().c_str()); - - // Store the device context object into the WDF object context - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterInitOut.AdapterObject); - pContext->pContext = this; - } - else { - logStream << "Failed to initialize adapter. Status: " << Status; - vddlog("e", logStream.str().c_str()); - } + UNREFERENCED_PARAMETER(AdapterObject); + UNREFERENCED_PARAMETER(pInArgs); + //auto* pDeviceContextWrapper = WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject); + //if (NULL == pDeviceContextWrapper || NULL == pDeviceContextWrapper->pContext) { + // LOG_ERROR("Get AdapterObject=0x%p NULL", AdapterObject); + // return STATUS_INVALID_PARAMETER; + //} + ////pInArgs->PathCount + //LOG_DEBUG("打印参数:路径数量:%d", pInArgs->PathCount); + + // 对于示例,当模式被选择时,不执行任何操作 - 交换链由IddCx处理 + // ============================== + // TODO: 在实际的驱动程序中,此函数将用于重新配置设备以应用新模式。 Loop + // 遍历pInArgs->pPaths并查找IDDCX_PATH_FLAGS_ACTIVE。 + // 任何未处于活动状态的路径均为非活动状态(例如,显示器应处于关闭状态)。 + // ============================== + // pDeviceContextWrapper->pContext->CommitModes(pInArgs); + + //for (UINT i = 0; i < pInArgs->PathCount; i++) { + // auto path = &(pInArgs->pPaths[i]); + // // if (path->Flags == IDDCX_PATH_FLAGS_ACTIVE) { + // auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(path->MonitorObject); + // if (pMonitorContextWrapper && pMonitorContextWrapper->pContext) { + // auto pInfo = &path->TargetVideoSignalInfo; + // LOG_INFO("display[%d] totalSize=%dx%d activeSize=%dx%d hSyncFreq=%d/%d vSyncFreq=%d/%d" + // " pixelRate=%d scanLineOrdering=%d vSyncFreqDivider=%d videoStandard=%d", i, + // pInfo->totalSize.cx, pInfo->totalSize.cy, + // pInfo->activeSize.cx, pInfo->activeSize.cy, + // pInfo->hSyncFreq.Numerator, pInfo->hSyncFreq.Denominator, + // pInfo->vSyncFreq.Numerator, pInfo->vSyncFreq.Denominator, + // pInfo->pixelRate, pInfo->scanLineOrdering, + // pInfo->AdditionalSignalInfo.vSyncFreqDivider, pInfo->AdditionalSignalInfo.videoStandard + // ); + // } + // else { + // LOG_INFO("Get MonitorObject=0x%p NULL", path->MonitorObject); + // } + // // } + // } + + + + + + return STATUS_SUCCESS; } -void IndirectDeviceContext::FinishInit() +_Use_decl_annotations_ +NTSTATUS VirtualDisplayParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* pInArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs) { - Options.Adapter.apply(m_Adapter); - vddlog("i", "Applied Adapter configs."); - for (unsigned int i = 0; i < numVirtualDisplays; i++) { - CreateMonitor(i); - } + // ============================== + // TODO: In a real driver, this function would be called to generate monitor modes for an EDID by parsing it. In + // this sample driver, we hard-code the EDID, so this function can generate known modes. + // ============================== + // edid: pInArgs->MonitorDescription.pData, pInArgs->MonitorDescription.DataSize + LOG_DEBUG("display[%d] pInArgs->MonitorModeBufferInputCount=%d, call VirtualDisplayParseMonitorDescription", g_CurConnectorIndex, pInArgs->MonitorModeBufferInputCount); + DisplayInfo* display_info = &displays[g_CurConnectorIndex]; + pOutArgs->MonitorModeBufferOutputCount = UINT(display_info->size_cnt * display_info->fps_cnt); + if (pInArgs->MonitorModeBufferInputCount < pOutArgs->MonitorModeBufferOutputCount) + { + // Return success if there was no buffer, since the caller was only asking for a count of modes + return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; + } + else + { + // In the sample driver, we have reported some static information about connected monitors + // Check which of the reported monitors this call is for by comparing it to the pointer of + // our known EDID blocks. + + int nIndex = 0; + int bestIndex = 0; + for (int item_idx = 0; item_idx < display_info->size_cnt; item_idx++) { + auto item = display_info->size_list[item_idx]; + for (int fps_idx = 0; fps_idx < display_info->fps_cnt; fps_idx++) { + auto fps = display_info->fps_list[fps_idx]; + LOG_DEBUG("正在处理分辨率 %dx%d@%d index:%d", item.cx, item.cy, fps, nIndex); + if (item.cx == display_info->prefer_width && item.cy == display_info->prefer_height && + fps == display_info->prefer_fps) { + pInArgs->pMonitorModes[nIndex] = CreateIddCxMonitorMode(item.cx, item.cy, fps); + LOG_DEBUG("设置当前分辨率为推荐分辨率 %dx%d@%d", item.cx, item.cy, fps); + nIndex++; + break; + } + } + } + if(nIndex==0){ + display_info->prefer_width=1920; + display_info->prefer_height=1080; + display_info->prefer_fps=60; + pInArgs->pMonitorModes[nIndex] = CreateIddCxMonitorMode(display_info->prefer_width, display_info->prefer_height, display_info->prefer_fps); + LOG_DEBUG("分辨率配置错误,设置默认分辨率 %dx%d@%d", display_info->prefer_width, display_info->prefer_height, display_info->prefer_fps); + } +// pInArgs->pMonitorModes[nIndex] = CreateIddCxMonitorMode(item.cx, item.cy, fps); +// if (item.cx == display_info->prefer_width && item.cy == display_info->prefer_height && fps == display_info->prefer_fps) { +// bestIndex = nIndex; +// LOG_DEBUG("设置当前分辨率为推荐分辨率 %dx%d@%d",item.cx,item.cy,fps); +// } +// nIndex++; +// } +// } +// if (bestIndex == -1) { +// LOG_DEBUG("未匹配到合适的分辨率,默认使用第一个"); +// bestIndex = 0; +// } + pOutArgs->PreferredMonitorModeIdx = bestIndex; + return STATUS_SUCCESS; + } } -void IndirectDeviceContext::CreateMonitor(unsigned int index) { - wstring logMessage = L"Creating Monitor: " + to_wstring(index + 1); - string narrowLogMessage = WStringToString(logMessage); - vddlog("i", narrowLogMessage.c_str()); - - // ============================== - // TODO: In a real driver, the EDID should be retrieved dynamically from a connected physical monitor. The EDID - // provided here is purely for demonstration, as it describes only 640x480 @ 60 Hz and 800x600 @ 60 Hz. Monitor - // manufacturers are required to correctly fill in physical monitor attributes in order to allow the OS to optimize - // settings like viewing distance and scale factor. Manufacturers should also use a unique serial number every - // single device to ensure the OS can tell the monitors apart. - // ============================== - - WDF_OBJECT_ATTRIBUTES Attr; - WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); - - IDDCX_MONITOR_INFO MonitorInfo = {}; - MonitorInfo.Size = sizeof(MonitorInfo); - MonitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI; - MonitorInfo.ConnectorIndex = index; - MonitorInfo.MonitorDescription.Size = sizeof(MonitorInfo.MonitorDescription); - MonitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID; - //MonitorInfo.MonitorDescription.DataSize = sizeof(s_KnownMonitorEdid); can no longer use size of as converted to vector - if (s_KnownMonitorEdid.size() > UINT_MAX) - { - vddlog("e", "Edid size passes UINT_Max, escape to prevent loading borked display"); - } - else - { - MonitorInfo.MonitorDescription.DataSize = static_cast(s_KnownMonitorEdid.size()); - } - //MonitorInfo.MonitorDescription.pData = const_cast(s_KnownMonitorEdid); - // Changed from using const_cast to data() to safely access the EDID data. - // This improves type safety and code readability, as it eliminates the need for casting - // and ensures we are directly working with the underlying container of known monitor EDID data. - MonitorInfo.MonitorDescription.pData = IndirectDeviceContext::s_KnownMonitorEdid.data(); - - - - - - - // ============================== - // TODO: The monitor's container ID should be distinct from "this" device's container ID if the monitor is not - // permanently attached to the display adapter device object. The container ID is typically made unique for each - // monitor and can be used to associate the monitor with other devices, like audio or input devices. In this - // sample we generate a random container ID GUID, but it's best practice to choose a stable container ID for a - // unique monitor or to use "this" device's container ID for a permanent/integrated monitor. - // ============================== - - // Create a container ID - CoCreateGuid(&MonitorInfo.MonitorContainerId); - vddlog("d", "Created container ID"); - - IDARG_IN_MONITORCREATE MonitorCreate = {}; - MonitorCreate.ObjectAttributes = &Attr; - MonitorCreate.pMonitorInfo = &MonitorInfo; - - // Create a monitor object with the specified monitor descriptor - IDARG_OUT_MONITORCREATE MonitorCreateOut; - NTSTATUS Status = IddCxMonitorCreate(m_Adapter, &MonitorCreate, &MonitorCreateOut); - if (NT_SUCCESS(Status)) - { - vddlog("d", "Monitor created successfully."); - m_Monitor = MonitorCreateOut.MonitorObject; - - // Associate the monitor with this device context - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorCreateOut.MonitorObject); - pContext->pContext = this; - - // Tell the OS that the monitor has been plugged in - IDARG_OUT_MONITORARRIVAL ArrivalOut; - Status = IddCxMonitorArrival(m_Monitor, &ArrivalOut); - if (NT_SUCCESS(Status)) - { - vddlog("d", "Monitor arrival successfully reported."); - } - else - { - stringstream ss; - ss << "Failed to report monitor arrival. Status: " << Status; - vddlog("e", ss.str().c_str()); - } - } - else - { - stringstream ss; - ss << "Failed to create monitor. Status: " << Status; - vddlog("e", ss.str().c_str()); - } +_Use_decl_annotations_ +NTSTATUS VirtualDisplayMonitorGetDefaultModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* pInArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* pOutArgs) +{ + LOG_DEBUG("未使用edid加载,使用简单显示配置支持===================>"); + UNREFERENCED_PARAMETER(MonitorObject); +// UNREFERENCED_PARAMETER(pInArgs); +// UNREFERENCED_PARAMETER(pOutArgs); + // ============================== + // TODO: 驱动程序应报告传输协议和几乎所有监视器(如640x480、800x600或1024x768)都保证支持的模式。如果驾驶员可以从EDID以外的描述符访问监控模式,则这些模式也将在此处报告。 + // ============================== + if (pInArgs->DefaultMonitorModeBufferInputCount == 0) + { + pOutArgs->DefaultMonitorModeBufferOutputCount = 1; //默认我们只支持一个默认 + } + else + { + auto config = displayResFpsList[0]; + pInArgs->pDefaultMonitorModes[0] = CreateIddCxMonitorMode(get<0>(config), get<1>(config), get<2>(config)); + pOutArgs->DefaultMonitorModeBufferOutputCount = 1; + pOutArgs->PreferredMonitorModeIdx = 0; + } + return STATUS_SUCCESS; } -void IndirectDeviceContext::AssignSwapChain(IDDCX_MONITOR Monitor, IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent) +_Use_decl_annotations_ +NTSTATUS VirtualDisplayMonitorQueryModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_QUERYTARGETMODES* pInArgs, IDARG_OUT_QUERYTARGETMODES* pOutArgs) { - // Only cleanup expired devices periodically, not on every assignment - static int assignmentCount = 0; - if (++assignmentCount % 10 == 0) { - CleanupExpiredDevices(); - } - - auto Device = GetOrCreateDevice(RenderAdapter); - if (!Device) - { - vddlog("e", "Failed to get or create Direct3DDevice, deleting existing swap-chain."); - WdfObjectDelete(SwapChain); - return; - } - else - { - std::unique_ptr previousProcessor; - auto newProcessor = std::make_unique(SwapChain, Device, NewFrameEvent); - - { - std::lock_guard lock(m_ProcessingThreadsMutex); - auto& processorSlot = m_ProcessingThreads[Monitor]; - previousProcessor = std::move(processorSlot); - processorSlot = std::move(newProcessor); - } - - if (previousProcessor) { - vddlog("d", "Replaced existing processing thread for this monitor only."); - } - else { - vddlog("d", "Created a new processing thread for this monitor."); - } - - if (hardwareCursor){ - HANDLE mouseEvent = CreateEventA( - nullptr, - false, - false, - "VirtualDisplayDriverMouse" - ); - - if (!mouseEvent) - { - vddlog("e", "Failed to create mouse event. No hardware cursor supported!"); - return; - } - - IDDCX_CURSOR_CAPS cursorInfo = {}; - cursorInfo.Size = sizeof(cursorInfo); - cursorInfo.ColorXorCursorSupport = IDDCX_XOR_CURSOR_SUPPORT_FULL; - cursorInfo.AlphaCursorSupport = alphaCursorSupport; - - cursorInfo.MaxX = CursorMaxX; //Apparently in most cases 128 is fine but for safe guarding we will go 512, older intel cpus may be limited to 64x64 - cursorInfo.MaxY = CursorMaxY; - - //DirectXDevice->QueryMaxCursorSize(&cursorInfo.MaxX, &cursorInfo.MaxY); Experimental to get max cursor size - THIS IS NTO WORKING CODE - - - IDARG_IN_SETUP_HWCURSOR hwCursor = {}; - hwCursor.CursorInfo = cursorInfo; - hwCursor.hNewCursorDataAvailable = mouseEvent; - - NTSTATUS Status = IddCxMonitorSetupHardwareCursor( - Monitor, - &hwCursor - ); - - if (FAILED(Status)) - { - CloseHandle(mouseEvent); - return; - } - - vddlog("d", "Hardware cursor setup completed successfully."); - } - else { - vddlog("d", "Hardware cursor is disabled, Skipped creation."); - } - // At this point, the swap-chain is set up and the hardware cursor is enabled - // Further swap-chain and cursor processing will occur in the new processing thread. - } -} + //UNREFERENCED_PARAMETER(MonitorObject); + //UNREFERENCED_PARAMETER(pInArgs); + //UNREFERENCED_PARAMETER(pOutArgs); + auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject); + if (NULL == pMonitorContextWrapper || NULL == pMonitorContextWrapper->pContext) { + LOG_ERROR("Get MonitorObject=0x%p NULL", MonitorObject); + return STATUS_INVALID_PARAMETER; + } + auto display_idx = pMonitorContextWrapper->pContext->GetConnectorIndex(); + DisplayInfo *display_info = &displays[display_idx]; -void IndirectDeviceContext::UnassignSwapChain(IDDCX_MONITOR Monitor) -{ - std::unique_ptr processorToStop; - - { - std::lock_guard lock(m_ProcessingThreadsMutex); - auto it = m_ProcessingThreads.find(Monitor); - if (it != m_ProcessingThreads.end()) - { - processorToStop = std::move(it->second); - m_ProcessingThreads.erase(it); - } - } - - if (processorToStop) - { - vddlog("i", "Unassigning swapchain for one monitor. Its processing thread will be stopped."); - } - else - { - vddlog("w", "UnassignSwapChain called for a monitor without an active processing thread."); - } -} + //vector TargetModes; -#pragma endregion + // Create a set of modes supported for frame processing and scan-out. These are typically not based on the + // monitor's descriptor and instead are based on the static processing capability of the device. The OS will + // report the available set of modes for a given output as the intersection of monitor modes with target modes. + // edid: pInArgs->MonitorDescription.pData pInArgs->MonitorDescription.DataSize -#pragma region DDI Callbacks + // 获取缓冲区大小 + pOutArgs->TargetModeBufferOutputCount = UINT(display_info->size_cnt * display_info->fps_cnt); + LOG_DEBUG("display[%d] TargetModeBufferInputCount=%d,TargetModeBufferOutputCount=%d, call VirtualDisplayMonitorQueryModes", + display_idx, pInArgs->TargetModeBufferInputCount, pOutArgs->TargetModeBufferOutputCount); + if (pInArgs->TargetModeBufferInputCount == 0) {/* 用于设置缓冲区大小 */ + return STATUS_SUCCESS; + } + else if (pInArgs->TargetModeBufferInputCount < pOutArgs->TargetModeBufferOutputCount) { + LOG_INFO("display[%d] STATUS_BUFFER_TOO_SMALL", display_idx); + return STATUS_BUFFER_TOO_SMALL; + } + else { + int nIndex = 0; + pInArgs->pTargetModes[nIndex] = CreateIddCxTargetMode(display_info->prefer_width, display_info->prefer_height, display_info->prefer_fps); + LOG_DEBUG("创建虚拟显示器目标模式:%dx%d@%d TargetMode index %d", display_info->prefer_width, display_info->prefer_height, display_info->prefer_fps, nIndex) +// for (int item_idx = 0; item_idx < display_info->size_cnt; item_idx++) { +// auto item = display_info->size_list[item_idx]; +// for (int fps_idx = 0; fps_idx < display_info->fps_cnt; fps_idx++) { +// auto fps = display_info->fps_list[fps_idx]; +// pInArgs->pTargetModes[nIndex] = CreateIddCxTargetMode(item.cx, item.cy, fps); +// LOG_DEBUG("创建虚拟显示器目标模式:%dx%d@%d TargetMode index %d", item.cx, item.cy, fps, nIndex) +// nIndex++; +// } +// } + } + return STATUS_SUCCESS; +} _Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverAdapterInitFinished(IDDCX_ADAPTER AdapterObject, const IDARG_IN_ADAPTER_INIT_FINISHED* pInArgs) +NTSTATUS VirtualDisplayMonitorAssignSwapChain(IDDCX_MONITOR MonitorObject, const IDARG_IN_SETSWAPCHAIN* pInArgs) { - // This is called when the OS has finished setting up the adapter for use by the IddCx driver. It's now possible - // to report attached monitors. - - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject); - if (NT_SUCCESS(pInArgs->AdapterInitStatus)) - { - pContext->pContext->FinishInit(); - vddlog("d", "Adapter initialization finished successfully."); - } - else - { - stringstream ss; - ss << "Adapter initialization failed. Status: " << pInArgs->AdapterInitStatus; - vddlog("e", ss.str().c_str()); - } - vddlog("i", "Finished Setting up adapter."); - - - return STATUS_SUCCESS; + //UNREFERENCED_PARAMETER(MonitorObject); + //UNREFERENCED_PARAMETER(pInArgs); + auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject); + if (NULL == pMonitorContextWrapper || NULL == pMonitorContextWrapper->pContext) { + LOG_ERROR("Get MonitorObject=0x%p NULL", MonitorObject); + return STATUS_INVALID_PARAMETER; + } + auto display_idx = pMonitorContextWrapper->pContext->GetConnectorIndex(); + LOG_DEBUG("display[%d] call pContext->AssignSwapChain", display_idx); + pMonitorContextWrapper->pContext->AssignSwapChain(pInArgs->hSwapChain, pInArgs->RenderAdapterLuid, pInArgs->hNextSurfaceAvailable); + return STATUS_SUCCESS; } _Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverAdapterCommitModes(IDDCX_ADAPTER AdapterObject, const IDARG_IN_COMMITMODES* pInArgs) +NTSTATUS VirtualDisplayMonitorUnassignSwapChain(IDDCX_MONITOR MonitorObject) { - UNREFERENCED_PARAMETER(AdapterObject); - UNREFERENCED_PARAMETER(pInArgs); - - // For the sample, do nothing when modes are picked - the swap-chain is taken care of by IddCx - - // ============================== - // TODO: In a real driver, this function would be used to reconfigure the device to commit the new modes. Loop - // through pInArgs->pPaths and look for IDDCX_PATH_FLAGS_ACTIVE. Any path not active is inactive (e.g. the monitor - // should be turned off). - // ============================== - - return STATUS_SUCCESS; + //UNREFERENCED_PARAMETER(MonitorObject); + auto* pMonitorContextWrapper = WdfObjectGet_IndirectMonitorContextWrapper(MonitorObject); + if (NULL == pMonitorContextWrapper || NULL == pMonitorContextWrapper->pContext) { + LOG_ERROR("Get MonitorObject=0x%p NULL", MonitorObject); + return STATUS_INVALID_PARAMETER; + } + auto display_idx = pMonitorContextWrapper->pContext->GetConnectorIndex(); + LOG_DEBUG("display[%d] call pContext->UnassignSwapChain", display_idx); + pMonitorContextWrapper->pContext->UnassignSwapChain(); + return STATUS_SUCCESS; } + _Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* pInArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs) +NTSTATUS VirtualDisplayMonitorSetGammaRamp(IDDCX_MONITOR MonitorObject, const IDARG_IN_SET_GAMMARAMP* pInArgs) { - // ============================== - // TODO: In a real driver, this function would be called to generate monitor modes for an EDID by parsing it. In - // this sample driver, we hard-code the EDID, so this function can generate known modes. - // ============================== - - stringstream logStream; - logStream << "Parsing monitor description. Input buffer count: " << pInArgs->MonitorModeBufferInputCount; - vddlog("d", logStream.str().c_str()); - - RebuildKnownMonitorModesCache(); - pOutArgs->MonitorModeBufferOutputCount = (UINT)monitorModes.size(); - - logStream.str(""); - logStream << "Number of monitor modes generated: " << monitorModes.size(); - vddlog("d", logStream.str().c_str()); - - if (pInArgs->MonitorModeBufferInputCount < monitorModes.size()) - { - logStream.str(""); - logStream << "Buffer too small. Input count: " << pInArgs->MonitorModeBufferInputCount << ", Required: " << monitorModes.size(); - vddlog("w", logStream.str().c_str()); - // Return success if there was no buffer, since the caller was only asking for a count of modes - return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; - } - else - { - // Copy the known modes to the output buffer - for (DWORD ModeIndex = 0; ModeIndex < monitorModes.size(); ModeIndex++) - { - pInArgs->pMonitorModes[ModeIndex].Size = sizeof(IDDCX_MONITOR_MODE); - pInArgs->pMonitorModes[ModeIndex].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; - pInArgs->pMonitorModes[ModeIndex].MonitorVideoSignalInfo = s_KnownMonitorModes2[ModeIndex]; - } - - // Set the preferred mode as represented in the EDID - pOutArgs->PreferredMonitorModeIdx = 0; - vddlog("d", "Monitor description parsed successfully."); - return STATUS_SUCCESS; - } + LOG_INFO("VirtualDisplayMonitorSetGammaRamp===============>"); + UNREFERENCED_PARAMETER(MonitorObject); + UNREFERENCED_PARAMETER(pInArgs); + return STATUS_SUCCESS; } -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverMonitorGetDefaultModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* pInArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* pOutArgs) -{ - UNREFERENCED_PARAMETER(MonitorObject); - UNREFERENCED_PARAMETER(pInArgs); - UNREFERENCED_PARAMETER(pOutArgs); +#pragma endregion - // Should never be called since we create a single monitor with a known EDID in this sample driver. - // ============================== - // TODO: In a real driver, this function would be called to generate monitor modes for a monitor with no EDID. - // Drivers should report modes that are guaranteed to be supported by the transport protocol and by nearly all - // monitors (such 640x480, 800x600, or 1024x768). If the driver has access to monitor modes from a descriptor other - // than an EDID, those modes would also be reported here. - // ============================== +#pragma region Pipe相关 - return STATUS_NOT_IMPLEMENTED; -} +#define PIPE_NAME L"\\\\.\\pipe\\NztVdd" -/// -/// Creates a target mode from the fundamental mode attributes. -/// -void CreateTargetMode(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& Mode, UINT Width, UINT Height, UINT VSyncNum, UINT VSyncDen) -{ - stringstream logStream; - logStream << "Creating target mode with Width: " << Width - << ", Height: " << Height - << ", VSyncNum: " << VSyncNum - << ", VSyncDen: " << VSyncDen; - vddlog("d", logStream.str().c_str()); - - Mode.totalSize.cx = Mode.activeSize.cx = Width; - Mode.totalSize.cy = Mode.activeSize.cy = Height; - Mode.AdditionalSignalInfo.vSyncFreqDivider = 1; - Mode.AdditionalSignalInfo.videoStandard = 255; - Mode.vSyncFreq.Numerator = VSyncNum; - Mode.vSyncFreq.Denominator = VSyncDen; - Mode.hSyncFreq.Numerator = VSyncNum * Height; - Mode.hSyncFreq.Denominator = VSyncDen; - Mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; - Mode.pixelRate = VSyncNum * Width * Height / VSyncDen; - - logStream.str(""); - logStream << "Target mode configured with:" - << "\n Total Size: (" << Mode.totalSize.cx << ", " << Mode.totalSize.cy << ")" - << "\n Active Size: (" << Mode.activeSize.cx << ", " << Mode.activeSize.cy << ")" - << "\n vSync Frequency: " << Mode.vSyncFreq.Numerator << "/" << Mode.vSyncFreq.Denominator - << "\n hSync Frequency: " << Mode.hSyncFreq.Numerator << "/" << Mode.hSyncFreq.Denominator - << "\n Pixel Rate: " << Mode.pixelRate - << "\n Scan Line Ordering: " << Mode.scanLineOrdering; - vddlog("d", logStream.str().c_str()); +HANDLE hPipeThread = NULL; +bool g_Running = true; +mutex g_Mutex; +HANDLE g_pipeHandle = INVALID_HANDLE_VALUE; + +void SendToPipe(const std::string& logMessage) { + if (g_pipeHandle != INVALID_HANDLE_VALUE) { + DWORD bytesWritten; + DWORD logMessageSize = static_cast(logMessage.size()); + WriteFile(g_pipeHandle, logMessage.c_str(), logMessageSize, &bytesWritten, NULL); + } } -void CreateTargetMode(IDDCX_TARGET_MODE& Mode, UINT Width, UINT Height, UINT VSyncNum, UINT VSyncDen) -{ - Mode.Size = sizeof(Mode); - CreateTargetMode(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSyncNum, VSyncDen); +void ReloadDriver() { + // 驱动内部收到 RELOAD 命令时检查 + if (GetSystemMetrics(SM_REMOTESESSION)) { // 当前处于 RDP 远程桌面环境! + SendToPipe("windows using RDP now,Reload reject!!!"); + LOG_WARN("windows using RDP now,Reload reject!!!"); + return; + } + LOG_INFO("Adapter will reinitialize..."); + if(PipeWorkingContext.adapter!= nullptr) { + auto *pContext = WdfObjectGet_IndirectDeviceContextWrapper(PipeWorkingContext.adapter); + if (pContext != nullptr) { + WDFDEVICE wdfDevice = (WDFDEVICE)WdfObjectContextGetObject(pContext);// 使用框架宏,直接反向获取绑定的 WDFDEVICE + if (wdfDevice) { + SendToPipe("ReloadDriver working..."); +// pContext->pContext->DestroyMonitors();//拔除显示器 todo:拔除会造成新的问题。 + PipeWorkingContext.adapter = nullptr;//清理全局指针 + WdfDeviceSetFailed(wdfDevice, WdfDeviceFailedAttemptRestart); // 现在你可以安全地调用了 + LOG_INFO("Adapter reinitialized");//这句理论上永远无法调用执行,因为已经重新加载了 + return; + } + } + } + LOG_WARN("Adapter reinitialize failed,context is null"); } -void CreateTargetMode2(IDDCX_TARGET_MODE2& Mode, UINT Width, UINT Height, UINT VSyncNum, UINT VSyncDen) -{ - stringstream logStream; - logStream << "Creating IDDCX_TARGET_MODE2 with Width: " << Width - << ", Height: " << Height - << ", VSyncNum: " << VSyncNum - << ", VSyncDen: " << VSyncDen; - vddlog("d", logStream.str().c_str()); - - Mode.Size = sizeof(Mode); - - - if (ColourFormat == L"RGB") { - Mode.BitsPerComponent.Rgb = SDRCOLOUR | HDRCOLOUR; - } - else if (ColourFormat == L"YCbCr444") { - Mode.BitsPerComponent.YCbCr444 = SDRCOLOUR | HDRCOLOUR; - } - else if (ColourFormat == L"YCbCr422") { - Mode.BitsPerComponent.YCbCr422 = SDRCOLOUR | HDRCOLOUR; - } - else if (ColourFormat == L"YCbCr420") { - Mode.BitsPerComponent.YCbCr420 = SDRCOLOUR | HDRCOLOUR; - } - else { - Mode.BitsPerComponent.Rgb = SDRCOLOUR | HDRCOLOUR; // Default to RGB - } - - - logStream.str(""); - logStream << "IDDCX_TARGET_MODE2 configured with Size: " << Mode.Size - << " and colour format " << WStringToString(ColourFormat); - vddlog("d", logStream.str().c_str()); - - - CreateTargetMode(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSyncNum, VSyncDen); +void LogIddCxVersion() { + IDARG_OUT_GETVERSION outArgs; + NTSTATUS status = IddCxGetVersion(&outArgs); + + if (NT_SUCCESS(status)) { + char versionStr[16]; + sprintf_s(versionStr, "0x%lx", outArgs.IddCxVersion); + string logMessage = "IDDCX Version: " + string(versionStr); + LOG_INFO(logMessage.c_str()); + } + else { + LOG_ERROR("Failed to get IDDCX version"); + } } -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverMonitorQueryModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_QUERYTARGETMODES* pInArgs, IDARG_OUT_QUERYTARGETMODES* pOutArgs)//////////////////////////////////////////////////////////////////////////////// -{ - UNREFERENCED_PARAMETER(MonitorObject); - - vector TargetModes(monitorModes.size()); - - stringstream logStream; - logStream << "Creating target modes. Number of monitor modes: " << monitorModes.size(); - vddlog("d", logStream.str().c_str()); - - // Create a set of modes supported for frame processing and scan-out. These are typically not based on the - // monitor's descriptor and instead are based on the static processing capability of the device. The OS will - // report the available set of modes for a given output as the intersection of monitor modes with target modes. - - for (int i = 0; i < monitorModes.size(); i++) { - CreateTargetMode(TargetModes[i], std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i]), std::get<3>(monitorModes[i])); - - logStream.str(""); - logStream << "Created target mode " << i << ": Width = " << std::get<0>(monitorModes[i]) - << ", Height = " << std::get<1>(monitorModes[i]) - << ", VSync = " << std::get<2>(monitorModes[i]); - vddlog("d", logStream.str().c_str()); - } - - pOutArgs->TargetModeBufferOutputCount = (UINT)TargetModes.size(); - - logStream.str(""); - logStream << "Number of target modes to output: " << pOutArgs->TargetModeBufferOutputCount; - vddlog("d", logStream.str().c_str()); - - if (pInArgs->TargetModeBufferInputCount >= TargetModes.size()) - { - logStream.str(""); - logStream << "Copying target modes to output buffer."; - vddlog("d", logStream.str().c_str()); - copy(TargetModes.begin(), TargetModes.end(), pInArgs->pTargetModes); - } - else { - logStream.str(""); - logStream << "Input buffer too small. Required: " << TargetModes.size() - << ", Provided: " << pInArgs->TargetModeBufferInputCount; - vddlog("w", logStream.str().c_str()); - } - - return STATUS_SUCCESS; +LUID getSetAdapterLuid() { + AdapterOption& adapterOption = Options.Adapter; + if (!adapterOption.hasTargetAdapter) { + LOG_ERROR("No Gpu Found/Selected"); + } + return adapterOption.adapterLuid; } -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverMonitorAssignSwapChain(IDDCX_MONITOR MonitorObject, const IDARG_IN_SETSWAPCHAIN* pInArgs) + +void GetGpuInfo() { - stringstream logStream; - logStream << "Assigning swap chain:" - << "\n hSwapChain: " << pInArgs->hSwapChain - << "\n RenderAdapterLuid: " << pInArgs->RenderAdapterLuid.LowPart << "-" << pInArgs->RenderAdapterLuid.HighPart - << "\n hNextSurfaceAvailable: " << pInArgs->hNextSurfaceAvailable; - vddlog("d", logStream.str().c_str()); - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject); - pContext->pContext->AssignSwapChain(MonitorObject, pInArgs->hSwapChain, pInArgs->RenderAdapterLuid, pInArgs->hNextSurfaceAvailable); - vddlog("d", "Swap chain assigned successfully."); - return STATUS_SUCCESS; + AdapterOption& adapterOption = Options.Adapter; + if (!adapterOption.hasTargetAdapter) { + LOG_ERROR("No GPU found or set."); + return; + } + try { + string utf8_desc = WStringToString(adapterOption.target_name); + LUID luid = getSetAdapterLuid(); + string logtext = "ASSIGNED GPU: " + utf8_desc +" (LUID: " + std::to_string(luid.LowPart) + "-" + std::to_string(luid.HighPart) + ")"; + LOG_INFO( logtext.c_str()); + } + catch (const exception& e) { + LOG_ERROR(("Error: " + string(e.what())).c_str()); + } } -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverMonitorUnassignSwapChain(IDDCX_MONITOR MonitorObject) -{ - stringstream logStream; - logStream << "Unassigning swap chain for monitor object: " << MonitorObject; - vddlog("d", logStream.str().c_str()); - auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject); - pContext->pContext->UnassignSwapChain(MonitorObject); - vddlog("d", "Swap chain unassigned successfully."); - return STATUS_SUCCESS; +void logAvailableGPUs() { + vector gpus; + ComPtr factory; + if (!SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) { + return; + } + for (UINT i = 0;; i++) { + ComPtr adapter; + if (!SUCCEEDED(factory->EnumAdapters(i, &adapter))) { + break; + } + DXGI_ADAPTER_DESC desc; + if (!SUCCEEDED(adapter->GetDesc(&desc))) { + continue; + } + GPUInfo gpuInfo{ desc.Description, adapter, desc }; + gpus.push_back(gpuInfo); + } + for (const auto& gpu : gpus) { + wstring logMessage = L"GPU Name: "; + logMessage += gpu.desc.Description; + wstring memorySize = L" Memory: "; + memorySize += std::to_wstring(gpu.desc.DedicatedVideoMemory / (1024 * 1024)) + L" MB"; + wstring logText = logMessage + memorySize; + int bufferSize = WideCharToMultiByte(CP_UTF8, 0, logText.c_str(), -1, nullptr, 0, nullptr, nullptr); + if (bufferSize > 0) { + std::string logTextA(bufferSize - 1, '\0'); + WideCharToMultiByte(CP_UTF8, 0, logText.c_str(), -1, &logTextA[0], bufferSize, nullptr, nullptr); + LOG_INFO(logTextA.c_str()); + } + } } -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverEvtIddCxAdapterQueryTargetInfo( - IDDCX_ADAPTER AdapterObject, - IDARG_IN_QUERYTARGET_INFO* pInArgs, - IDARG_OUT_QUERYTARGET_INFO* pOutArgs -) -{ - stringstream logStream; - logStream << "Querying target info for adapter object: " << AdapterObject; - vddlog("d", logStream.str().c_str()); - - UNREFERENCED_PARAMETER(pInArgs); - - pOutArgs->TargetCaps = IDDCX_TARGET_CAPS_HIGH_COLOR_SPACE | IDDCX_TARGET_CAPS_WIDE_COLOR_SPACE; - - if (ColourFormat == L"RGB") { - pOutArgs->DitheringSupport.Rgb = SDRCOLOUR | HDRCOLOUR; - } - else if (ColourFormat == L"YCbCr444") { - pOutArgs->DitheringSupport.YCbCr444 = SDRCOLOUR | HDRCOLOUR; - } - else if (ColourFormat == L"YCbCr422") { - pOutArgs->DitheringSupport.YCbCr422 = SDRCOLOUR | HDRCOLOUR; - } - else if (ColourFormat == L"YCbCr420") { - pOutArgs->DitheringSupport.YCbCr420 = SDRCOLOUR | HDRCOLOUR; - } - else { - pOutArgs->DitheringSupport.Rgb = SDRCOLOUR | HDRCOLOUR; // Default to RGB - } - - logStream.str(""); - logStream << "Target capabilities set to: " << pOutArgs->TargetCaps - << "\nDithering support colour format set to: " << WStringToString(ColourFormat); - vddlog("d", logStream.str().c_str()); - - return STATUS_SUCCESS; +void HandleClient() { + g_pipeHandle = PipeWorkingContext.hPipe; //pWorkContext->hPipe; + LOG_DEBUG("Client Handling Enabled"); + wchar_t buffer[128]; + DWORD bytesRead; + BOOL result = ReadFile(g_pipeHandle, buffer, sizeof(buffer) - sizeof(wchar_t), &bytesRead, NULL); + if (result && bytesRead != 0) { + buffer[bytesRead / sizeof(wchar_t)] = L'\0'; + wstring bufferwstr(buffer); + int bufferSize = WideCharToMultiByte(CP_UTF8, 0, bufferwstr.c_str(), -1, nullptr, 0, nullptr, nullptr); + string bufferstr(bufferSize, 0); + WideCharToMultiByte(CP_UTF8, 0, bufferwstr.c_str(), -1, &bufferstr[0], bufferSize, nullptr, nullptr); + LOG_INFO(bufferstr.c_str()); + if (wcsncmp(buffer, L"RELOAD_DRIVER", 13) == 0) { + LOG_DEBUG("Reloading the driver"); + ReloadDriver(); + } + else if (wcsncmp(buffer, L"LOG_DEBUG", 9) == 0) { + wchar_t* param = buffer + 10; + if (wcsncmp(param, L"true", 4) == 0) { +// UpdateXmlToggleSetting(true, L"debug logging"); + logLevel = 4; + LOG_INFO("Pipe debugging enabled"); + } + else if (wcsncmp(param, L"false", 5) == 0) { +// UpdateXmlToggleSetting(false, L"debuglogging"); + logLevel = 3; + LOG_INFO("Debugging disabled"); + } + //} + //else if (wcsncmp(buffer, L"LOGGING", 7) == 0) { + // wchar_t* param = buffer + 8; + // if (wcsncmp(param, L"true", 4) == 0) { + // UpdateXmlToggleSetting(true, L"logging"); + // logsEnabled = true; + // LOG_INFO("Logging Enabled"); + // } + // else if (wcsncmp(param, L"false", 5) == 0) { + // UpdateXmlToggleSetting(false, L"logging"); + // logsEnabled = false; + // LOG_INFO("Logging disabled"); // We can keep this here just to make it delete the logs on disable + // } + //} + //else if (wcsncmp(buffer, L"HDRPLUS", 7) == 0) { + // wchar_t* param = buffer + 8; + // if (wcsncmp(param, L"true", 4) == 0) { + // UpdateXmlToggleSetting(true, L"HDRPlus"); + // LOG_INFO("HDR+ Enabled"); + // ReloadDriver(hPipe); + // } + // else if (wcsncmp(param, L"false", 5) == 0) { + // UpdateXmlToggleSetting(false, L"HDRPlus"); + // LOG_INFO("HDR+ Disabled"); + // ReloadDriver(hPipe); + // } + //} + //else if (wcsncmp(buffer, L"SDR10", 5) == 0) { + // wchar_t* param = buffer + 6; + // if (wcsncmp(param, L"true", 4) == 0) { + // UpdateXmlToggleSetting(true, L"SDR10bit"); + // LOG_INFO("SDR 10 Bit Enabled"); + // ReloadDriver(hPipe); + // } + // else if (wcsncmp(param, L"false", 5) == 0) { + // UpdateXmlToggleSetting(false, L"SDR10bit"); + // LOG_INFO("SDR 10 Bit Disabled"); + // ReloadDriver(hPipe); + // } + //} + //else if (wcsncmp(buffer, L"CUSTOMEDID", 10) == 0) { + // wchar_t* param = buffer + 11; + // if (wcsncmp(param, L"true", 4) == 0) { + // UpdateXmlToggleSetting(true, L"CustomEdid"); + // LOG_INFO("Custom Edid Enabled"); + // ReloadDriver(hPipe); + // } + // else if (wcsncmp(param, L"false", 5) == 0) { + // UpdateXmlToggleSetting(false, L"CustomEdid"); + // LOG_INFO("Custom Edid Disabled"); + // ReloadDriver(hPipe); + // } + //} + //else if (wcsncmp(buffer, L"PREVENTSPOOF", 12) == 0) { + // wchar_t* param = buffer + 13; + // if (wcsncmp(param, L"true", 4) == 0) { + // UpdateXmlToggleSetting(true, L"PreventSpoof"); + // LOG_INFO("Prevent Spoof Enabled"); + // ReloadDriver(hPipe); + // } + // else if (wcsncmp(param, L"false", 5) == 0) { + // UpdateXmlToggleSetting(false, L"PreventSpoof"); + // LOG_INFO("Prevent Spoof Disabled"); + // ReloadDriver(hPipe); + // } + //} + //else if (wcsncmp(buffer, L"CEAOVERRIDE", 11) == 0) { + // wchar_t* param = buffer + 12; + // if (wcsncmp(param, L"true", 4) == 0) { + // UpdateXmlToggleSetting(true, L"EdidCeaOverride"); + // LOG_INFO("Cea override Enabled"); + // ReloadDriver(hPipe); + // } + // else if (wcsncmp(param, L"false", 5) == 0) { + // UpdateXmlToggleSetting(false, L"EdidCeaOverride"); + // LOG_INFO("Cea override Disabled"); + // ReloadDriver(hPipe); + // } + //} + //else if (wcsncmp(buffer, L"HARDWARECURSOR", 14) == 0) { + // wchar_t* param = buffer + 15; + // if (wcsncmp(param, L"true", 4) == 0) { + // UpdateXmlToggleSetting(true, L"HardwareCursor"); + // LOG_INFO("Hardware Cursor Enabled"); + // ReloadDriver(hPipe); + // } + // else if (wcsncmp(param, L"false", 5) == 0) { + // UpdateXmlToggleSetting(false, L"HardwareCursor"); + // LOG_INFO("Hardware Cursor Disabled"); + // ReloadDriver(hPipe); + // } + //} + //else if (wcsncmp(buffer, L"D3DDEVICEGPU", 12) == 0) { + // LOG_INFO("Retrieving D3D GPU (This information may be inaccurate without reloading the driver first)"); + // InitializeD3DDeviceAndLogGPU(); + // LOG_INFO("Retrieved D3D GPU"); + }else if (wcsncmp(buffer, L"IDDCX_VERSION", 12) == 0) { + LOG_INFO("Logging iddcx version"); + LogIddCxVersion(); + } + else if (wcsncmp(buffer, L"GET_GPUINFO", 14) == 0) { + LOG_INFO("Retrieving Assigned GPU"); + GetGpuInfo(); + LOG_INFO("Retrieved Assigned GPU"); + }else if (wcsncmp(buffer, L"GET_GPUS", 10) == 0) { + LOG_INFO("Logging all GPUs"); + LOG_INFO( "Any GPUs which show twice but you only have one, will most likely be the GPU the driver is attached to"); + logAvailableGPUs(); + LOG_INFO("Logged all GPUs"); +// }else if (wcsncmp(buffer, L"SETGPU", 6) == 0) { //设置指定的gpu,暂时不提供支持 +// std::wstring gpuName = buffer + 7; +// gpuName = gpuName.substr(1, gpuName.size() - 2); +// +// int size_needed = WideCharToMultiByte(CP_UTF8, 0, gpuName.c_str(), static_cast(gpuName.length()), nullptr, 0, nullptr, nullptr); +// std::string gpuNameNarrow(size_needed, 0); +// WideCharToMultiByte(CP_UTF8, 0, gpuName.c_str(), static_cast(gpuName.length()), &gpuNameNarrow[0], size_needed, nullptr, nullptr); +// +// LOG_INFO(("Setting GPU to: " + gpuNameNarrow).c_str()); +// if (UpdateXmlGpuSetting(gpuName.c_str())) { +// LOG_INFO("Gpu Changed, Restarting Driver"); +// } +// else { +// LOG_ERROR( "Failed to update GPU setting in XML. Restarting anyway"); +// } +// ReloadDriver(hPipe); +// }else if (wcsncmp(buffer, L"SETDISPLAYCOUNT", 15) == 0) { //暂不提供通过修改配置来重新加载驱动的支持 +// LOG_INFO( "Setting Display Count"); +// +// int newDisplayCount = 1; +// swscanf_s(buffer + 15, L"%d", &newDisplayCount); +// +// std::wstring displayLog = L"Setting display count to " + std::to_wstring(newDisplayCount); +// LOG_INFO(WStringToString(displayLog).c_str()); +// +// if (UpdateXmlDisplayCountSetting(newDisplayCount)){ +// LOG_INFO("Display Count Changed, Restarting Driver"); +// } +// else { +// LOG_ERROR( "Failed to update display count setting in XML. Restarting anyway"); +// } +// ReloadDriver(hPipe); + //} + //else if (wcsncmp(buffer, L"GETSETTINGS", 11) == 0) { + // //query and return settings + // bool debugEnabled = EnabledQuery(L"DebugLoggingEnabled"); + // bool loggingEnabled = EnabledQuery(L"LoggingEnabled"); + + // wstring settingsResponse = L"SETTINGS "; + // settingsResponse += debugEnabled ? L"DEBUG=true " : L"DEBUG=false "; + // settingsResponse += loggingEnabled ? L"LOG=true" : L"LOG=false"; + + // DWORD bytesWritten; + // DWORD bytesToWrite = static_cast((settingsResponse.length() + 1) * sizeof(wchar_t)); + // WriteFile(hPipe, settingsResponse.c_str(), bytesToWrite, &bytesWritten, NULL); + + } + else if (wcsncmp(buffer, L"PING", 4) == 0) { + SendToPipe("PONG"); + LOG_INFO("Heartbeat Ping"); + } + else { + LOG_ERROR( "Unknown command"); + + size_t size_needed; + wcstombs_s(&size_needed, nullptr, 0, buffer, 0); + std::string narrowString(size_needed, 0); + wcstombs_s(nullptr, &narrowString[0], size_needed, buffer, size_needed); + LOG_ERROR( narrowString.c_str()); + } + } + DisconnectNamedPipe(g_pipeHandle); + CloseHandle(g_pipeHandle); + g_pipeHandle = INVALID_HANDLE_VALUE; // This value determines whether or not all data gets sent back through the pipe or just the handling pipe data } -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverEvtIddCxMonitorSetDefaultHdrMetadata( - IDDCX_MONITOR MonitorObject, - const IDARG_IN_MONITOR_SET_DEFAULT_HDR_METADATA* pInArgs -) -{ - UNREFERENCED_PARAMETER(pInArgs); - - stringstream logStream; - logStream << "=== PROCESSING HDR METADATA REQUEST ==="; - vddlog("d", logStream.str().c_str()); - - logStream.str(""); - logStream << "Monitor Object: " << MonitorObject - << ", HDR10 Metadata Enabled: " << (hdr10StaticMetadataEnabled ? "Yes" : "No") - << ", Color Primaries Enabled: " << (colorPrimariesEnabled ? "Yes" : "No"); - vddlog("d", logStream.str().c_str()); - - // Check if HDR metadata processing is enabled - if (!hdr10StaticMetadataEnabled) { - vddlog("i", "HDR10 static metadata is disabled, skipping metadata configuration"); - return STATUS_SUCCESS; - } - - VddHdrMetadata metadata = {}; - bool hasValidMetadata = false; - - // Priority 1: Use EDID-derived metadata if available - if (edidIntegrationEnabled && autoConfigureFromEdid) { - // First check for monitor-specific metadata - auto storeIt = g_HdrMetadataStore.find(MonitorObject); - if (storeIt != g_HdrMetadataStore.end() && storeIt->second.isValid) { - metadata = storeIt->second; - hasValidMetadata = true; - vddlog("i", "Using monitor-specific EDID-derived HDR metadata"); - } - // If no monitor-specific metadata, check for template metadata from EDID profile - else { - auto templateIt = g_HdrMetadataStore.find(reinterpret_cast(0)); - if (templateIt != g_HdrMetadataStore.end() && templateIt->second.isValid) { - metadata = templateIt->second; - hasValidMetadata = true; - // Store it for this specific monitor for future use - g_HdrMetadataStore[MonitorObject] = metadata; - vddlog("i", "Using template EDID-derived HDR metadata and storing for monitor"); - } - } - } - - // Priority 2: Use manual configuration if no EDID data or manual override - if (!hasValidMetadata || overrideManualSettings) { - if (colorPrimariesEnabled) { - metadata = ConvertManualToSmpteMetadata(); - hasValidMetadata = metadata.isValid; - vddlog("i", "Using manually configured HDR metadata"); - } - } - - // If we still don't have valid metadata, return early - if (!hasValidMetadata) { - vddlog("w", "No valid HDR metadata available, skipping configuration"); - return STATUS_SUCCESS; - } - - // Log the HDR metadata values being applied - logStream.str(""); - logStream << "=== APPLYING SMPTE ST.2086 HDR METADATA ===\n" - << "Red Primary: (" << metadata.display_primaries_x[0] << ", " << metadata.display_primaries_y[0] << ")\n" - << "Green Primary: (" << metadata.display_primaries_x[1] << ", " << metadata.display_primaries_y[1] << ")\n" - << "Blue Primary: (" << metadata.display_primaries_x[2] << ", " << metadata.display_primaries_y[2] << ")\n" - << "White Point: (" << metadata.white_point_x << ", " << metadata.white_point_y << ")\n" - << "Max Mastering Luminance: " << metadata.max_display_mastering_luminance << " (0.0001 cd/m² units)\n" - << "Min Mastering Luminance: " << metadata.min_display_mastering_luminance << " (0.0001 cd/m² units)\n" - << "Max Content Light Level: " << metadata.max_content_light_level << " nits\n" - << "Max Frame Average Light Level: " << metadata.max_frame_avg_light_level << " nits"; - vddlog("i", logStream.str().c_str()); - - // Store the metadata for this monitor - g_HdrMetadataStore[MonitorObject] = metadata; - - // Convert our metadata to the IddCx expected format - // Note: The actual HDR metadata structure would depend on the IddCx version - // For now, we log that the metadata has been processed and stored - - logStream.str(""); - logStream << "HDR metadata successfully configured and stored for monitor " << MonitorObject; - vddlog("i", logStream.str().c_str()); - - // In a full implementation, you would pass the metadata to the IddCx framework here - // The exact API calls would depend on IddCx version and HDR implementation details - // For Phase 2, we focus on the metadata preparation and storage - - return STATUS_SUCCESS; +DWORD WINAPI NamedPipeServer(LPVOID lpParam) { + UNREFERENCED_PARAMETER(lpParam); + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = FALSE; + const wchar_t *sddl = L"D:(A;;GA;;;WD)"; + LOG_INFO("Starting pipe with parameters: D:(A;;GA;;;WD)"); + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl, SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL)) { + DWORD ErrorCode = GetLastError(); + string errorMessage = to_string(ErrorCode); + LOG_ERROR(errorMessage.c_str()); + return 1; + } + HANDLE hPipe; + while (g_Running) { + hPipe = CreateNamedPipeW(PIPE_NAME,PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES,512, 512,0,&sa); + + if (hPipe == INVALID_HANDLE_VALUE) { + DWORD ErrorCode = GetLastError(); + string errorMessage = to_string(ErrorCode); + LOG_ERROR(errorMessage.c_str()); + LocalFree(sa.lpSecurityDescriptor); + return 1; + } + + BOOL connected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + if (connected) { + PipeWorkingContext.hPipe=hPipe; + LOG_INFO("创建命名的Pipe[%s]成功!",WStringToString(PIPE_NAME).c_str()); + HandleClient(); + } + else { + LOG_ERROR("创建命名的Pipe[%s]失败!",WStringToString(PIPE_NAME).c_str()); + CloseHandle(hPipe); + } + } + LocalFree(sa.lpSecurityDescriptor); + return 0; } -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverEvtIddCxParseMonitorDescription2( - const IDARG_IN_PARSEMONITORDESCRIPTION2* pInArgs, - IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs -) -{ - // ============================== - // TODO: In a real driver, this function would be called to generate monitor modes for an EDID by parsing it. In - // this sample driver, we hard-code the EDID, so this function can generate known modes. - // ============================== - - stringstream logStream; - logStream << "Parsing monitor description:" - << "\n MonitorModeBufferInputCount: " << pInArgs->MonitorModeBufferInputCount - << "\n pMonitorModes: " << (pInArgs->pMonitorModes ? "Valid" : "Null"); - vddlog("d", logStream.str().c_str()); - - logStream.str(""); - logStream << "Monitor Modes:"; - for (const auto& mode : monitorModes) - { - logStream << "\n Mode - Width: " << std::get<0>(mode) - << ", Height: " << std::get<1>(mode) - << ", RefreshRate: " << std::get<2>(mode); - } - vddlog("d", logStream.str().c_str()); - - RebuildKnownMonitorModesCache(); - pOutArgs->MonitorModeBufferOutputCount = (UINT)monitorModes.size(); - - if (pInArgs->MonitorModeBufferInputCount < monitorModes.size()) - { - // Return success if there was no buffer, since the caller was only asking for a count of modes - return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; - } - else - { - // Copy the known modes to the output buffer - if (pInArgs->pMonitorModes == nullptr) { - vddlog("e", "pMonitorModes is null but buffer size is sufficient"); - return STATUS_INVALID_PARAMETER; - } - - logStream.str(""); // Clear the stream - logStream << "Writing monitor modes to output buffer:"; - for (DWORD ModeIndex = 0; ModeIndex < monitorModes.size(); ModeIndex++) - { - pInArgs->pMonitorModes[ModeIndex].Size = sizeof(IDDCX_MONITOR_MODE2); - pInArgs->pMonitorModes[ModeIndex].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; - pInArgs->pMonitorModes[ModeIndex].MonitorVideoSignalInfo = s_KnownMonitorModes2[ModeIndex]; - - - if (ColourFormat == L"RGB") { - pInArgs->pMonitorModes[ModeIndex].BitsPerComponent.Rgb = SDRCOLOUR | HDRCOLOUR; - - } - else if (ColourFormat == L"YCbCr444") { - pInArgs->pMonitorModes[ModeIndex].BitsPerComponent.YCbCr444 = SDRCOLOUR | HDRCOLOUR; - } - else if (ColourFormat == L"YCbCr422") { - pInArgs->pMonitorModes[ModeIndex].BitsPerComponent.YCbCr422 = SDRCOLOUR | HDRCOLOUR; - } - else if (ColourFormat == L"YCbCr420") { - pInArgs->pMonitorModes[ModeIndex].BitsPerComponent.YCbCr420 = SDRCOLOUR | HDRCOLOUR; - } - else { - pInArgs->pMonitorModes[ModeIndex].BitsPerComponent.Rgb = SDRCOLOUR | HDRCOLOUR; // Default to RGB - } - - - - logStream << "\n ModeIndex: " << ModeIndex - << "\n Size: " << pInArgs->pMonitorModes[ModeIndex].Size - << "\n Origin: " << pInArgs->pMonitorModes[ModeIndex].Origin - << "\n Colour Format: " << WStringToString(ColourFormat); - } - - vddlog("d", logStream.str().c_str()); - - // Set the preferred mode as represented in the EDID - pOutArgs->PreferredMonitorModeIdx = 0; - - return STATUS_SUCCESS; - } +void StartNamedPipeServer() { + LOG_INFO("Starting Pipe"); + hPipeThread = CreateThread(NULL, 0, NamedPipeServer, NULL, 0, NULL); + if (hPipeThread == NULL) { + DWORD ErrorCode = GetLastError(); + string errorMessage = to_string(ErrorCode); + LOG_ERROR( errorMessage.c_str()); + } + else { + LOG_INFO("Pipe created"); + } } -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverEvtIddCxMonitorQueryTargetModes2( - IDDCX_MONITOR MonitorObject, - const IDARG_IN_QUERYTARGETMODES2* pInArgs, - IDARG_OUT_QUERYTARGETMODES* pOutArgs -) -{ - //UNREFERENCED_PARAMETER(MonitorObject); - stringstream logStream; - - logStream << "Querying target modes:" - << "\n MonitorObject Handle: " << static_cast(MonitorObject) - << "\n TargetModeBufferInputCount: " << pInArgs->TargetModeBufferInputCount; - vddlog("d", logStream.str().c_str()); - - vector TargetModes(monitorModes.size()); - - // Create a set of modes supported for frame processing and scan-out. These are typically not based on the - // monitor's descriptor and instead are based on the static processing capability of the device. The OS will - // report the available set of modes for a given output as the intersection of monitor modes with target modes. - - logStream.str(""); // Clear the stream - logStream << "Creating target modes:"; - - for (int i = 0; i < monitorModes.size(); i++) { - CreateTargetMode2(TargetModes[i], std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i]), std::get<3>(monitorModes[i])); - logStream << "\n TargetModeIndex: " << i - << "\n Width: " << std::get<0>(monitorModes[i]) - << "\n Height: " << std::get<1>(monitorModes[i]) - << "\n RefreshRate: " << std::get<2>(monitorModes[i]); - } - vddlog("d", logStream.str().c_str()); - - pOutArgs->TargetModeBufferOutputCount = (UINT)TargetModes.size(); - - logStream.str(""); - logStream << "Output target modes count: " << pOutArgs->TargetModeBufferOutputCount; - vddlog("d", logStream.str().c_str()); - - if (pInArgs->TargetModeBufferInputCount >= TargetModes.size()) - { - copy(TargetModes.begin(), TargetModes.end(), pInArgs->pTargetModes); - - logStream.str(""); - logStream << "Target modes copied to output buffer:"; - for (int i = 0; i < TargetModes.size(); i++) - { - logStream << "\n TargetModeIndex: " << i - << "\n Size: " << TargetModes[i].Size - << "\n ColourFormat: " << WStringToString(ColourFormat); - } - vddlog("d", logStream.str().c_str()); - } - else - { - vddlog("w", "Input buffer is too small for target modes."); - } - - return STATUS_SUCCESS; +void StopNamedPipeServer() { + LOG_INFO("Stopping Pipe"); + { + lock_guard lock(g_Mutex); + g_Running = false; + } + if (hPipeThread) { + HANDLE hPipe = CreateFileW( + PIPE_NAME, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (hPipe != INVALID_HANDLE_VALUE) { + DisconnectNamedPipe(hPipe); + CloseHandle(hPipe); + } + + WaitForSingleObject(hPipeThread, INFINITE); + CloseHandle(hPipeThread); + hPipeThread = NULL; + LOG_INFO("Stopped Pipe"); + } } -_Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverEvtIddCxAdapterCommitModes2( - IDDCX_ADAPTER AdapterObject, - const IDARG_IN_COMMITMODES2* pInArgs -) -{ - UNREFERENCED_PARAMETER(AdapterObject); - UNREFERENCED_PARAMETER(pInArgs); - return STATUS_SUCCESS; +extern "C" EVT_WDF_DRIVER_UNLOAD EvtDriverUnload; +//驱动卸载事件 +VOID EvtDriverUnload(_In_ WDFDRIVER Driver) +{ + UNREFERENCED_PARAMETER(Driver); + StopNamedPipeServer(); + LOG_INFO( "Driver Unloaded"); } +//驱动入口 _Use_decl_annotations_ -NTSTATUS VirtualDisplayDriverEvtIddCxMonitorSetGammaRamp( - IDDCX_MONITOR MonitorObject, - const IDARG_IN_SET_GAMMARAMP* pInArgs -) +extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath) { - stringstream logStream; - logStream << "=== PROCESSING GAMMA RAMP REQUEST ==="; - vddlog("d", logStream.str().c_str()); - - logStream.str(""); - logStream << "Monitor Object: " << MonitorObject - << ", Color Space Enabled: " << (colorSpaceEnabled ? "Yes" : "No") - << ", Matrix Transform Enabled: " << (enableMatrixTransform ? "Yes" : "No"); - vddlog("d", logStream.str().c_str()); - - // Check if color space processing is enabled - if (!colorSpaceEnabled) { - vddlog("i", "Color space processing is disabled, skipping gamma ramp configuration"); - return STATUS_SUCCESS; - } - - VddGammaRamp gammaRamp = {}; - bool hasValidGammaRamp = false; - - // Priority 1: Use EDID-derived gamma settings if available - if (edidIntegrationEnabled && autoConfigureFromEdid) { - // First check for monitor-specific gamma ramp - auto storeIt = g_GammaRampStore.find(MonitorObject); - if (storeIt != g_GammaRampStore.end() && storeIt->second.isValid) { - gammaRamp = storeIt->second; - hasValidGammaRamp = true; - vddlog("i", "Using monitor-specific EDID-derived gamma ramp"); - } - // If no monitor-specific gamma ramp, check for template from EDID profile - else { - auto templateIt = g_GammaRampStore.find(reinterpret_cast(0)); - if (templateIt != g_GammaRampStore.end() && templateIt->second.isValid) { - gammaRamp = templateIt->second; - hasValidGammaRamp = true; - // Store it for this specific monitor for future use - g_GammaRampStore[MonitorObject] = gammaRamp; - vddlog("i", "Using template EDID-derived gamma ramp and storing for monitor"); - } - } - } - - // Priority 2: Use manual configuration if no EDID data or manual override - if (!hasValidGammaRamp || overrideManualSettings) { - gammaRamp = ConvertManualToGammaRamp(); - hasValidGammaRamp = gammaRamp.isValid; - vddlog("i", "Using manually configured gamma ramp"); - } - - // If we still don't have valid gamma settings, return early - if (!hasValidGammaRamp) { - vddlog("w", "No valid gamma ramp available, skipping configuration"); - return STATUS_SUCCESS; - } - - // Log the gamma ramp values being applied - logStream.str(""); - logStream << "=== APPLYING GAMMA RAMP AND COLOR SPACE TRANSFORM ===\n" - << "Gamma Value: " << gammaRamp.gamma << "\n" - << "Color Space: " << WStringToString(gammaRamp.colorSpace) << "\n" - << "Use Matrix Transform: " << (gammaRamp.useMatrix ? "Yes" : "No"); - vddlog("i", logStream.str().c_str()); - - // Apply gamma ramp based on type - if (pInArgs->Type == IDDCX_GAMMARAMP_TYPE_3x4_COLORSPACE_TRANSFORM && gammaRamp.useMatrix) { - // Apply 3x4 color space transformation matrix - logStream.str(""); - logStream << "Applying 3x4 Color Space Matrix:\n" - << " [" << gammaRamp.matrix.matrix[0][0] << ", " << gammaRamp.matrix.matrix[0][1] << ", " << gammaRamp.matrix.matrix[0][2] << ", " << gammaRamp.matrix.matrix[0][3] << "]\n" - << " [" << gammaRamp.matrix.matrix[1][0] << ", " << gammaRamp.matrix.matrix[1][1] << ", " << gammaRamp.matrix.matrix[1][2] << ", " << gammaRamp.matrix.matrix[1][3] << "]\n" - << " [" << gammaRamp.matrix.matrix[2][0] << ", " << gammaRamp.matrix.matrix[2][1] << ", " << gammaRamp.matrix.matrix[2][2] << ", " << gammaRamp.matrix.matrix[2][3] << "]"; - vddlog("i", logStream.str().c_str()); - - // Store the matrix for this monitor - g_GammaRampStore[MonitorObject] = gammaRamp; - - // In a full implementation, you would apply the matrix to the rendering pipeline here - // The exact API calls would depend on IddCx version and hardware capabilities - - logStream.str(""); - logStream << "3x4 matrix transform applied successfully for monitor " << MonitorObject; - vddlog("i", logStream.str().c_str()); - } - else if (pInArgs->Type == IDDCX_GAMMARAMP_TYPE_RGB256x3x16) { - // Apply traditional RGB gamma ramp - logStream.str(""); - logStream << "Applying RGB 256x3x16 gamma ramp with gamma " << gammaRamp.gamma; - vddlog("i", logStream.str().c_str()); - - // In a full implementation, you would generate and apply RGB lookup tables here - // Based on the gamma value and color space - - logStream.str(""); - logStream << "RGB gamma ramp applied successfully for monitor " << MonitorObject; - vddlog("i", logStream.str().c_str()); - } - else { - logStream.str(""); - logStream << "Unsupported gamma ramp type: " << pInArgs->Type << ", using default gamma processing"; - vddlog("w", logStream.str().c_str()); - } - - // Store the final gamma ramp for this monitor - g_GammaRampStore[MonitorObject] = gammaRamp; - - logStream.str(""); - logStream << "Gamma ramp configuration completed for monitor " << MonitorObject; - vddlog("i", logStream.str().c_str()); - - return STATUS_SUCCESS; -} + SetUnhandledExceptionFilter(exception_handler); + init(); //加载配置 + string version=VERSION; + LOG_INFO("call WdfDriverCreate!version:%s",version.c_str()); -#pragma endregion + WDF_DRIVER_CONFIG Config; + NTSTATUS Status; -#pragma region Phase 5: Final Integration and Testing - -// =========================================== -// PHASE 5: FINAL INTEGRATION AND TESTING -// =========================================== - -struct VddIntegrationStatus { - bool edidParsingEnabled = false; - bool hdrMetadataValid = false; - bool gammaRampValid = false; - bool modeManagementActive = false; - bool configurationValid = false; - wstring lastError = L""; - DWORD errorCount = 0; - DWORD warningCount = 0; -}; + WDF_OBJECT_ATTRIBUTES Attributes; + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); -static VddIntegrationStatus g_IntegrationStatus = {}; + WDF_DRIVER_CONFIG_INIT(&Config,VirtualDisplayDeviceAdd); -NTSTATUS ValidateEdidIntegration() -{ - stringstream logStream; - logStream << "=== EDID INTEGRATION VALIDATION ==="; - vddlog("i", logStream.str().c_str()); - - bool validationPassed = true; - DWORD issues = 0; - - // Check EDID integration settings - bool edidEnabled = EnabledQuery(L"EdidIntegrationEnabled"); - bool autoConfig = EnabledQuery(L"AutoConfigureFromEdid"); - wstring profilePath = GetStringSetting(L"EdidProfilePath"); - - logStream.str(""); - logStream << "EDID Configuration Status:" - << "\n Integration Enabled: " << (edidEnabled ? "Yes" : "No") - << "\n Auto Configuration: " << (autoConfig ? "Yes" : "No") - << "\n Profile Path: " << WStringToString(profilePath); - vddlog("d", logStream.str().c_str()); - - if (!edidEnabled) { - vddlog("w", "EDID integration is disabled - manual configuration mode"); - issues++; - } - - if (profilePath.empty() || profilePath == L"EDID/monitor_profile.xml") { - vddlog("w", "EDID profile path not configured or using default path"); - issues++; - } - - // Validate HDR configuration - bool hdrEnabled = EnabledQuery(L"Hdr10StaticMetadataEnabled"); - bool colorEnabled = EnabledQuery(L"ColorPrimariesEnabled"); - - logStream.str(""); - logStream << "HDR Configuration Status:" - << "\n HDR10 Metadata: " << (hdrEnabled ? "Enabled" : "Disabled") - << "\n Color Primaries: " << (colorEnabled ? "Enabled" : "Disabled"); - vddlog("d", logStream.str().c_str()); - - // Validate mode management - bool autoResEnabled = EnabledQuery(L"AutoResolutionsEnabled"); - wstring localSourcePriority = GetStringSetting(L"SourcePriority"); - - logStream.str(""); - logStream << "Mode Management Status:" - << "\n Auto Resolutions: " << (autoResEnabled ? "Enabled" : "Disabled") - << "\n Source Priority: " << WStringToString(localSourcePriority); - vddlog("d", logStream.str().c_str()); - - // Update integration status - g_IntegrationStatus.edidParsingEnabled = edidEnabled; - g_IntegrationStatus.hdrMetadataValid = hdrEnabled; - g_IntegrationStatus.modeManagementActive = autoResEnabled; - g_IntegrationStatus.configurationValid = (issues < 3); - g_IntegrationStatus.warningCount = issues; - - logStream.str(""); - logStream << "=== VALIDATION SUMMARY ===" - << "\n Total Issues Found: " << issues - << "\n Configuration Valid: " << (g_IntegrationStatus.configurationValid ? "Yes" : "No") - << "\n Integration Status: " << (validationPassed ? "PASSED" : "FAILED"); - vddlog("i", logStream.str().c_str()); - - return validationPassed ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; -} + Config.EvtDriverUnload = EvtDriverUnload; //注册驱动卸载事件 -NTSTATUS PerformanceMonitor() -{ - stringstream logStream; - logStream << "=== PERFORMANCE MONITORING ==="; - vddlog("d", logStream.str().c_str()); - - // Monitor mode generation performance - auto start = chrono::high_resolution_clock::now(); - - // Test mode generation with current configuration - vector> testModes; - // Use the global monitorModes for performance testing - for (const auto& mode : monitorModes) { - testModes.push_back(make_tuple(get<0>(mode), get<1>(mode), get<2>(mode), get<3>(mode))); - } - - auto end = chrono::high_resolution_clock::now(); - auto duration = chrono::duration_cast(end - start); - - logStream.str(""); - logStream << "Performance Metrics:" - << "\n Mode Generation Time: " << duration.count() << " microseconds" - << "\n Total Modes Generated: " << testModes.size() - << "\n Memory Usage: ~" << (testModes.size() * sizeof(tuple)) << " bytes"; - vddlog("i", logStream.str().c_str()); - - // Performance thresholds - if (duration.count() > 10000) { // 10ms threshold - vddlog("w", "Mode generation is slower than expected (>10ms)"); - g_IntegrationStatus.warningCount++; - } - - if (testModes.size() > 100) { - vddlog("w", "Large number of modes generated - consider filtering"); - g_IntegrationStatus.warningCount++; - } - - return STATUS_SUCCESS; + //todo:在这里做了大量的配置初始化,我们使用上面的init来初始化即可 + LogIddCxVersion(); + Status = WdfDriverCreate(pDriverObject, pRegistryPath, &Attributes, &Config, WDF_NO_HANDLE); + if (!NT_SUCCESS(Status)) + { + LOG_ERROR("WdfDriverCreate error: 0x%08x", Status); + return Status; + } + StartNamedPipeServer(); + return Status; } +#pragma endregion -NTSTATUS CreateFallbackConfiguration() -{ - stringstream logStream; - logStream << "=== CREATING FALLBACK CONFIGURATION ==="; - vddlog("i", logStream.str().c_str()); - - // Create safe fallback modes if EDID parsing fails - vector> fallbackModes = { - make_tuple(1920, 1080, 60, 0), // Full HD 60Hz - make_tuple(1366, 768, 60, 0), // Common laptop resolution - make_tuple(1280, 720, 60, 0), // HD 60Hz - make_tuple(800, 600, 60, 0) // Safe fallback - }; - - logStream.str(""); - logStream << "Fallback modes created:"; - for (const auto& mode : fallbackModes) { - logStream << "\n " << get<0>(mode) << "x" << get<1>(mode) << "@" << get<2>(mode) << "Hz"; - } - vddlog("d", logStream.str().c_str()); - - // Create fallback HDR metadata - VddHdrMetadata fallbackHdr = {}; - fallbackHdr.display_primaries_x[0] = 31250; // sRGB red - fallbackHdr.display_primaries_y[0] = 16992; - fallbackHdr.display_primaries_x[1] = 15625; // sRGB green - fallbackHdr.display_primaries_y[1] = 35352; - fallbackHdr.display_primaries_x[2] = 7812; // sRGB blue - fallbackHdr.display_primaries_y[2] = 3906; - fallbackHdr.white_point_x = 15625; // D65 white point - fallbackHdr.white_point_y = 16406; - fallbackHdr.max_display_mastering_luminance = 1000000; // 100 nits - fallbackHdr.min_display_mastering_luminance = 500; // 0.05 nits - fallbackHdr.max_content_light_level = 400; - fallbackHdr.max_frame_avg_light_level = 100; - fallbackHdr.isValid = true; - - logStream.str(""); - logStream << "Fallback HDR metadata (sRGB/D65):" - << "\n Max Mastering Luminance: " << fallbackHdr.max_display_mastering_luminance - << "\n Min Mastering Luminance: " << fallbackHdr.min_display_mastering_luminance - << "\n Max Content Light: " << fallbackHdr.max_content_light_level << " nits"; - vddlog("d", logStream.str().c_str()); - - vddlog("i", "Fallback configuration ready for emergency use"); - return STATUS_SUCCESS; -} +_Use_decl_annotations_ +NTSTATUS VirtualDisplayDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT pDeviceInit){ + LOG_INFO("call IddCxDeviceInitConfig WdfDeviceCreate"); + + NTSTATUS Status = STATUS_SUCCESS; + WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks; + + UNREFERENCED_PARAMETER(Driver); + + // Register for power callbacks - in this sample only power-on is needed + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks); + PnpPowerCallbacks.EvtDeviceD0Entry = VirtualDisplayDeviceD0Entry; + WdfDeviceInitSetPnpPowerEventCallbacks(pDeviceInit, &PnpPowerCallbacks); + + IDD_CX_CLIENT_CONFIG IddConfig; + IDD_CX_CLIENT_CONFIG_INIT(&IddConfig); + // If the driver wishes to handle custom IoDeviceControl requests, + // it's necessary to use this callback since IddCx + // redirects IoDeviceControl requests to an internal queue. This sample does not need this. + // IddConfig.EvtIddCxDeviceIoControl = VirtualDisplayIoDeviceControl; + + GetGpuInfo(); + + IddConfig.EvtIddCxAdapterInitFinished = VirtualDisplayAdapterInitFinished; + + IddConfig.EvtIddCxMonitorGetDefaultDescriptionModes = VirtualDisplayMonitorGetDefaultModes; + IddConfig.EvtIddCxMonitorAssignSwapChain = VirtualDisplayMonitorAssignSwapChain; + IddConfig.EvtIddCxMonitorUnassignSwapChain = VirtualDisplayMonitorUnassignSwapChain; +// if (IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxAdapterQueryTargetInfo){ +// LOG_ERROR("激活客户端配置失败!"); +// +//EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES2 dd; +// IddConfig.EvtIddCxAdapterQueryTargetInfo = VirtualDisplayDriverEvtIddCxAdapterQueryTargetInfo; +// // IddConfig.EvtIddCxMonitorSetDefaultHdrMetaData = VirtualDisplayDriverEvtIddCxMonitorSetDefaultHdrMetadata; +// // IddConfig.EvtIddCxParseMonitorDescription2 = VirtualDisplayDriverEvtIddCxParseMonitorDescription2; +// // IddConfig.EvtIddCxMonitorQueryTargetModes2 = VirtualDisplayDriverEvtIddCxMonitorQueryTargetModes2; +// // IddConfig.EvtIddCxAdapterCommitModes2 = VirtualDisplayDriverEvtIddCxAdapterCommitModes2; +// IddConfig.EvtIddCxMonitorSetGammaRamp = VirtualDisplayMonitorSetGammaRamp; +// }else{ + //windows 10的书写方式 + IddConfig.EvtIddCxParseMonitorDescription = VirtualDisplayParseMonitorDescription; + IddConfig.EvtIddCxMonitorQueryTargetModes = VirtualDisplayMonitorQueryModes; + IddConfig.EvtIddCxAdapterCommitModes = VirtualDisplayAdapterCommitModes; + IddConfig.EvtIddCxMonitorSetGammaRamp = VirtualDisplayMonitorSetGammaRamp; +// } + + Status = IddCxDeviceInitConfig(pDeviceInit, &IddConfig); + if (!NT_SUCCESS(Status)){ + LOG_ERROR("IddCxDeviceInitConfig error: 0x%08x", Status); + return Status; + } + LOG_DEBUG("IddCxDevice inited!"); + loadSettings();//我们在这里加载细节配置 +// initShareStream(); + + WDF_OBJECT_ATTRIBUTES Attr; + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); + Attr.EvtCleanupCallback = [](WDFOBJECT Object) { + // Automatically cleanup the context when the WDF object is about to be deleted + auto *pContext = WdfObjectGet_IndirectDeviceContextWrapper(Object); + LOG_DEBUG("EvtCleanupCallback Device Object: 0x%p", Object); + if (pContext) { + pContext->Cleanup(); + } + }; -NTSTATUS RunComprehensiveDiagnostics() -{ - stringstream logStream; - logStream << "=== COMPREHENSIVE SYSTEM DIAGNOSTICS ==="; - vddlog("i", logStream.str().c_str()); - - NTSTATUS status = STATUS_SUCCESS; - - // Reset diagnostic counters - g_IntegrationStatus.errorCount = 0; - g_IntegrationStatus.warningCount = 0; - - // Test 1: Configuration validation - logStream.str(""); - logStream << "Running Test 1: Configuration Validation"; - vddlog("d", logStream.str().c_str()); - - NTSTATUS configStatus = ValidateEdidIntegration(); - if (!NT_SUCCESS(configStatus)) { - vddlog("e", "Configuration validation failed"); - g_IntegrationStatus.errorCount++; - status = configStatus; - } - - // Test 2: Performance monitoring - logStream.str(""); - logStream << "Running Test 2: Performance Monitoring"; - vddlog("d", logStream.str().c_str()); - - NTSTATUS perfStatus = PerformanceMonitor(); - if (!NT_SUCCESS(perfStatus)) { - vddlog("e", "Performance monitoring failed"); - g_IntegrationStatus.errorCount++; - } - - // Test 3: Fallback configuration - logStream.str(""); - logStream << "Running Test 3: Fallback Configuration"; - vddlog("d", logStream.str().c_str()); - - NTSTATUS fallbackStatus = CreateFallbackConfiguration(); - if (!NT_SUCCESS(fallbackStatus)) { - vddlog("e", "Fallback configuration creation failed"); - g_IntegrationStatus.errorCount++; - } - - // Test 4: Memory and resource validation - logStream.str(""); - logStream << "Running Test 4: Resource Validation"; - vddlog("d", logStream.str().c_str()); - - // Check global stores - size_t hdrStoreSize = g_HdrMetadataStore.size(); - size_t gammaStoreSize = g_GammaRampStore.size(); - - logStream.str(""); - logStream << "Resource Usage:" - << "\n HDR Metadata Store: " << hdrStoreSize << " entries" - << "\n Gamma Ramp Store: " << gammaStoreSize << " entries" - << "\n Known Monitor Modes: " << s_KnownMonitorModes2.size() << " modes"; - vddlog("d", logStream.str().c_str()); - - // Final diagnostic summary - logStream.str(""); - logStream << "=== DIAGNOSTIC SUMMARY ===" - << "\n Tests Run: 4" - << "\n Errors: " << g_IntegrationStatus.errorCount - << "\n Warnings: " << g_IntegrationStatus.warningCount - << "\n Overall Status: " << (NT_SUCCESS(status) ? "PASSED" : "FAILED"); - vddlog("i", logStream.str().c_str()); - - return status; -} + WDFDEVICE Device = nullptr; + Status = WdfDeviceCreate(&pDeviceInit, &Attr, &Device); + if (!NT_SUCCESS(Status)){ + LOG_ERROR("WdfDeviceCreate error: 0x%08x", Status); + return Status; + } -NTSTATUS ValidateAndSanitizeConfiguration() -{ - stringstream logStream; - logStream << "=== CONFIGURATION VALIDATION AND SANITIZATION ==="; - vddlog("i", logStream.str().c_str()); - - DWORD sanitizedSettings = 0; - - // Validate refresh rate settings - double minRefresh = GetDoubleSetting(L"MinRefreshRate"); - double maxRefresh = GetDoubleSetting(L"MaxRefreshRate"); - - if (minRefresh <= 0 || minRefresh > 300) { - vddlog("w", "Invalid min refresh rate detected, setting to safe default (24Hz)"); - minRefresh = 24.0; - sanitizedSettings++; - } - - if (maxRefresh <= minRefresh || maxRefresh > 500) { - vddlog("w", "Invalid max refresh rate detected, setting to safe default (240Hz)"); - maxRefresh = 240.0; - sanitizedSettings++; - } - - // Validate resolution settings - int minWidth = GetIntegerSetting(L"MinResolutionWidth"); - int minHeight = GetIntegerSetting(L"MinResolutionHeight"); - int maxWidth = GetIntegerSetting(L"MaxResolutionWidth"); - int maxHeight = GetIntegerSetting(L"MaxResolutionHeight"); - - if (minWidth < 640 || minWidth > 7680) { - vddlog("w", "Invalid min width detected, setting to 640"); - minWidth = 640; - sanitizedSettings++; - } - - if (minHeight < 480 || minHeight > 4320) { - vddlog("w", "Invalid min height detected, setting to 480"); - minHeight = 480; - sanitizedSettings++; - } - - if (maxWidth < minWidth || maxWidth > 15360) { - vddlog("w", "Invalid max width detected, setting to 7680"); - maxWidth = 7680; - sanitizedSettings++; - } - - if (maxHeight < minHeight || maxHeight > 8640) { - vddlog("w", "Invalid max height detected, setting to 4320"); - maxHeight = 4320; - sanitizedSettings++; - } - - // Validate HDR luminance values - double maxLuminance = GetDoubleSetting(L"MaxDisplayMasteringLuminance"); - double minLuminance = GetDoubleSetting(L"MinDisplayMasteringLuminance"); - - if (maxLuminance <= 0 || maxLuminance > 10000) { - vddlog("w", "Invalid max luminance detected, setting to 1000 nits"); - maxLuminance = 1000.0; - sanitizedSettings++; - } - - if (minLuminance <= 0 || minLuminance >= maxLuminance) { - vddlog("w", "Invalid min luminance detected, setting to 0.05 nits"); - minLuminance = 0.05; - sanitizedSettings++; - } - - // Validate color primaries - double localRedX = GetDoubleSetting(L"RedX"); - double localRedY = GetDoubleSetting(L"RedY"); - double localGreenX = GetDoubleSetting(L"GreenX"); - double localGreenY = GetDoubleSetting(L"GreenY"); - double localBlueX = GetDoubleSetting(L"BlueX"); - double localBlueY = GetDoubleSetting(L"BlueY"); - - if (localRedX < 0.0 || localRedX > 1.0 || localRedY < 0.0 || localRedY > 1.0) { - vddlog("w", "Invalid red primary coordinates, using sRGB defaults"); - sanitizedSettings++; - } - - if (localGreenX < 0.0 || localGreenX > 1.0 || localGreenY < 0.0 || localGreenY > 1.0) { - vddlog("w", "Invalid green primary coordinates, using sRGB defaults"); - sanitizedSettings++; - } - - if (localBlueX < 0.0 || localBlueX > 1.0 || localBlueY < 0.0 || localBlueY > 1.0) { - vddlog("w", "Invalid blue primary coordinates, using sRGB defaults"); - sanitizedSettings++; - } - - logStream.str(""); - logStream << "Configuration validation completed:" - << "\n Settings sanitized: " << sanitizedSettings - << "\n Refresh rate range: " << minRefresh << "-" << maxRefresh << " Hz" - << "\n Resolution range: " << minWidth << "x" << minHeight << " to " << maxWidth << "x" << maxHeight - << "\n Luminance range: " << minLuminance << "-" << maxLuminance << " nits"; - vddlog("i", logStream.str().c_str()); - - return (sanitizedSettings < 5) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; -} + Status = IddCxDeviceInitialize(Device); + if (!NT_SUCCESS(Status)){ + LOG_ERROR("IddCxDeviceInitialize error: 0x%08x", Status); + } -NTSTATUS InitializePhase5Integration() -{ - stringstream logStream; - logStream << "=== PHASE 5: FINAL INTEGRATION INITIALIZATION ==="; - vddlog("i", logStream.str().c_str()); - - // Initialize integration status - g_IntegrationStatus = {}; - - // Run configuration validation and sanitization - NTSTATUS configStatus = ValidateAndSanitizeConfiguration(); - if (!NT_SUCCESS(configStatus)) { - vddlog("w", "Configuration validation completed with warnings"); - g_IntegrationStatus.warningCount++; - } - - // Run comprehensive diagnostics - NTSTATUS diagStatus = RunComprehensiveDiagnostics(); - - if (NT_SUCCESS(diagStatus) && NT_SUCCESS(configStatus)) { - vddlog("i", "Phase 5 integration completed successfully"); - g_IntegrationStatus.configurationValid = true; - } else { - vddlog("e", "Phase 5 integration completed with errors"); - g_IntegrationStatus.lastError = L"Diagnostic failures detected"; - } - - // Log final integration status - logStream.str(""); - logStream << "=== FINAL INTEGRATION STATUS ===" - << "\n EDID Integration: " << (g_IntegrationStatus.edidParsingEnabled ? "ACTIVE" : "INACTIVE") - << "\n HDR Metadata: " << (g_IntegrationStatus.hdrMetadataValid ? "VALID" : "INVALID") - << "\n Gamma Processing: " << (g_IntegrationStatus.gammaRampValid ? "ACTIVE" : "INACTIVE") - << "\n Mode Management: " << (g_IntegrationStatus.modeManagementActive ? "ACTIVE" : "INACTIVE") - << "\n Configuration: " << (g_IntegrationStatus.configurationValid ? "VALID" : "INVALID") - << "\n Total Errors: " << g_IntegrationStatus.errorCount - << "\n Total Warnings: " << g_IntegrationStatus.warningCount; - vddlog("i", logStream.str().c_str()); - - return (NT_SUCCESS(diagStatus) && NT_SUCCESS(configStatus)) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; -} + // Create a new device context object and attach it to the WDF device object + auto *pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); + pContext->pContext = new IndirectDeviceContext(Device); -#pragma endregion + LOG_INFO("Create DeviceObject=0x%p suc", Device); + return Status; +} \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/Driver.h b/Virtual Display Driver (HDR)/MttVDD/Driver.h index b48b97ff..e10f72ec 100644 --- a/Virtual Display Driver (HDR)/MttVDD/Driver.h +++ b/Virtual Display Driver (HDR)/MttVDD/Driver.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include @@ -14,24 +14,117 @@ #include #include -#include -#include #include -#include #include "Trace.h" -// Utility function declarations -std::vector split(std::string& input, char delimiter); -std::string WStringToString(const std::wstring& wstr); +const int STREAM_LOG_INFO_LEN = 120; +const int monitorMaxCount = 4;//默认最多4个显示器 +enum { + ENCODER_PARAM_NOSET = 0, + ENCODER_PARAM_RESTART = 0x0001, + ENCODER_PARAM_RESET = 0x0002, + ENCODER_PARAM_TEST = 0x0004, +}; + +enum { + VIDEO_FRAME_FLAG_NONE = 0x00, + VIDEO_FRAME_FLAG_RESTART = 0x01, + VIDEO_FRAME_FLAG_IFRAME = 0x02, +}; + +enum { + STREAM_LOG_INFO = 0, + STREAM_LOG_ERR = 1, +}; + +#define STREAM_INFO(stream, info) \ + setStreamLog(stream, STREAM_LOG_INFO, info); \ + LOG_INFO(info); + +#define STREAM_ERROR(stream, info) \ + setStreamLog(stream, STREAM_LOG_ERR, info); \ + LOG_ERROR(info); + +LONG exception_handler(struct _EXCEPTION_POINTERS* apExceptionInfo); + +struct FrameHead { + uint8_t frame_head_len; + uint8_t codec; + uint8_t frame_flag; + uint8_t resv; + uint32_t frame_id; + uint32_t ts; + uint16_t width; + uint16_t height; +}; + +struct DisplayInfo +{ + bool use_edid; + bool hw_cursor; + LONG prefer_width; + LONG prefer_height; + UINT prefer_fps; + BYTE edid_data[256]; + size_t edid_len; + SIZE size_list[256]; + size_t size_cnt; + UINT fps_list[16]; + size_t fps_cnt; + + bool record_stream; + int record_size; + char record_path[256]; +}; + +struct ShareStream +{ + // 初始化时设置 + DisplayInfo* display; + char* stream_head; + char* stream_config; + char* stream_log; + char* stream_pos; + char* stream_buffer_common; + char* stream_buffer; + HANDLE shmen_handle; + HANDLE capture_handle; + HANDLE resume_handle; + int stream_buffer_len; + int max_frame_len; + + // 全局变量 + int32_t display_idx; + int32_t check_encoder; + int64_t write_id; + int64_t stream_cur_pos; + + // 编码时设置 + FrameHead frame_head; + uint64_t start_us; + int frame_buffer_cnt; + int frame_block_time; + + uint64_t encode_start_us; + int force_idr; // 内部编码错误时,指示重新编码 + int resv; +}; + +uint64_t getUs(); + +HANDLE initResetEvent(void** addr); +UINT getSignDisplayCount(void* addr); + +void initShareStream(); +void initShareStream(UINT idx); +ShareStream* getShareStream(UINT idx); +bool checkStreamEncoder(ShareStream* stream); +void setStreamEncoder(ShareStream* stream, bool valid); +void setStreamLog(ShareStream* stream, int level, const char* info); +int waitStream(ShareStream* stream); +int pauseStream(UINT idx); -// Phase 5: Final Integration function declarations -NTSTATUS ValidateEdidIntegration(); -NTSTATUS PerformanceMonitor(); -NTSTATUS CreateFallbackConfiguration(); -NTSTATUS ValidateAndSanitizeConfiguration(); -NTSTATUS RunComprehensiveDiagnostics(); -NTSTATUS InitializePhase5Integration(); namespace Microsoft { @@ -49,6 +142,19 @@ namespace Microsoft { namespace IndirectDisp { + struct InitChainParam { + IDDCX_ADAPTER hAdapter; + IDDCX_MONITOR hMonitor; + UINT nConnectorIndex; +// int nFramerate; + }; + +// struct TextureInfo +// { +// IDXGIResource* pSurface; +// Microsoft::WRL::ComPtr Texture2D; +// }; + /// /// Manages the creation and lifetime of a Direct3D render device. /// @@ -64,41 +170,42 @@ namespace Microsoft Microsoft::WRL::ComPtr Device; Microsoft::WRL::ComPtr DeviceContext; }; - + class IndirectMonitorContext; /// /// Manages a thread that consumes buffers from an indirect display swap-chain object. /// class SwapChainProcessor { public: - SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, std::shared_ptr Device, HANDLE NewFrameEvent); + SwapChainProcessor(IndirectMonitorContext* monitorContext,const InitChainParam ¶m, IDDCX_SWAPCHAIN hSwapChain, std::shared_ptr Device, HANDLE NewFrameEvent); ~SwapChainProcessor(); + IndirectMonitorContext* m_monitorContext; + IDDCX_SWAPCHAIN m_hSwapChain; + std::shared_ptr m_Device; + HANDLE m_hAvailableBufferEvent; + Microsoft::WRL::Wrappers::Thread m_hThread; +// Microsoft::WRL::Wrappers::Event m_hTerminateEvent; + bool isClosed=false; + void ClearEncoder(); private: static DWORD CALLBACK RunThread(LPVOID Argument); void Run(); void RunCore(); - public: - IDDCX_SWAPCHAIN m_hSwapChain; - std::shared_ptr m_Device; - HANDLE m_hAvailableBufferEvent; - Microsoft::WRL::Wrappers::Thread m_hThread; - Microsoft::WRL::Wrappers::Event m_hTerminateEvent; - }; + InitChainParam m_InitParam; - /// - /// Custom comparator for LUID to be used in std::map - /// - struct LuidComparator - { - bool operator()(const LUID& a, const LUID& b) const - { - if (a.HighPart != b.HighPart) - return a.HighPart < b.HighPart; - return a.LowPart < b.LowPart; - } +// int m_nTextureOk; +// int m_nTextureCnt; + //TextureInfo m_TextureInfo[4]; +// D3D11_TEXTURE2D_DESC m_TextureDesc; + //IDXGIResource* m_pCurSurface = nullptr; +// int m_nEncodeSurface; + +// EncodeParam m_stEncodeParam; +// EncodeConfig m_stEncodeConfig; +// ShareStream* m_pShareStream; }; /// @@ -112,31 +219,36 @@ namespace Microsoft void InitAdapter(); void FinishInit(); - - void CreateMonitor(unsigned int index); - - void AssignSwapChain(IDDCX_MONITOR Monitor, IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent); - void UnassignSwapChain(IDDCX_MONITOR Monitor); - + void CreateMonitor(UINT displayIndex); +// void DestroyMonitor(UINT displayIndex); +// void DestroyMonitors(); +// void CommitModes(_In_ const IDARG_IN_COMMITMODES* pInArgs); protected: - WDFDEVICE m_WdfDevice; IDDCX_ADAPTER m_Adapter; - IDDCX_MONITOR m_Monitor; - IDDCX_MONITOR m_Monitor2; - - std::map> m_ProcessingThreads; - std::mutex m_ProcessingThreadsMutex; +// IDDCX_MONITOR m_iddMonitor[monitorMaxCount]; + }; + class IndirectMonitorContext + { public: - static const DISPLAYCONFIG_VIDEO_SIGNAL_INFO s_KnownMonitorModes[]; - static std::vector s_KnownMonitorEdid; + IndirectMonitorContext(_In_ UINT ConnectorIndex, _In_ IDDCX_MONITOR Monitor, _In_ IDDCX_ADAPTER Adapter); + virtual ~IndirectMonitorContext(); + +// void CommitModes(_In_ const DISPLAYCONFIG_VIDEO_SIGNAL_INFO* pInfo); + void AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent); + void UnassignSwapChain(); + UINT GetConnectorIndex() { return m_ConnectorIndex; } + bool SetHwCursorMode(); private: - static std::map, LuidComparator> s_DeviceCache; - static std::mutex s_DeviceCacheMutex; - static std::shared_ptr GetOrCreateDevice(LUID RenderAdapter); - static void CleanupExpiredDevices(); - }; + + UINT m_ConnectorIndex; + IDDCX_ADAPTER m_Adapter; + IDDCX_MONITOR m_Monitor; + std::unique_ptr m_ProcessingThread; + HANDLE m_hCursorEvent; +// int m_nFrameRate = 60; + } ; } -} +} \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/IOHelper.cpp b/Virtual Display Driver (HDR)/MttVDD/IOHelper.cpp new file mode 100644 index 00000000..8e1f2f02 --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/IOHelper.cpp @@ -0,0 +1,163 @@ +#include "IOHelper.h" + +#include + +#include "Utils.h" + +const int readMaxSize = 64 * 1024; +using namespace std; +bool IOHelper::checkAndCreateDirectory(string path) +{ + return checkAndCreateDirectory(filesystem::path(path)); +} + +bool IOHelper::checkAndCreateDirectory(wstring path) +{ + return checkAndCreateDirectory(filesystem::path(path)); +} + +bool IOHelper::checkAndCreateDirectory(filesystem::path path) { + try { + if (!filesystem::exists(path)) { + bool success = checkAndCreateDirectory(path.parent_path());//递归处理上级目录 + if (success) { + return filesystem::create_directory(path); //只会创建1级的目录 + } + } + return true; + } catch (filesystem::filesystem_error &e) { + LOG_ERROR(u8"无法打开文件:%s", e.what()); + } + return false; +} + +bool IOHelper::exists(string path) +{ + return filesystem::exists(path); +} + +string IOHelper::getFileNameWithoutExt(string filename) +{ + auto path = filesystem::path(filename); + return getFileNameWithoutExt(path); +} + +string IOHelper::getFileNameWithoutExt(filesystem::path path) +{ + if (path.filename().has_stem()) + return path.filename().stem().string(); + return path.filename().string(); +} + +string IOHelper::getFileNameExt(string filename) +{ + return getFileNameExt(filesystem::path(filename)); +} + +string IOHelper::getFileNameExt(filesystem::path path) +{ + return path.extension().string(); +} + +string IOHelper::pathCombine(string path1, string path2) +{ + if (!filesystem::is_directory(path1))//如果路径1已经是文件名,则返回文件名 + return path1; + auto path= filesystem::path(path2); + if (path.is_absolute()) { //路径2 已经是绝对路径 + return path2; + } + auto firstPath=filesystem::path(path1); + auto resultPath = firstPath / path; + return resultPath.string(); +} + + +bool IOHelper::backupFile(string filename, string backupDirectory) { + if (filesystem::exists(filename)) { + try { + auto path = filesystem::path(filename); + auto ext = path.extension().string(); + string newFileName; + for (int i = 0;; i++) { + newFileName =pathCombine(backupDirectory , path.filename().stem().string() + "_" + getCurrentDate() + "_" + to_string(i) + ext); + if (!filesystem::exists(newFileName)) + break; + } + checkAndCreateDirectory(backupDirectory); + filesystem::rename(filename, newFileName); + return true; + } + catch (filesystem::filesystem_error& e) { + //string content = u8"无法打开文件:" + *e.what(); + //LOG_ERROR(content.c_str()); + LOG_ERROR(u8"无法打开文件:%s" , e.what()); + } + } + return false; +} + +vector IOHelper::readAllLine(string filename) +{ + vector result; + if (exists(filename)) { + fstream fileStream;// 使用构造函数直接创建并打开文件 + fileStream.open(filename, ios::in); // 以只读模式打开 + if (!fileStream.is_open()) { + string content = u8"无法打开文件:" + filename; + LOG_ERROR(content.c_str()); + return result; + } + string line; + while (getline(fileStream, line)) { + result.push_back(line); + } + }else { + string content = u8"读取的文件不存在:" + filename; + LOG_WARN(content.c_str()); //这样写入没有问题 + //LOG_WARN(u8"读取的文件不存在:%s", &filename); //这样写入文件时,会乱码,且文件格式变成ansi + } + return result; +} + +bool IOHelper::readEdIdData(const char* path, std::vector vec) +{ + FILE* fp = NULL; + if (fopen_s(&fp, path, "rb") == 0 && fp != NULL) { + fseek(fp, 0, SEEK_END); + auto file_len = ftell(fp); + fseek(fp, 0, SEEK_SET); + + bool ret = true; + if (file_len > 0 && file_len <= readMaxSize) + { + std::vector tmp(file_len); + for (size_t total_len = 0; total_len < file_len; ) { + auto read_len = fread(&tmp[total_len], 1, file_len - total_len, fp); + if (read_len > 0) { + total_len += read_len; + } + else if (read_len == 0) { + break; + } + else { + LOG_ERROR("fread %s error: %d", path, read_len); + ret = false; + break; + } + } + vec.swap(tmp); + } + else + { + LOG_ERROR("file %s too big: file_len=%d", path, file_len); + ret = false; + } + fclose(fp); + return ret; + } + else { + LOG_INFO("fopen %s fail", path); + } + return false; +} diff --git a/Virtual Display Driver (HDR)/MttVDD/IOHelper.h b/Virtual Display Driver (HDR)/MttVDD/IOHelper.h new file mode 100644 index 00000000..ef9ac93d --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/IOHelper.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include +class IOHelper +{ +public: + static bool checkAndCreateDirectory(std::string path); + static bool checkAndCreateDirectory(std::wstring path); + static bool checkAndCreateDirectory(std::filesystem::path path); + static bool exists(std::string path); + static std::string getFileNameWithoutExt(std::string filename); + static std::string getFileNameWithoutExt(std::filesystem::path path); + static std::string getFileNameExt(std::string filename); + static std::string getFileNameExt(std::filesystem::path path); + static std::string pathCombine(std::string path1, std::string path2); + static bool backupFile(std::string filename, std::string backupDirectory); + + static std::vector readAllLine(std::string filename); + static bool readEdIdData(const char* path,std::vector vec); +}; + diff --git a/Virtual Display Driver (HDR)/MttVDD/MttVDD.inf b/Virtual Display Driver (HDR)/MttVDD/MttVDD.inf deleted file mode 100644 index e6ba858c..00000000 Binary files a/Virtual Display Driver (HDR)/MttVDD/MttVDD.inf and /dev/null differ diff --git a/Virtual Display Driver (HDR)/MttVDD/MttVDD.sln b/Virtual Display Driver (HDR)/MttVDD/MttVDD.sln deleted file mode 100644 index f45ea3f9..00000000 --- a/Virtual Display Driver (HDR)/MttVDD/MttVDD.sln +++ /dev/null @@ -1,50 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.9.34622.214 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IddSampleDriver", "IddSampleDriver.vcxproj", "{2D54CB75-8B17-4F11-97DC-847B0244CD46}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|ARM = Debug|ARM - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|ARM = Release|ARM - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.ActiveCfg = Debug|ARM - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.Build.0 = Debug|ARM - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.Deploy.0 = Debug|ARM - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.Build.0 = Debug|ARM64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.Deploy.0 = Debug|ARM64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.ActiveCfg = Debug|x64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.Build.0 = Debug|x64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.Deploy.0 = Debug|x64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.ActiveCfg = Debug|Win32 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.Build.0 = Debug|Win32 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.Deploy.0 = Debug|Win32 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.ActiveCfg = Release|ARM - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.Build.0 = Release|ARM - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.Deploy.0 = Release|ARM - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.ActiveCfg = Release|ARM64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.Build.0 = Release|ARM64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.Deploy.0 = Release|ARM64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.ActiveCfg = Release|x64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.Build.0 = Release|x64 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.ActiveCfg = Release|Win32 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Build.0 = Release|Win32 - {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Deploy.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9A8BE7D9-9F0F-40A2-BA2D-8AE9E35890DB} - EndGlobalSection -EndGlobal diff --git a/Virtual Display Driver (HDR)/MttVDD/MttVDD.vcxproj b/Virtual Display Driver (HDR)/MttVDD/MttVDD.vcxproj deleted file mode 100644 index 6d20ef7f..00000000 --- a/Virtual Display Driver (HDR)/MttVDD/MttVDD.vcxproj +++ /dev/null @@ -1,367 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - Debug - ARM - - - Release - ARM - - - Debug - ARM64 - - - Release - ARM64 - - - - - - - - - - - - - - {2D54CB75-8B17-4F11-97DC-847B0244CD46} - {32909489-7be5-497b-aafa-db6669d9b44b} - v4.5 - 12.0 - Debug - Win32 - MttVDD - - - WindowsUserModeDriver10.0 - DynamicLibrary - Universal - - - WindowsUserModeDriver10.0 - DynamicLibrary - Universal - - - WindowsUserModeDriver10.0 - DynamicLibrary - Universal - - - WindowsUserModeDriver10.0 - DynamicLibrary - Universal - - - WindowsUserModeDriver10.0 - DynamicLibrary - Universal - - - WindowsUserModeDriver10.0 - DynamicLibrary - Universal - - - WindowsUserModeDriver10.0 - DynamicLibrary - Universal - - - WindowsUserModeDriver10.0 - DynamicLibrary - Universal - - - - Windows10 - true - 2 - true - 1 - 0 - - - Windows10 - false - 2 - true - 1 - 0 - - - Windows10 - true - 2 - true - 1 - 10 - Spectre - 25 - - - Windows10 - false - 2 - true - 1 - 10 - Spectre - 25 - 25 - - - Windows10 - true - 2 - true - 1 - 0 - - - Windows10 - false - 2 - true - 1 - 0 - - - Windows10 - true - 2 - true - 1 - 10 - Spectre - 25 - - - Windows10 - false - 2 - true - 1 - 10 - Spectre - 25 - 25 - - - - - - - - - - DbgengRemoteDebugger - true - - - DbgengRemoteDebugger - true - - - DbgengRemoteDebugger - true - true - - - - DbgengRemoteDebugger - true - true - - - DbgengRemoteDebugger - true - - - DbgengRemoteDebugger - true - - - DbgengRemoteDebugger - true - true - - - DbgengRemoteDebugger - true - true - - - - true - true - trace.h - Async - true - $(WindowsSdkDir)Include\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(ProjectDir)..\..\ThirdParty\Windows-Driver-Frameworks\src\publicinc\wdf\umdf\2.15;$(ProjectDir)..\..\Common\Include;$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\um\iddcx\$(IDDCX_VERSION_MAJOR).$(IDDCX_VERSION_MINOR);%(AdditionalIncludeDirectories) - - - %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;setupapi.lib - - - - - true - true - trace.h - Async - true - $(WindowsSdkDir)Include\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(ProjectDir)..\..\ThirdParty\Windows-Driver-Frameworks\src\publicinc\wdf\umdf\2.15;$(ProjectDir)..\..\Common\Include;$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\um\iddcx\$(IDDCX_VERSION_MAJOR).$(IDDCX_VERSION_MINOR);%(AdditionalIncludeDirectories) - - - %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;setupapi.lib - - - - - true - true - trace.h - Async - true - /D_ATL_NO_WIN_SUPPORT /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=4 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) - $(WindowsSdkDir)Include\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(ProjectDir)..\..\ThirdParty\Windows-Driver-Frameworks\src\publicinc\wdf\umdf\2.15;$(ProjectDir)..\..\Common\Include;$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\um\iddcx\$(IDDCX_VERSION_MAJOR).$(IDDCX_VERSION_MINOR);%(AdditionalIncludeDirectories) - stdcpp17 - - - %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;setupapi.lib - - - SHA1 - - - copy "$(ProjectDir)..\vdd_settings.xml" "$(TargetDir)\MttVDD" - - - - - true - true - trace.h - Async - true - /D_ATL_NO_WIN_SUPPORT /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=10 /DIDDCX_MINIMUM_VERSION_REQUIRED=3 %(AdditionalOptions) - $(WindowsSdkDir)Include\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(ProjectDir)..\..\ThirdParty\Windows-Driver-Frameworks\src\publicinc\wdf\umdf\2.15;$(ProjectDir)..\..\Common\Include;$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\um\iddcx\$(IDDCX_VERSION_MAJOR).$(IDDCX_VERSION_MINOR);%(AdditionalIncludeDirectories) - stdcpp17 - - - %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;setupapi.lib - - - SHA256 - - - copy "$(ProjectDir)..\vdd_settings.xml" "$(TargetDir)\MttVDD" - - - - - true - true - trace.h - Async - true - - - %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;setupapi.lib - - - - - true - true - trace.h - Async - true - - - %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;setupapi.lib - - - - - true - true - trace.h - Async - true - /D_ATL_NO_WIN_SUPPORT /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=4 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) - $(WindowsSdkDir)Include\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(ProjectDir)..\..\ThirdParty\Windows-Driver-Frameworks\src\publicinc\wdf\umdf\2.15;$(ProjectDir)..\..\Common\Include;$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\um\iddcx\$(IDDCX_VERSION_MAJOR).$(IDDCX_VERSION_MINOR);%(AdditionalIncludeDirectories) - stdcpp17 - - - %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;setupapi.lib - - - SHA1 - - - copy "$(ProjectDir)..\vdd_settings.xml" "$(TargetDir)\MttVDD" - - - 10/16/2024 - - - - - true - true - trace.h - Async - true - /D_ATL_NO_WIN_SUPPORT /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=10 /DIDDCX_MINIMUM_VERSION_REQUIRED=3 %(AdditionalOptions) - $(WindowsSdkDir)Include\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\wdf\umdf\$(UMDF_VERSION_MAJOR).$(UMDF_VERSION_MINOR);$(ProjectDir)..\..\ThirdParty\Windows-Driver-Frameworks\src\publicinc\wdf\umdf\2.15;$(ProjectDir)..\..\Common\Include;$(WindowsSdkDir)Include\$(WindowsTargetPlatformVersion)\um\iddcx\$(IDDCX_VERSION_MAJOR).$(IDDCX_VERSION_MINOR);%(AdditionalIncludeDirectories) - stdcpp17 - - - %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;setupapi.lib - - - SHA256 - - - copy "$(ProjectDir)..\vdd_settings.xml" "$(TargetDir)\MttVDD" - - - 10/16/2024 - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/Trace.h b/Virtual Display Driver (HDR)/MttVDD/Trace.h index c1655901..0a3c8621 100644 --- a/Virtual Display Driver (HDR)/MttVDD/Trace.h +++ b/Virtual Display Driver (HDR)/MttVDD/Trace.h @@ -57,7 +57,7 @@ Module Name: // // Driver specific #defines // -#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0 - // TODO: Update the name of the tracing provider - #define MYDRIVER_TRACING_ID L"Microsoft\\UMDF2.0\\IddSampleDriver V1.0" -#endif \ No newline at end of file + +// TODO: Use a unique driver tracing ID here, +// see https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/adding-wpp-software-tracing-to-a-windows-driver +#define MYDRIVER_TRACING_ID L"Microsoft\\UMDF2.25\\VirtualDisplayDriver v1.0" diff --git a/Virtual Display Driver (HDR)/MttVDD/Utils.cpp b/Virtual Display Driver (HDR)/MttVDD/Utils.cpp new file mode 100644 index 00000000..2996fc8b --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/Utils.cpp @@ -0,0 +1,153 @@ +#include "Utils.h" + +//#include +//#include +#include +#include +using namespace std; +string getCurrentDate() { + // 获取当前日期并以"YYYY-MM-DD"格式返回 + auto now = chrono::system_clock::now(); + auto in_time_t = chrono::system_clock::to_time_t(now); + tm* out_time=new tm(); + localtime_s(out_time, &in_time_t); + stringstream ss; + ss << put_time(out_time, "%Y-%m-%d"); + return ss.str(); +} + +int64_t getCurrentTime(){ + auto now = chrono::system_clock::now().time_since_epoch();//chrono::high_resolution_clock::now(); + return chrono::duration_cast(now).count(); +} + +string WStringToString(const wstring& wstr) { //basically just a function for converting strings since codecvt is depricated in c++ 17 + if (wstr.empty()) return ""; + + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), NULL, 0, NULL, NULL); + string str(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &str[0], size_needed, NULL, NULL); + //wstring_convert> conv; + //string str = conv.to_bytes(wstr); + return str; +} + +wstring StringToWString(const string& str) +{ + int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); + wstring wstr(size_needed,0); + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstr[0], size_needed); +// return wstring(wstr.data(), wstr.data() + size_needed); + return wstr; +} + +vector split(string& input, char delimiter) +{ + istringstream stream(input); + string field; + vector result; + while (getline(stream, field, delimiter)) { + result.push_back(field); + } + return result; +} + +// trim from start (inplace) +string& ltrimString(string& s,string words) { + if (s.empty()) + return s; + size_t index = s.find_first_not_of(words); + if(index != std::string::npos) + s.erase(0, index); + return s; +} + +// trim from end (inplace) +string& rtrimString(string& s, string words) { + if (s.empty()) + return s; + size_t index = s.find_last_not_of(words); + if (index != std::string::npos) + s.erase(index+1); + return s; +} + +// trim from both ends (inplace) +string& trimString(string& s, string words) { + ltrimString(s,words); + rtrimString(s, words); + return s; +} + +string replaceString(const string &sourceStr, string matchStr, string replaceStr) { + string result=sourceStr; + if(matchStr==replaceStr) + return result; + auto index=result.find(matchStr); + while(index != string::npos){ + result.replace(index,matchStr.size(),replaceStr); + index=result.find(matchStr,index+replaceStr.size()); + } + return result; +} + +int indexOfString(std::string s, std::string words) +{ + auto index = s.find_first_of(words); + if (index == std::string::npos) + return -1; + return (int)index; +} + +string& toLowerString(string& s) { + for (char& c : s) { + c=(char)tolower(static_cast(c)); + } + return s; +} + +string GuidToString(const GUID& guid) { + LPOLESTR wstr; + StringFromCLSID(guid, &wstr); + wstring ws(wstr); + string result = WStringToString(ws); + CoTaskMemFree(wstr); + return result; +} + +#pragma region 注册表相关 +wstring readFromRegString(wstring name,wstring subkey,HKEY key) +{ + HKEY hKey; + wchar_t value[MAX_PATH]; + DWORD dwBufferSize = sizeof(value); + LONG lResult = RegOpenKeyExW(key, subkey.c_str(), 0, KEY_READ, &hKey);//打开节点 + if (lResult == ERROR_SUCCESS) { + lResult = RegQueryValueExW(hKey, name.c_str(), NULL, NULL, (LPBYTE)&value, &dwBufferSize);//读取配置 + if (lResult == ERROR_SUCCESS) { + RegCloseKey(hKey); + return wstring(value); + } + } + RegCloseKey(hKey); + return wstring(); +} + +int readFromRegWord(wstring name, wstring subkey, HKEY key) +{ + HKEY hKey; + DWORD value; + DWORD dwBufferSize = sizeof(value); + LONG lResult = RegOpenKeyExW(key, subkey.c_str(), 0, KEY_READ, &hKey);//打开节点 + if (lResult == ERROR_SUCCESS) { + lResult = RegQueryValueExW(hKey, name.c_str(), NULL, NULL, (LPBYTE)&value, &dwBufferSize);//读取配置 + if (lResult == ERROR_SUCCESS) { + RegCloseKey(hKey); + return static_cast(value); + } + } + RegCloseKey(hKey); + return -1; + +} +#pragma endregion \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/Utils.h b/Virtual Display Driver (HDR)/MttVDD/Utils.h new file mode 100644 index 00000000..c4a67bef --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/Utils.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef REG_SZ_NODE +#define REG_SZ_NODE L"SOFTWARE\\Nzt\\VirtualDisplayDriver" +#endif // !REG_SZ_NODE + +enum class LogLevel{ + None, + Error, + Warning, + Info, + Debug +}; +#define LOG_DEBUG(fmt, ...) write_log("DEBUG", __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); +#define LOG_INFO(fmt, ...) write_log("INFO", __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); +#define LOG_WARN(fmt, ...) write_log("WARN", __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); +#define LOG_ERROR(fmt, ...) write_log("ERROR", __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); + +typedef void* frames_ptr; + +extern "C" { +void write_log(const char* level, const char* func, int line, const char* format, ...); +void streamAddFrame(frames_ptr frames, const void* frame_buf, int frame_len); +} + +std::string GuidToString(const GUID& guid); +std::string getCurrentDate(); +int64_t getCurrentTime(); +std::string WStringToString(const std::wstring& wstr); +std::wstring StringToWString(const std::string& str); + +std::vector split(std::string& input, char delimiter); + +std::string& ltrimString(std::string& s,std::string words= "\f\v\r\t\n "); +std::string& rtrimString(std::string& s,std::string words = "\f\v\r\t\n "); +std::string& trimString(std::string& s, std::string words = "\f\v\r\t\n "); +std::string replaceString(const std::string& sourceStr,std::string matchStr, std::string replaceStr); + +int indexOfString(std::string s, std::string words); +std::string& toLowerString(std::string& s); + +#pragma region reg + +std::wstring readFromRegString(std::wstring name, std::wstring subkey = REG_SZ_NODE, HKEY key = HKEY_LOCAL_MACHINE); +int readFromRegWord(std::wstring name, std::wstring subkey = REG_SZ_NODE, HKEY key = HKEY_LOCAL_MACHINE); + +#pragma endregion \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.inf b/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.inf new file mode 100644 index 00000000..d68f9aba Binary files /dev/null and b/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.inf differ diff --git a/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.rc b/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.rc new file mode 100644 index 00000000..f591b481 --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.rc @@ -0,0 +1,18 @@ +#include +#include +#include "version.h" + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "UMDF Indirect Display Driver" +#define VER_INTERNALNAME_STR "Virtual Display Device" +#define VER_ORIGINALFILENAME_STR "VirtualDisplayDriver.dll" + + +#define VER_PRODUCTVERSION_STR VERSION +#define VER_FILEVERSION_STR VERSION +#define VER_COMPANYNAME_STR "Nzt" +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) 2026" +#define VER_PRODUCTNAME_STR "VirtualDisplayDriver" + +#include "common.ver" diff --git a/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.vcxproj b/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.vcxproj new file mode 100644 index 00000000..51cefacf --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.vcxproj @@ -0,0 +1,211 @@ + + + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + + + + + + + + + + + + + {2D54CB75-8B17-4F11-97DC-847B0244CD46} + {32909489-7be5-497b-aafa-db6669d9b44b} + v4.5 + 12.0 + Debug + x64 + VirtualDisplayDriver + $(LatestTargetPlatformVersion) + + + WindowsUserModeDriver10.0 + DynamicLibrary + Universal + + + WindowsUserModeDriver10.0 + DynamicLibrary + Universal + + + WindowsUserModeDriver10.0 + DynamicLibrary + Universal + + + WindowsUserModeDriver10.0 + DynamicLibrary + Universal + + + + Windows10 + true + 2 + 25 + true + 1 + 4 + Spectre + + + Windows10 + false + 2 + 25 + true + 1 + 4 + Spectre + NotSet + + + Windows10 + true + 2 + 25 + true + 1 + 4 + Spectre + + + Windows10 + false + 2 + 25 + true + 1 + 4 + Spectre + + + + + + + + + + DbgengRemoteDebugger + true + + + DbgengRemoteDebugger + true + + + DbgengRemoteDebugger + true + + + DbgengRemoteDebugger + true + + + + true + true + trace.h + Sync + true + _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) + /D_ATL_NO_WIN_SUPPORT /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 /utf-8 %(AdditionalOptions) + $(IntDir);%(AdditionalIncludeDirectories);include + stdcpp17 + + + %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib + + + sha256 + + + + + true + true + trace.h + Sync + true + _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) + /D_ATL_NO_WIN_SUPPORT /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 /utf-8 %(AdditionalOptions) + $(IntDir);%(AdditionalIncludeDirectories);include + stdcpp17 + false + + + %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib + %(AdditionalOptions) + + + sha256 + + + + + true + true + trace.h + Sync + true + /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) + + + %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib + + + + + true + true + trace.h + Sync + true + /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=6 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) + + + %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib + + + + + + + + + + + + + + Document + + + + + + \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/MttVDD.vcxproj.filters b/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.vcxproj.filters similarity index 67% rename from Virtual Display Driver (HDR)/MttVDD/MttVDD.vcxproj.filters rename to Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.vcxproj.filters index 804b5c62..118402aa 100644 --- a/Virtual Display Driver (HDR)/MttVDD/MttVDD.vcxproj.filters +++ b/Virtual Display Driver (HDR)/MttVDD/VirtualDisplayDriver.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -18,11 +18,6 @@ inf;inv;inx;mof;mc; - - - Driver Files - - Header Files @@ -30,18 +25,40 @@ Header Files + + Header Files + + + Header Files + + + Header Files + Source Files + + Source Files + + + Source Files + - + + Resource Files + - + Resource Files - + + + + + Driver Files + \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/changelog.md b/Virtual Display Driver (HDR)/MttVDD/changelog.md new file mode 100644 index 00000000..6dd92f25 --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/changelog.md @@ -0,0 +1,31 @@ +##### 1.0.26.071001 +1. Improved IddCxVersionQuery to support command-line execution of pipe reload_driver. +2. Script upgrade and optimization. +3. Added official display driver development documentation. +4. Added support for dual monitors. +5. XML configuration reading is no longer supported; only option.txt is now supported. + +##### 1.0.26.062301 +1. update silent-install.ps1、silent-uninstall.ps1、vdd-install.bat +2. repair reload driver`s error. + +##### 1.0.26.060201 +1. remove devcon in all script. +2. update netcon to v1.17.40,add netconc.exe and netconw.exe to x64 directory. +3. remove install and uninstall from virtual-driver-manager.ps1,use vdd-install.bat and vdd-uninstall.bat instead. +4. add silent-uninstall.ps1 +5. repair all vdd`s product_id is same.now their have self id. + +##### 1.0.26.021201 +1. update install scripts. +2. merge from master. +3. update changeres-VDD script. + +##### 1.0.25.102701 +1. move script to child directory,like ./scripts/vdd/ . +2. add set display mode script for extend. +3. add set disable vdd after first setup. + +##### 1.0.25.102401 +1. add vdd install and uninstall shell bat. +2. optimize script make workspace is current directory. \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/include/AdapterOption.h b/Virtual Display Driver (HDR)/MttVDD/include/AdapterOption.h new file mode 100644 index 00000000..7358fb17 --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/include/AdapterOption.h @@ -0,0 +1,231 @@ +#pragma once + +#include // For ComPtr +#include // For IDXGIAdapter, IDXGIFactory1 +#include // For sort +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Microsoft::WRL; + +// DEVPKEY_Device_Luid: {60b193cb-5276-4d0f-96fc-f173ab17af69}, 2 +// Define it ourselves to avoid SDK/WDK header differences where DEVPKEY_Device_Luid may not be declared. +static const DEVPROPKEY DEVPKEY_Device_Luid_Custom = { + { 0x60b193cb, 0x5276, 0x4d0f, { 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6 } }, + 2 +}; + +// Structure to vector gpus +struct GPUInfo { + wstring name; // GPU name + ComPtr adapter;// COM pointer to the adapter + DXGI_ADAPTER_DESC desc; // Adapter description +}; + +// Sort function for GPUs by dedicated video memory +bool CompareGPUs(const GPUInfo& a, const GPUInfo& b) { + return a.desc.DedicatedVideoMemory > b.desc.DedicatedVideoMemory; +} + +// Get a enumerate list of available GPUs +vector getAvailableGPUs() { + vector gpus; // Vector to hold all GPU's information + + ComPtr factory; + if (!SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) { + return gpus; + } + + // Enumerate all adapters (GPUs) + for (UINT i = 0;; i++) { + ComPtr adapter; + if (!SUCCEEDED(factory->EnumAdapters(i, &adapter))) { + break; + } + + DXGI_ADAPTER_DESC desc; + + if (!SUCCEEDED(adapter->GetDesc(&desc))) { + continue; + } + + // Add the adapter information to the list + GPUInfo info{ desc.Description, adapter, desc }; + gpus.push_back(info); + } + + return gpus; +} + +// Resolve an adapter LUID from a PCI bus number by enumerating display devices (SetupAPI). +// Returns nullopt if no match is found or if the system doesn't expose the LUID property. +inline std::optional ResolveAdapterLuidFromPciBus(uint32_t targetBusIndex) { + HDEVINFO devInfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, nullptr, nullptr, DIGCF_PRESENT); + if (devInfo == INVALID_HANDLE_VALUE) { + return std::nullopt; + } + + SP_DEVINFO_DATA devData = {}; + devData.cbSize = sizeof(devData); + + std::optional result = std::nullopt; + + for (DWORD i = 0; SetupDiEnumDeviceInfo(devInfo, i, &devData); ++i) { + DWORD currentBus = 0; + if (!SetupDiGetDeviceRegistryPropertyW( + devInfo, + &devData, + SPDRP_BUSNUMBER, + nullptr, + reinterpret_cast(¤tBus), + sizeof(currentBus), + nullptr)) { + continue; + } + + if (static_cast(currentBus) != targetBusIndex) { + continue; + } + + // DEVPKEY_Device_Luid is exposed as a UINT64 on Windows; convert into LUID. + DEVPROPTYPE propType = 0; + ULONG propSize = 0; + ULONGLONG luid64 = 0; + + if (!SetupDiGetDevicePropertyW( + devInfo, + &devData, + &DEVPKEY_Device_Luid_Custom, + &propType, + reinterpret_cast(&luid64), + sizeof(luid64), + &propSize, + 0)) { + continue; + } + + if (propType != DEVPROP_TYPE_UINT64 || propSize != sizeof(luid64)) { + continue; + } + + LUID luid{}; + luid.LowPart = static_cast(luid64 & 0xFFFFFFFFull); + luid.HighPart = static_cast((luid64 >> 32) & 0xFFFFFFFFull); + result = luid; + break; + } + + SetupDiDestroyDeviceInfoList(devInfo); + return result; +} + +class AdapterOption { +public: + bool hasTargetAdapter{}; // Indicates if a target adapter is selected + LUID adapterLuid{}; // Adapter's unique identifier (LUID) + wstring target_name{}; // Target adapter name + + // Select the best GPU based on dedicated video memory + wstring selectBestGPU() { + auto gpus = getAvailableGPUs(); + if (gpus.empty()) { + return L""; // Error check for headless / vm + } + + // Sort GPUs by dedicated video memory in descending order + sort(gpus.begin(), gpus.end(), CompareGPUs); + auto bestGPU = gpus.front(); // Get the GPU with the most memory + + return bestGPU.name; + } + + // Load friendlyname from a file OR select the best GPU + void load(const wchar_t* path) { + ifstream ifs{ path }; + + if (!ifs.is_open()) { + target_name = selectBestGPU(); + } + else { + string line; + getline(ifs, line); + target_name.assign(line.begin(), line.end()); + } + + // Find and set the adapter based on the target name + if (!findAndSetAdapter(target_name)) { + // If the adapter is not found, select the best GPU and retry + target_name = selectBestGPU(); + findAndSetAdapter(target_name); + } + } + + // Set the target adapter from a given name and validate it + void xmlprovide(const wstring& xtarg) { + target_name = xtarg; + if (!findAndSetAdapter(target_name)) { + // If the adapter is not found, select the best GPU and retry + target_name = selectBestGPU(); + findAndSetAdapter(target_name); + } + } + + // Apply the adapter settings to the specified adapter + void apply(const IDDCX_ADAPTER& adapter) { + if (hasTargetAdapter && IDD_IS_FUNCTION_AVAILABLE(IddCxAdapterSetRenderAdapter)) { + IDARG_IN_ADAPTERSETRENDERADAPTER arg{}; + arg.PreferredRenderAdapter = adapterLuid; + IddCxAdapterSetRenderAdapter(adapter, &arg); + } + } + +private: + // Find and set the adapter by name, optionally using "name,bus" where bus is the PCI bus number. + bool findAndSetAdapter(const wstring& adapterSpec) { + // If user provides "name,bus", use bus to resolve LUID (more deterministic on multi-GPU setups). + const size_t comma = adapterSpec.find(L','); + if (comma != wstring::npos) { + const wstring namePart = adapterSpec.substr(0, comma); + wstring busPart = adapterSpec.substr(comma + 1); + // Trim whitespace in bus part + busPart.erase(remove_if(busPart.begin(), busPart.end(), iswspace), busPart.end()); + + wchar_t* end = nullptr; + const unsigned long busUl = wcstoul(busPart.c_str(), &end, 10); + const bool parsedOk = (end != nullptr) && (*end == L'\0') && (end != busPart.c_str()); + if (parsedOk && busUl <= 0xFFFFFFFFul) { + if (auto luidOpt = ResolveAdapterLuidFromPciBus(static_cast(busUl)); luidOpt.has_value()) { + adapterLuid = luidOpt.value(); + hasTargetAdapter = true; + return true; + } + } + + // Fall through to name matching using the name portion. + return findAndSetAdapter(namePart); + } + + const wstring& adapterName = adapterSpec; + auto gpus = getAvailableGPUs(); + + // Iterate through all available GPUs + for (const auto& gpu : gpus) { + if (_wcsicmp(gpu.name.c_str(), adapterName.c_str()) == 0) { + adapterLuid = gpu.desc.AdapterLuid; // Set the adapter LUID + hasTargetAdapter = true; // Indicate that a target adapter is selected + return true; + } + } + + hasTargetAdapter = false; // Indicate that no target adapter is selected + return false; + } +}; diff --git a/Virtual Display Driver (HDR)/MttVDD/option.txt b/Virtual Display Driver (HDR)/MttVDD/option.txt new file mode 100644 index 00000000..f32316f5 --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/option.txt @@ -0,0 +1,20 @@ +DisplayCount=1 +#个性化分辨率,如果使用这个功能,需要额外设置UseEdid=false +#Displays=1920, 1080, 60;800, 600, 120 +#是否使用edid,默认是为true +#UseEdid=true +#默认使用硬件鼠标 +#HardwareCursor=true +#None 0 Error 1 Warn 2 Info 3 Debug 4 +#日志级别默认是3 +#LogLevel=3 +#自定义刷新率,全局 +#Rates=60,90,120 +#后面是分辨率,分辨率只能放在文件末尾 +1920, 1080 +800, 600 +1024, 768 +1280, 720 +1366, 768 +2560, 1440 +3840, 2160 \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/readme.md b/Virtual Display Driver (HDR)/MttVDD/readme.md index e7fd0c05..d2def6ea 100644 --- a/Virtual Display Driver (HDR)/MttVDD/readme.md +++ b/Virtual Display Driver (HDR)/MttVDD/readme.md @@ -1,14 +1,12 @@ -# Get 5 virtual monitors for the price of zero! - -# Steps (for experienced pros only!!!) -1. Download the latest release as a zip file. - -2. As an administrator, Run the *.bat file to add the driver certificate as a trusted root certificate. - -3. Don't install the inf. Open device manager, click on any device, then click on the "Action" menu and click "Add Legacy Hardware" - -4. Select "Add hardware from a list (Advanced)" and then select Display adapters - -5. Click "Have Disk..." and click the "Browse..." button. Navigate to the extracted files and select the inf file. - -6. You are done! Go to display settings to customize the resolution of the additional displays. These displays show up in Oculus and should be able to be streamed from. \ No newline at end of file +### 开发参考 +https://learn.microsoft.com/zh-cn/windows-hardware/drivers/gettingstarted/provision-a-target-computer +大致的安装步骤如下: +1. 安装 C:\Program Files (x86)\Windows Kits\10\Remote\x64\WDK Test Target Setup x64-x64_en-us.msi +2. 如果目标计算机正在运行 Windows Server,请查找刚刚由 WDK 测试目标安装程序 MSI 创建的 DriverTest 文件夹。 (示例:c:\DriverTest)。 + 选择并按住 DriverTest 文件夹(或右键单击),然后选择 “属性”。 在“安全”选项卡上,向“经过身份验证的用户组”授予“修改”权限。 + +### 已知问题 +1. 如果windows启用网络共享(非高级共享),会导致目录无法访问,引发驱动加载失败,这个问题暂时只能通过不启用网络共享来解决。 + +### 特别说明 +1. windows 10 上内置的UMDF是1.4版本的,windows 11以上才内置2.0以上版本。 \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/MttVDD/version.h b/Virtual Display Driver (HDR)/MttVDD/version.h new file mode 100644 index 00000000..cd39d8c9 --- /dev/null +++ b/Virtual Display Driver (HDR)/MttVDD/version.h @@ -0,0 +1,3 @@ +#pragma once + +#define VERSION "1.0.26.071001" diff --git a/Virtual Display Driver (HDR)/option.txt b/Virtual Display Driver (HDR)/option.txt deleted file mode 100644 index 7bc082db..00000000 --- a/Virtual Display Driver (HDR)/option.txt +++ /dev/null @@ -1,73 +0,0 @@ -1 -640, 480, 30 -640, 480, 500 -800, 5000, 500 -1024, 768, 500 -1152, 864, 500 -1280, 5000, 500 -1280, 720, 500 -1280, 768, 500 -1280, 800, 500 -1280, 960, 500 -1280, 1024, 500 -1360, 768, 500 -1366, 768, 500 -1400, 1050, 500 -1440, 900, 500 -1600, 900, 500 -1680, 1050, 500 -1600, 1024, 500 -1920, 1080, 500 -1920, 1200, 500 -1920, 1440, 500 -2560, 1440, 500 -2560, 1600, 500 -2732, 2048, 500 -2880, 1620, 500 -2880, 1800, 500 -3008, 1692, 500 -3200, 1800, 500 -3200, 2400, 500 -3840, 2160, 500 -3840, 2400, 500 -4096, 2304, 500 -4096, 2560, 500 -5120, 2880, 500 -6016, 3384, 500 -7680, 4320, 500 -2560, 1080, 500 -2880, 1200, 500 -3440, 1440, 500 -3840, 1600, 500 -4320, 1800, 500 -5120, 2160, 500 -5760, 2400, 500 -6880, 2880, 500 -7680, 3200, 500 -8640, 3600, 500 -10240, 432, 500 -2880, 900, 500 -3840, 1080, 500 -3840, 1200, 500 -4320, 1200, 500 -5120, 1440, 500 -5120, 1600, 500 -5760, 1600, 500 -5760, 1800, 500 -6400, 1800, 500 -6480, 1800, 500 -7680, 2160, 500 -7680, 2400, 500 -8640, 2400, 500 -2388, 1668, 500 -2400, 1080, 500 -3456, 2234, 500 -8640, 3600, 30 -8640, 3600, 60 -8640, 3600, 75 -8640, 3600, 90 -8640, 3600, 120 -8640, 3600, 144 -8640, 3600, 165 -8640, 3600, 240 -8640, 3600, 480 \ No newline at end of file diff --git a/Virtual Display Driver (HDR)/vdd_settings.xml b/Virtual Display Driver (HDR)/vdd_settings.xml deleted file mode 100644 index 4bf082c3..00000000 --- a/Virtual Display Driver (HDR)/vdd_settings.xml +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - 1 - - - - default - - - - - - 60 - 90 - 120 - 144 - 165 - 240 - - - - - 1920 - 1080 - 60 - - - 2560 - 1440 - 60 - - - 3840 - 2160 - 60 - - - - - - true - false - false - - - - - false - false - RGB - - - - - true - 128 - 128 - true - - 2 - - - - - false - false - false - - - - - false - false - EDID/monitor_profile.xml - false - true - - - - - - false - 1000.0 - 0.05 - 1000 - 400 - - - false - 0.640 - 0.330 - 0.300 - 0.600 - 0.150 - 0.060 - 0.3127 - 0.3290 - - - false - 2.2 - sRGB - false - - - - - - false - manual - - 24 - 240 - false - 640 - 480 - 7680 - 4320 - - - false - 1920 - 1080 - 60 - - - - - - - false - 8 - true - - - - 80.0 - - - - - - \ No newline at end of file diff --git a/docs/windows-hardware-drivers-display.pdf b/docs/windows-hardware-drivers-display.pdf new file mode 100644 index 00000000..3d15528c Binary files /dev/null and b/docs/windows-hardware-drivers-display.pdf differ diff --git a/scripts/README.md b/scripts/README.md deleted file mode 100644 index d7cc2f75..00000000 --- a/scripts/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# VDD Utilities - -This folder contains utility scripts to help manage the Virtual Display Driver. - -## Toggle_VDD.bat - -A menu-based batch script to easily enable/disable VDD without opening Device Manager. - -### Features -- Real-time status display -- Enable/Disable VDD with one click -- Check detailed device information -- No external dependencies required - -### Requirements -- Windows 10/11 -- Administrator privileges -- PowerShell (built-in) - -### Usage -1. Right-click `Toggle_VDD.bat` → Properties → Advanced -2. Check "Run as administrator" → OK -3. Double-click to run - -### Menu Options -1. Enable VDD -2. Disable VDD -3. Check Status Only -4. Exit - -Perfect for remote desktop users who frequently toggle VDD!