5

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.

wywywywy
  • 61
  • 6
  • `DISPLAYCONFIG_SDR_WHITE_LEVEL` is [probably](https://www.computerbase.de/forum/threads/hdr-unter-windows-howto.2046426/page-27#post-26343543) what you are looking for. P.s. if you have to do registry comparisons, *regshot* is probably the better tool. – mirh Mar 13 '23 at 20:17
  • Thanks @mirh. I've looked at that already but it's only to GET the current value, not to SET. – wywywywy Mar 14 '23 at 21:04
  • Duh, right.. there's no `DISPLAYCONFIG_DEVICE_INFO_SET_SDR_WHITE_LEVEL`. If it can help though, after much grepping I found a `Windows::Graphics::Display::CDisplayInformation::_CallbackSdrWhiteLevelChanged` function (perhaps related to the `WNF_DX_SDR_WHITE_LEVEL_CHANGED` event), and that the relative control panel should be implemented inside *SettingsHandlers_PCDisplay.dll* (there's more mentions of SDRWhiteLevelSliderValueChanged and a reference to SystemSettings_Display_AdvancedDisplaySettingsHDRStatus_SDR which should be the corresponding setting key in Windows.UI.SettingsAppThreshold). – mirh Mar 20 '23 at 18:11
  • SO is a programming Q&A platform and this question is not about programming. Questions about operating systems, their utilities, networking and hardware, are off topic here. [What topics can I ask about here?](https://stackoverflow.com/help/on-topic). Please delete this and ask, instead, on https://superuser.com/ – Rob Jun 18 '23 at 12:13
  • 2
    OP specifically asked for a programmatic (winapi) way to control brightness, i don't see how that is offtopic here and should be asked on SU. – lulle2007200 Jun 18 '23 at 20:26

2 Answers2

5

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.

lulle2007200
  • 888
  • 9
  • 20
  • 1
    Thanks for this, I made a C# version based on this that works perfectly, I added that as another answer to the question. – MaloW Jun 20 '23 at 15:38
  • It works! Magic! Thank you very much! I wonder why the brightness ranges from 1.0 to 6.0, but I'm not going to complain. Just out of curiosity - could you share how you figured this out please? – wywywywy Jun 20 '23 at 19:04
  • 2
    @wywywywy Basically, run settings app under debugger, find the dll it loads for handling display settings, in that dll, find the event handler that is called when the value of the sdr brightness slider is changed, walk throught that handler until you find a call to dwmapi.dll. As for the brightness range, 1.0 corresponds to the min., 6.0 to the max. brightness the display supports. You can technically set it to >6.0, but then it gets clamped down, so you get white-oversaturated display output. – lulle2007200 Jun 21 '23 at 09:42
3

Here's a C# version I made based on lulle2007200's answer:

using System;
using System.Runtime.InteropServices;

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

        private delegate void DwmpSDRToHDRBoostPtr(IntPtr monitor, double brightness);

        static void Main(string[] args)
        {
            var primaryMonitor = MonitorFromWindow(IntPtr.Zero, 1);
            var hmodule_dwmapi = LoadLibrary("dwmapi.dll");
            DwmpSDRToHDRBoostPtr changeBrightness = Marshal.GetDelegateForFunctionPointer<DwmpSDRToHDRBoostPtr>(GetProcAddress(hmodule_dwmapi, 171));

            double brightness = 1.0;
            changeBrightness(primaryMonitor, brightness);
        }
    }
}
MaloW
  • 115
  • 4