WindowsDisplayManager.psm1

$maxNbrDisplaysSupported = 100

function GetAllPotentialDisplays() {
    # Loop through all display devices by index starting at 0 until no display is returned
    $displays = @()
    for ($i = 0; $i -lt $maxNbrDisplaysSupported; $i++) {
        $display = _GetDisplay($i)
        # If there is no display at index i, we can assume there are only i-1 displays in the system
        if ($null -eq $display) { return $displays }
        $displays += $display
    }
    return $displays
}

function GetEnabledDisplays() {
    $enabledDisplays = @()
    foreach ($display in GetAllPotentialDisplays) {
        if ($display.Enabled) { $enabledDisplays += $display }
    }
    if ($enabledDisplays.Length -eq 0) { Write-PSFMessage -Level Warning -Message "No enabled displays found" }
    return $enabledDisplays
}

function GetPrimaryDisplay() {
    for ($i = 0; $i -lt $maxNbrDisplaysSupported; $i++) {
        $display = _GetDisplay($i)
        if ($display.Primary) { return $display }
    }
    Write-PSFMessage -Level Warning -Message "No primary display found"
    return $null
}

function GetDisplayByMonitorName($monitorName) {
    # Gets the first enabled display if any with the target friendly name of monitorName
    for ($i = 0; $i -lt $maxNbrDisplaysSupported; $i++) {
        $display = _GetDisplay($i)
        if ($display.Target.FriendlyName -eq $monitorName) { return $display }
    }
    Write-PSFMessage -Level Debug -Message "No display found for monitor $monitorName. Is the display enabled?"
    return $null
}

function _GetDisplay($index) {
    # We expect an error if the display source at that index does not exist- still try to fetch a couple times to be safe in case of any transient failures
    $maxAttempts = 2
    $attemptDelayMs = 100
    for ($i = 0; $i -lt $maxAttempts; $i++) {
        Start-Sleep -Milliseconds $attemptDelayMs

        $displayDevice = New-Object DisplayDevices+DisplayDevice
        $displayDevice.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($displayDevice)
        $enumDisplayDevicesResult = [DisplayDevices]::EnumDisplayDevices([NullString]::Value, $index, [ref]$displayDevice, [DisplayDevices+EnumDisplayDevicesFlags]::None)
        if (-not $enumDisplayDevicesResult) { continue }

        $pathsCount = 0;
        $modesCount = 0;
        $displayConfigBufferSizesResult = [DisplayConfig]::GetDisplayConfigBufferSizes([DisplayConfig+QueryDisplayConfigFlags]::OnlyActivePaths, [ref]$pathsCount, [ref]$modesCount);
        if ([Win32Error]$displayConfigBufferSizesResult -ne [Win32Error]::ERROR_SUCCESS) {
            Write-PSFMessage -Level Critical -Message "Failed to get display configuration buffer for source index $index (error code $([Win32Error]$displayConfigBufferSizesResult))"
            continue
        }
        $paths = @()
        $modes = @()
        $displayConfigResult = [DisplayConfig]::QueryDisplayConfig([DisplayConfig+QueryDisplayConfigFlags]::OnlyActivePaths, [ref]$pathsCount, [ref]$paths, [ref]$modesCount, [ref]$modes);
        if ([Win32Error]$displayConfigResult -ne [Win32Error]::ERROR_SUCCESS) {
            Write-PSFMessage -Level Critical -Message "Failed to get display configuration for source index $index (error code $([Win32Error]$displayConfigResult))"
            continue
        }

        foreach ($path in $paths) {
            if ($path.sourceInfo.id -eq $index) {
                return [Display]::new($path.sourceInfo.id, $displayDevice, $path.targetInfo)
            }
        }
        return [Display]::new($index, $displayDevice)
    }
    return $null
}

function GetRefreshedDisplay($display) {
    # Get any potential display currently at the source id of the input display
    return _GetDisplay($display.Source.Id)
}

