Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controlling SDR content brightness programmatically in Windows 11

When using an HDR-enabled display in Windows 11, we can use the "SDR content brightness" slider in Settings > System > Display > HDR to control how bright the regular non-HDR elements are on screen.

E.g. if the display has a max of 400nit and the slider is set to 60%, the SDR content is shown at 240nit brightness.

Windows 10 has something similar too.

I'd like to be able to change the value of the slider programmatically, so I can have a brighter desktop during the day, and a dimmer one during the evening.

The "old" ways changing brightness through WMI or DDC/CI don't work on HDR-enabled displayed.

I used ProcessMonitor to see what the slider does, but it generates way too many entries, and the only relevant bits I can see are that it changes the registry key Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\MonitorDataStore\DELA1E4#G7IYMxgwAAgX_10_07E6_A1\SDRWhiteLevel.

The value is from 1000 (0%) to 3500 (50%) to 6000 (100%) in dec on my display, where 1000 equals 80nit.

Once the SystemSettings.exe has changed the registry value, it calls something in D3D12 to actually apply the change.

Looking through the Microsoft documentation on Win32 API, I can't find any relevant information.

Anyone knows how to get it to work please? Any language will do, PowerShell, C#, C++, Python...

Thank you.

like image 917
wywywywy Avatar asked Apr 19 '26 00:04

wywywywy


2 Answers

After a bunch of digging i found how SDR brightness is set:

dwmapi.dll has a function HRESULT DwmpSDRToHDRBoost(HMONITOR monitor, double brightness) that is exported by ordinal 171, where brightness must be 1.0 (80 nits) or greater. The maximum the settings app allows is 6.0 (480 nits), but it can be set higher. If you set a value higher than the brightness your monitor supports, you get clipping.

The settings app additionally sets the registry key you mentioned, however i didn't find anything that references that key.

Here is a minimal example:

#include <Windows.h>

using DwmpSDRToHDRBoostPtr = HRESULT(__stdcall *)(HMONITOR, double);

HMONITOR GetPrimaryMonitor(){
    return MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
}

int main()
{
    double brightness = 6.0;
    HMODULE hmodule_dwmapi = LoadLibraryW(L"dwmapi.dll");
    DwmpSDRToHDRBoostPtr DwmpSDRToHDRBoost = reinterpret_cast<DwmpSDRToHDRBoostPtr>(GetProcAddress(hmodule_dwmapi, MAKEINTRESOURCE(171)));
    DwmpSDRToHDRBoost(GetPrimaryMonitor(), brightness);
}

Keep in mind, that this is undocumented API and the ordinal and behaviour might change in the future.


Update:

Atleast in the latest Windows 11 insider previews, setting SDR brightness with DwmpSdrToHdrBoost does not update the slider in Windows settings anymore. The value reported by DisplayAdvancedColorInfo.SdrWhiteLevelInNits also doesn't update. They probably cache the values somewhere or read it from registry.

like image 173
lulle2007200 Avatar answered Apr 21 '26 12:04

lulle2007200


Here's a PowerShell script and an AutoHotkey script based on lulle2007200's and MaloW's answers


SetBrightness.ps1

param(
    [Parameter(Mandatory = $true, Position = 0,
        HelpMessage = "Brightness value between 1.0 and 6.0")]
    [ValidateRange(1.0, 6.0)][double]$brightness
)

Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;

public class ScreenBrightnessSetter
{
    [DllImport("user32.dll")]
    public static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
    [DllImport("kernel32", CharSet = CharSet.Unicode)]
    public static extern IntPtr LoadLibrary(string lpFileName);
    [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true,
        SetLastError = true)]
    public static extern IntPtr GetProcAddress(IntPtr hModule, int address);

    public delegate void DwmpSDRToHDRBoostPtr(IntPtr monitor,
        double brightness);
}
"@

$primaryMonitor = [ScreenBrightnessSetter]::MonitorFromWindow([IntPtr]::Zero, 1)
$hmodule_dwmapi = [ScreenBrightnessSetter]::LoadLibrary("dwmapi.dll")
$procAddress = [ScreenBrightnessSetter]::GetProcAddress($hmodule_dwmapi, 171)

$changeBrightness = (
    [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
        $procAddress, [ScreenBrightnessSetter+DwmpSDRToHDRBoostPtr])
)

$changeBrightness.Invoke($primaryMonitor, $brightness)

SetBrightness.ahk

#Requires AutoHotkey v2.0

SetBrightness(value) {
    cmd := 'powershell.exe -ExecutionPolicy Bypass -Command '
    cmd .= '"& .\SetBrightness.ps1 ' . value . '"'
    RunWait cmd, A_ScriptDir, "Hide"
}

>^>+1::SetBrightness(1)
>^>+2::SetBrightness(2)
>^>+3::SetBrightness(3)
>^>+4::SetBrightness(4)
>^>+5::SetBrightness(5)
>^>+6::SetBrightness(6)
like image 45
Sam Hylamia Avatar answered Apr 21 '26 14:04

Sam Hylamia



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!