Does anyone have code to Change the DPI of the current monitor?

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
iseahound
Posts: 1472
Joined: 13 Aug 2016, 21:04
Contact:

Does anyone have code to Change the DPI of the current monitor?

Post by iseahound » 07 Mar 2024, 18:28

Was wondering if anyone had already written a utility to change the DPI of the monitor.
image.png
image.png (63.01 KiB) Viewed 255 times
Here is some half finished work:

Code: Select all

; struct DISPLAYCONFIG_DEVICE_INFO_HEADER
NumPut(  "uint", -3    ; DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE
      ,  "uint", 28
      ,  "uint", adapterID
      ,  "uint", sourceID
      ,   "int", 0     ; minScaleRel
      ,   "int", 0 
      ,   "int", 0 
      , requestPacket := Buffer(28))
if !DllCall("DisplayConfigGetDeviceInfo", "ptr", requestPacket)
   throw OSError()

; Each relative scaling value is defined as the number of steps from the recommended scaling.
; For example, if minScaleRel is -3, then the recommended scaling is 175%.
; This is because 100% is 3 steps down from 175%.

minScaleRel := NumGet(requestPacket, 16, "int")
curScaleRel := NumGet(requestPacket, 20, "int")
maxScaleRel := NumGet(requestPacket, 24, "int")

curScaleRel := max(minScaleRel, curScaleRel)
curScaleRel := min(maxScaleRel, curScaleRel)

NumPut(  "uint", -4    ; DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE
      ,  "uint", 28
      ,  "uint", adapterID
      ,  "uint", sourceID
      ,   "int", minScaleRel     ; desiredScaleRel
      , setPacket := Buffer(28))

DllCall("DisplayConfigSetDeviceInfo", "ptr", setPacket)

/*
  DISPLAYCONFIG_DEVICE_INFO_TYPE type;
  UINT32                         size;
  LUID                           adapterId;
  UINT32                         id;
*/

/*
* struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
* @brief used to fetch min, max, suggested, and currently applied DPI scaling values.
* All values are relative to the recommended DPI scaling value
* Note that DPI scaling is a property of the source, and not of target.
struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
{
    DISPLAYCONFIG_DEVICE_INFO_HEADER            header;
 
    * @brief min value of DPI scaling is always 100, minScaleRel gives no. of steps down from recommended scaling
    * eg. if minScaleRel is -3 => 100 is 3 steps down from recommended scaling => recommended scaling is 175%
    
    std::int32_t minScaleRel;

    
    * @brief currently applied DPI scaling value wrt the recommended value. eg. if recommended value is 175%,
    * => if curScaleRel == 0 the current scaling is 175%, if curScaleRel == -1, then current scale is 150%
    
    std::int32_t curScaleRel;

    
    * @brief maximum supported DPI scaling wrt recommended value
    
    std::int32_t maxScaleRel;
};
*/
/*
* struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
* @brief set DPI scaling value of a source
* Note that DPI scaling is a property of the source, and not of target.

struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
{
    DISPLAYCONFIG_DEVICE_INFO_HEADER            header;
    
    * @brief The value we want to set. The value should be relative to the recommended DPI scaling value of source.
    * eg. if scaleRel == 1, and recommended value is 175% => we are trying to set 200% scaling for the source
    
    int32_t scaleRel;
};
*/

/*
* struct DPIScalingInfo
* @brief DPI info about a source
* mininum :     minumum DPI scaling in terms of percentage supported by source. Will always be 100%.
* maximum :     maximum DPI scaling in terms of percentage supported by source. eg. 100%, 150%, etc.
* current :     currently applied DPI scaling value
* recommended : DPI scaling value reommended by OS. OS takes resolution, physical size, and expected viewing distance
*               into account while calculating this, however exact formula is not known, hence must be retrieved from OS
*               For a system in which user has not explicitly changed DPI, current should eqaul recommended.
* bInitDone :   If true, it means that the members of the struct contain values, as fetched from OS, and not the default
*               ones given while object creation.

struct DPIScalingInfo
{
    UINT32 mininum = 100;
    UINT32 maximum = 100;
    UINT32 current = 100;
    UINT32 recommended = 100;
    bool bInitDone = false;
};
*/
See: https://github.com/lihas/windows-DPI-scaling-sample/blob/master/DPIHelper/DpiHelper.h