# TODO find out why the positions aren't getting applied properly (one is just getting placed next to the other, despite us supplying the correct offset values)
function SetPrimaryDisplay($display) {
    $enabledDisplays = GetEnabledDisplays
    $displayToUpdate = $null
    foreach ($enabledDisplay in $enabledDisplays) {
        if (-not $enabledDisplay.Equals($display)) { continue }
        if ($enabledDisplay.Primary) {
            Write-PSFMessage -Level Debug -Message "Display $($enabledDisplay.Description) is already the primary display, nothing to change"
            return $true
        }
        $displayToUpdate = $enabledDisplay
    }
    if (-not $displayToUpdate) {
        Write-PSFMessage -Level Debug -Message "Unable to set Display $($display.Description) as primary- could not find currently enabled display at its source and target"
        return $true
    }

    $displayToUpdateDeviceMode = $displayToUpdate.Source._GetDisplaySettingsDeviceMode()
    if ($null -eq $displayToUpdateDeviceMode) { return $false }
    $positionOffset = [Position]::new($displayToUpdateDeviceMode.dmPositionX, $displayToUpdateDeviceMode.dmPositionY)

    $displayToUpdateDeviceMode.dmPositionX = 0
    $displayToUpdateDeviceMode.dmPositionY = 0
    $primaryChangeDisplaySettingsFlags = [DisplaySettings+ChangeDisplaySettingsFlags]::UpdateRegistry -bor [DisplaySettings+ChangeDisplaySettingsFlags]::SetPrimary -bor [DisplaySettings+ChangeDisplaySettingsFlags]::NoReset
    $primaryChangeDisplaySettingsResult = [DisplaySettings]::ChangeDisplaySettingsEx($displayToUpdate.Source.Name, [ref]$displayToUpdateDeviceMode, $primaryChangeDisplaySettingsFlags)
    if ($primaryChangeDisplaySettingsResult -ne [DisplaySettings+ChangeDisplaySettingsResult]::DISP_CHANGE_SUCCESSFUL) {
        Write-PSFMessage -Level Critical -Message "Failed to adjust position of display $($displayToUpdate.Description) (error code $([DisplaySettings+ChangeDisplaySettingsResult]$primaryChangeDisplaySettingsResult))"
        return $false
    }

    foreach ($enabledDisplay in $enabledDisplays) {
        if ($enabledDisplay.Equals($displayToUpdate)) { continue }
        if (-not $enabledDisplay.Source._GetIsActive()) { continue }
        $otherDisplayDeviceMode = $enabledDisplay.Source._GetDisplaySettingsDeviceMode()
        if ($null -eq $otherDisplayDeviceMode) { return $false }
        $otherDisplayDeviceMode.dmPositionX -= $positionOffset.X
        $otherDisplayDeviceMode.dmPositionY -= $positionOffset.Y
        Write-PSFMessage -Level Debug -Message "Display $($enabledDisplay.Description) will be moved to position $($otherDisplayDeviceMode.dmPositionX),$($otherDisplayDeviceMode.dmPositionY)"
        $otherChangeDisplaySettingsFlags = [DisplaySettings+ChangeDisplaySettingsFlags]::UpdateRegistry -bor [DisplaySettings+ChangeDisplaySettingsFlags]::NoReset
        $otherChangeDisplaySettingsResult = [DisplaySettings]::ChangeDisplaySettingsEx($enabledDisplay.Source.Name, [ref]$otherDisplayDeviceMode, $otherChangeDisplaySettingsFlags)
        if ($otherChangeDisplaySettingsResult -ne [DisplaySettings+ChangeDisplaySettingsResult]::DISP_CHANGE_SUCCESSFUL) {
            Write-PSFMessage -Level Critical -Message "Failed to adjust position of display $($enabledDisplay.Description) (error code $([DisplaySettings+ChangeDisplaySettingsResult]$otherChangeDisplaySettingsResult))"
            return $false
        }
    }

    $commitChangeDisplaySettingsResult = [DisplaySettings]::ChangeDisplaySettingsEx([NullString]::Value)
    if ($commitChangeDisplaySettingsResult -ne [DisplaySettings+ChangeDisplaySettingsResult]::DISP_CHANGE_SUCCESSFUL) {
        Write-PSFMessage -Level Critical -Message "Unable to set Display $($display.Description) as primary- failed to commit display settings (error code $([DisplaySettings+ChangeDisplaySettingsResult]$commitChangeDisplaySettingsResult))"
        return $false
    }
    Write-PSFMessage -Level Verbose -Message "Display $($displayToUpdate.Description) set successfully as primary display"
    return $true
}

function SaveDisplaysToFile($displays, $filePath) {
    try {
        ($displays | ConvertTo-Json | Out-String).Trim() | Set-Content -Path $filePath
        return $true
    }
    catch {
        Write-PSFMessage -Level Critical -Message "Error saving displays to file $filePath" -ErrorRecord $_
        return $false
    }
}

function LoadDisplayStatesFromFile($filePath) {
    return (Get-Content -Raw -Path $filePath | ConvertFrom-Json)
}

function UpdateDisplaysFromFile() {
    param(
        [string]$filePath,
        # Option of whether to disable any currently enabled displays which are not present in the specified display states
        [switch]$disableNotSpecifiedDisplays,
        # Option of whether to double check that all updates occurred as expected after windows reports everything was successful
        [switch]$validate
    )

    $displayStates = LoadDisplayStatesFromFile -filePath $filePath
    if (-not $displayStates) { return $false }
    return UpdateDisplaysToStates -displayStates $displayStates -disableNotSpecifiedDisplays:$disableNotSpecifiedDisplays -validate:$validate
}

