4

I am developing a multi-platform game that runs on iOS as well as desktops (Windows, Mac, Linux). I want the game to be able to resize certain UI elements depending on the resolution of the screen in inches. The idea is that if a button should be, say, around 1/2 inch across in any interface, it will be scaled automatically that size.

Now for iOS devices this problem is reasonably well solvable using brute force techniques. You can look up the type of the device and use a hard-coded table to determine the screen size in inches for each device. Not the most elegant solution, but sufficient.

Desktops are the tricky ones. What I wish and hope exists is a mechanism by which (some?) monitors report to operating systems their actual screen size in inches. If that mechanism exists and I can access it somehow, I can get good numbers at least for some monitors. But I've never come across any such concept in any of the major OS APIs.

Is there a way to ask for the screen size in inches in Win32? If so, are there monitors that actually provide this information?

(And if the answer is no: Gosh, doesn't this seem awfully useful?)

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
OldPeculier
  • 11,049
  • 13
  • 50
  • 76
  • Seems like this might be best separated out by platform, since there will be a different correct answer for each one. You already have an answer for Windows, so I suggest making this your Windows question and posting another one for OS X. (I have an answer for you, and I'll post it on that question.) – Peter Hosey Jul 20 '13 at 19:53
  • 1

2 Answers2

3

For Windows, first see SetProcessDPIAware() for a discussion on turning off automatic scaling, and then call GetDeviceCaps( LOGPIXELSX ) and GetDeviceCaps( LOGPIXELSY ) on your HDC to determine the monitor's DPI. Divide the screen resolution on your active monitor by those settings and you've got the size.

Also see this article for a similar discussion on DPI aware apps.

HerrJoebob
  • 2,264
  • 16
  • 25
  • 3
    Is there some reason to avoid `GetDeviceCaps(VERTSIZE)` and `GetDeviceCaps(HORZSIZE)`? Unless there's some problem with them, they seem to provide the desired information much more directly. – Jerry Coffin Jul 21 '13 at 05:50
  • 1
    @JerryCoffin - Apparently there's some question on the reliability of those, at least on Windows 7. See http://social.msdn.microsoft.com/Forums/vstudio/en-US/6f06fa5e-1626-4668-b0ee-1f0d07e8d175/getdevicecapshorzsize-is-incorrect-in-windows-7 – HerrJoebob Jul 22 '13 at 17:01
  • sadly VERTSIZE and HORZSIzZE produce incorrect measurement as far as my tape measure can tell. My smaller screen labtop had more millimeters than my larger desktop monitor. How then can you print accurately? There is a book called Small Windows which covered this topic. – user13947194 Sep 18 '22 at 18:40
  • @user13947194 sorry, looks like you're wrong. You may have missed the call to _SetProcessDPIAware()_ (literally in the first line of my answer) before relying on logical pixels. – HerrJoebob Sep 19 '22 at 19:55
  • Infact I did notice. I didn't get the function to use as it is not available on winxp. And using DPI in the manifest didn't run on win 7. The problem is that, as far as I understand, DPI is just a number you set. At any time user can choose a higher or lower dpi; which will result in Dpi aware applications having to draw a bigger or smaller image. Nothing to do with the actuall size of your screen. – user13947194 Sep 28 '22 at 01:14
  • I had a look at SetMapMode(HDC, MM_LOMETRIC); which promised to map logical units to 0.1 millimeters. The results I get are very inaccurate. As luck would have it, the only way to get accurate result is to know your screen size and divide it by the screen resolution. And only HwInfo app has the way to get the screen size from the system. – user13947194 Sep 28 '22 at 01:21
-1

Here is a method I found at the web address "https://ofekshilon.com/2011/11/13/reading-monitor-physical-dimensions-or-getting-the-edid-the-right-way/".

Authour says that measurement is in millimeters.

Does not give you the precise width and height but better approximation than HORSIZE and VERTSIZE. In which I tried on two different monitors and got a max difference of 38 cm (measured screen size - calculated screen size).

#include <SetupApi.h>
#pragma comment(lib, "setupapi.lib")

#define NAME_SIZE 128

const GUID GUID_CLASS_MONITOR = {0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18};

// Assumes hDevRegKey is valid
bool GetMonitorSizeFromEDID(const HKEY hDevRegKey, short& WidthMm, short& HeightMm)
{
    DWORD dwType, AcutalValueNameLength = NAME_SIZE;
    TCHAR valueName[NAME_SIZE];

    BYTE EDIDdata[1024];
    DWORD edidsize=sizeof(EDIDdata);

    for (LONG i = 0, retValue = ERROR_SUCCESS; retValue != ERROR_NO_MORE_ITEMS; ++i)
    {
        retValue = RegEnumValueA ( hDevRegKey, i, &valueName[0],
            &AcutalValueNameLength, NULL, &dwType,
            EDIDdata, // buffer
            &edidsize); // buffer size

        if (retValue != ERROR_SUCCESS || 0 != strcmp(valueName,"EDID"))
            continue;

        WidthMm  = ((EDIDdata[68] & 0xF0) << 4) + EDIDdata[66];
        HeightMm = ((EDIDdata[68] & 0x0F) << 8) + EDIDdata[67];
        return true; // valid EDID found
    }

    return false; // EDID not found
}

// strange! Authour requires TargetDevID argument but does not use it
bool GetSizeForDevID(const char *TargetDevID, short& WidthMm, short& HeightMm)
{
    HDEVINFO devInfo = SetupDiGetClassDevsExA(
                &GUID_CLASS_MONITOR, //class GUID
                NULL, //enumerator
                NULL, //HWND
                DIGCF_PRESENT, // Flags //DIGCF_ALLCLASSES|
                NULL, // device info, create a new one.
                NULL, // machine name, local machine
                NULL);// reserved

    if (NULL == devInfo)  return false;

    bool bRes = false;
    for (ULONG i=0; ERROR_NO_MORE_ITEMS != GetLastError(); ++i)
    {
        SP_DEVINFO_DATA devInfoData;
        memset(&devInfoData,0,sizeof(devInfoData));
        devInfoData.cbSize = sizeof(devInfoData);

        if (SetupDiEnumDeviceInfo(devInfo,i,&devInfoData))
        {
            HKEY hDevRegKey = SetupDiOpenDevRegKey(devInfo,&devInfoData,DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
            if(!hDevRegKey || (hDevRegKey == INVALID_HANDLE_VALUE)) continue;

            bRes = GetMonitorSizeFromEDID(hDevRegKey, WidthMm, HeightMm);
            RegCloseKey(hDevRegKey);
        }
    }

    SetupDiDestroyDeviceInfoList(devInfo);
    return bRes;
}

int main(int argc, CHAR* argv[])
{
    short WidthMm, HeightMm;

    DISPLAY_DEVICE dd;
    dd.cb = sizeof(dd);

    DWORD dev = 0; // device index
    int id = 1; // monitor number, as used by Display Properties > Settings

    char DeviceID[1024];
    bool bFoundDevice = false;

    while (EnumDisplayDevices(0, dev, &dd, 0) && !bFoundDevice)
    {
        DISPLAY_DEVICE ddMon = {sizeof(ddMon)};
        DWORD devMon = 0;

        while (EnumDisplayDevices(dd.DeviceName, devMon, &ddMon, 0) && !bFoundDevice)
        {
            if (ddMon.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP &&
                !(ddMon.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
            {
                sprintf(DeviceID,"%s", ddMon.DeviceID+8);

                for(auto it=DeviceID; *it; ++it)
                if(*it == '\\') { *it = 0; break; }
             
                bFoundDevice = GetSizeForDevID(DeviceID, WidthMm, HeightMm);
            }
            devMon++;

            ZeroMemory(&ddMon, sizeof(ddMon));
            ddMon.cb = sizeof(ddMon);
        }

        ZeroMemory(&dd, sizeof(dd));
        dd.cb = sizeof(dd);
        dev++;
    }
    
    return 0;
}
user13947194
  • 337
  • 5
  • 7