iseahound
Posts: 1472
Joined: 13 Aug 2016, 21:04
Contact:

Re: Does anyone have code to Change the DPI of the current monitor?

Post by iseahound » 08 Mar 2024, 11:05

It's close. I think the structures I'm showing are undocumented. Teadrinker uses only the publicly available API.

Some issues I had were trying to convert the sourceID and adapterID into a simple hMonitor handle. If these two are connected, it should be able to do:
  • A hotkey that sets the recommended scaling value.
  • A hotkey that increases the scaling value by 1 to the next increment.
  • A hotkey that decreases the scaling value by 1 to the next increment.
Teadrinker's code just guesses at possible scaling values.

iseahound
Posts: 1472
Joined: 13 Aug 2016, 21:04
Contact:

Re: Does anyone have code to Change the DPI of the current monitor?

Post by iseahound » 08 Mar 2024, 12:11

On that note, here's some code that connects the sourceID and adapterID to an hMonitor handle:

Code: Select all

#include <Windows.h>
#include <stdio.h>
#include <vector>
#include <tuple>
#include <string>

int main()
{
    // get all paths
    UINT pathCount;
    UINT modeCount;
    if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount))
        return 0;

    std::vector<DISPLAYCONFIG_PATH_INFO> paths(pathCount);
    std::vector<DISPLAYCONFIG_MODE_INFO> modes(modeCount);
    if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.data(), &modeCount, modes.data(), nullptr))
        return 0;

    // enum all monitors => (handle, device name)>
    std::vector<std::tuple<HMONITOR, std::wstring>> monitors;
    EnumDisplayMonitors(nullptr, nullptr, [](HMONITOR hmon, HDC hdc, LPRECT rc, LPARAM lp)
    {
        MONITORINFOEX mi = {};
        mi.cbSize = sizeof(MONITORINFOEX);
        GetMonitorInfo(hmon, &mi);
        auto monitors = (std::vector<std::tuple<HMONITOR, std::wstring>>*)lp;
        monitors->push_back({ hmon, mi.szDevice });
        return TRUE;
    }, (LPARAM)&monitors);

    // for each path, get GDI device name and compare with monitor device name
    for (UINT i = 0; i < pathCount; i++)
    {
        DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
        deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
        deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
        deviceName.header.adapterId = paths[i].targetInfo.adapterId;
        deviceName.header.id = paths[i].targetInfo.id;
        if (DisplayConfigGetDeviceInfo((DISPLAYCONFIG_DEVICE_INFO_HEADER*)&deviceName))
            continue;

        wprintf(L"Monitor Friendly Name : %s\n", deviceName.monitorFriendlyDeviceName);
        wprintf(L"Monitor Device Path   : %s\n", deviceName.monitorDevicePath);

        DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {};
        sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
        sourceName.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME);
        sourceName.header.adapterId = paths[i].targetInfo.adapterId;
        sourceName.header.id = paths[i].sourceInfo.id;
        if (DisplayConfigGetDeviceInfo((DISPLAYCONFIG_DEVICE_INFO_HEADER*)&sourceName))
            continue;

        wprintf(L"GDI Device Name       : %s\n", sourceName.viewGdiDeviceName);

        // find the monitor with this device name
        auto mon = std::find_if(monitors.begin(), monitors.end(), [&sourceName](std::tuple<HMONITOR, std::wstring> t)
        {
            return !std::get<1>(t).compare(sourceName.viewGdiDeviceName);
        });
        wprintf(L"Monitor Handle        : %p\n", std::get<0>(*mon));
        wprintf(L"\n");
    }
    return 0;
}
but it's in C++ I think.

See: https://stackoverflow.com/questions/74603194/is-it-possible-to-get-a-hmonitor-handle-from-a-windows-object-manager-path

Post Reply

Return to “Ask for Help (v2)”