function UpdateDisplaysToStates() {
    param(
        [PSCustomObject[]]$displayStates,
        # Option of whether to disable any currently enabled displays which are not present in the specified display states
        [switch]$disableNotSpecifiedDisplays,
        # Option of whether to double check that all updates occurred as expected after windows reports everything was successful
        [switch]$validate
    )

    if (-not $displayStates) {
        Write-PSFMessage -Level Verbose -Message "No display states specified- nothing to update"
        return $true
    }
    $allDisplays = GetAllPotentialDisplays
    if (-not $allDisplays) { return $false }
    Write-PSFMessage -Level Debug -Message "Updating displays to the following states:"
    $displayStates | ForEach-Object { Write-PSFMessage -Level Debug -Message $(($_ | ConvertTo-Json -Compress | Out-String).Trim()) }
    Write-PSFMessage -Level Debug -Message "Enabled displays before updating:"
    $allDisplays | ForEach-Object { if ($_.Enabled) { Write-PSFMessage -Level Debug -Message $($_.ToTableString()) } }
    $allUpdatesSuccessful = $true

    # First, enable any monitors and set primary as needed
    foreach ($displayState in @($displayStates)) {
        if ($displayState.Enabled -or $displayState.Primary) {
            $displayToUpdate = $null
            # Update the existing enabled display connected to the target if there is one, else the first available display source
            foreach ($display in $allDisplays) {
                if (($null -ne $displayState.Target.Id) -and ($display.Target.Id -eq $displayState.Target.Id)) {
                    $displayToUpdate = $display
                    break
                } elseif ((-not $displayToUpdate) -and ($display.Enabled -eq $false)) { 
                    $displayToUpdate = $display
                }
            }
            if (-not $displayToUpdate) {
                Write-PSFMessage -Level Warning -Message "No available display source found to enable for monitor $($displayState.Description)- are there open outputs on your host device?"
                continue
            }

            if (-not $displayToUpdate.Enable($displayState.Target.Id)) { $allUpdatesSuccessful = $false } 
            elseif ($displayToUpdate.Enabled) {
                # Refresh target info and set primary if required after effective enable
                $displayToUpdate = GetRefreshedDisplay -display $displayToUpdate
                if ($displayState.Primary) {
                    if (-not (SetPrimaryDisplay -display $displayToUpdate)) { $allUpdatesSuccessful = $false }
                }
            }
        }
    }

    # Enabling and setting primary for displays may effect source -> target mapping. Refresh the active displays so that everything is accurate
    $currentEnabledDisplays = GetEnabledDisplays

    # Then, set graphics settings or disable as needed
    foreach ($displayState in @($displayStates)) {
        $displayToUpdate = $null
        foreach ($enabledDisplay in $currentEnabledDisplays) {
            if (($null -ne $displayState.Target.Id) -and ($enabledDisplay.Target.Id -eq $displayState.Target.Id)) { 
                $displayToUpdate = $enabledDisplay
                break
            }
        }
        if (-not $displayToUpdate) {
            Write-PSFMessage -Level Debug -Message "No enabled display found to update for display state of $($displayState.Description)"
            continue
        }
        if ($displayState.Enabled -eq $false) { 
            if (-not $displayToUpdate.Disable()) { $allUpdatesSuccessful = $false }
            continue
        }
        if ($displayState.HdrInfo.HdrEnabled -is "boolean") {
            if ($displayState.HdrInfo.HdrEnabled) {
                if (-not $displayToUpdate.EnableHdr()) { $allUpdatesSuccessful = $false }
            } else {
                if (-not $displayToUpdate.DisableHdr()) { $allUpdatesSuccessful = $false }
            }
        }
        if (($null -ne $displayState.Resolution.Width) -and ($null -ne $displayState.Resolution.Height)) {
            # If we fail to set resolution with refresh rate, at least still try width x height
            if (-not ($displayToUpdate.SetResolution($displayState.Resolution.Width, $displayState.Resolution.Height, $displayState.Resolution.RefreshRate) `
                -or $displayToUpdate.SetResolution($displayState.Resolution.Width, $displayState.Resolution.Height))) {
                    $allUpdatesSuccessful = $false
            }
        }
    }

    # If requested, try to disable any currently enabled displays which aren't present in the file
    if ($disableNotSpecifiedDisplays) {
        foreach ($display in $currentEnabledDisplays) {
            if ((@($displayStates) | Where-Object { ($null -ne $_.Target.Id) -and ($_.Target.Id -eq $display.Target.Id) }).Length -eq 0) {
                if (-not $display.Disable()) { $allUpdatesSuccessful = $false }
            }
        }
    }

    Write-PSFMessage -Level Debug -Message "Enabled displays after updating:"
    # We may have disabled some displays in the last step- filter those out
    $currentEnabledDisplays | ForEach-Object {  if ($_.Enabled) { Write-PSFMessage -Level Debug -Message $($_.ToTableString()) } }

    # Validate no errors were encountered during update. If requested, wait a short time and double check everything was successful.
    if (-not $allUpdatesSuccessful) { return $false }
    if ($validate) {
        Start-Sleep -Milliseconds 500
        if (-not (CurrentDisplaysAreSameAsStates -validateAllEnabledDisplaysSpecified:$disableNotSpecifiedDisplays -displayStates $displayStates)) {
            Write-PSFMessage -Level Warning -Message "Unable to validate that all display settings were updated correctly"
            return $false
        }
    }
    return $true
}

function CurrentDisplaysAreSameAsFile() {
    param(
        [string]$filePath,
        # Option of whether to validate that all enabled displays are represented in the specified display states
        [switch]$validateAllEnabledDisplaysSpecified
    )

    $displayStates = LoadDisplayStatesFromFile -filePath $filePath
    if (-not $displayStates) { return $false }
    return CurrentDisplaysAreSameAsStates -validateAllEnabledDisplaysSpecified:$validateAllEnabledDisplaysSpecified -displayStates $displayStates
}

function CurrentDisplaysAreSameAsStates() {
    param(
        [PSCustomObject[]]$displayStates,
        # Option of whether to validate that all enabled displays are represented in the specified display states
        [switch]$validateAllEnabledDisplaysSpecified
    )

    $allDisplays = GetAllPotentialDisplays
    if (-not $allDisplays) { return $false }
    foreach ($display in $allDisplays) {
        $matchingDisplayState = $null
        foreach ($displayState in $displayStates) {
            if (($null -ne $displayState.Target.Id) -and ($display.Target.Id -eq $displayState.Target.Id)) {
                $matchingDisplayState = $displayState
                break
            } elseif (($null -eq $displayState.Target.Id) -and ($null -ne $displayState.Source.Id) -and ($display.Source.Id -eq $displayState.Source.Id)) { 
                $matchingDisplayState = $displayState
                break
            }
        }

        # If requested, fail if any currently enabled displays aren't present in the specified states
        if ($validateAllEnabledDisplaysSpecified -and $display.Enabled -and -not $matchingDisplayState) {
            Write-PSFMessage -Level Debug -Message "Enabled display $($display.Description) not present in specified states"
            return $false
        }
        # Fail if any current display states don't match enablement of any matching record in the file
        if (($matchingDisplayState.Enabled -is "boolean") -and ($display.Enabled -ne $matchingDisplayState.Enabled)) {
            Write-PSFMessage -Level Debug -Message "Actual display enablement of $($display.Description) is not $($matchingDisplayState.Enabled)"
            return $false
        }
        if (-not $matchingDisplayState.Enabled) { continue }

        # Fail if resolution or hdr differ on any current display which is in the specified states
        if (($matchingDisplayState.HdrInfo.HdrEnabled -is "boolean") -and ($display.HdrInfo.HdrEnabled -ne $matchingDisplayState.HdrInfo.HdrEnabled)) {
            Write-PSFMessage -Level Debug -Message "Actual display $($display.Description) HDR enablement is not $($matchingDisplayState.HdrInfo.HdrEnabled)"
            return $false
        }
        $displayResolution = $display.Resolution
        $refreshRateTolerance = 3 # Tolerance for when to consider a refresh rate close enough to be considered equivalent
        if (($null -ne $matchingDisplayState.Resolution) -and `
                ($displayResolution.Width -ne $matchingDisplayState.Resolution.Width `
                -or $displayResolution.Height -ne $matchingDisplayState.Resolution.Height `
                -or ($displayResolution.RefreshRate -and [Math]::Abs($displayResolution.RefreshRate - $matchingDisplayState.Resolution.RefreshRate) -gt $refreshRateTolerance))) {
                    $matchingDisplayResolutionStr = "$($matchingDisplayState.Resolution.Width)x$($matchingDisplayState.Resolution.Height)@$($matchingDisplayState.Resolution.RefreshRate)fps"
                    Write-PSFMessage -Level Debug -Message "Actual display $($display.Description) resolution is not $matchingDisplayResolutionStr)"
                return $false
        }
    }
    return $true
}