Facepunch / garrysmod-requests

Feature requests for Garry's Mod
84 stars 24 forks source link

Add function to get monitor DPI scaling #1048

Open KingofBeast opened 6 years ago

KingofBeast commented 6 years ago

As the number of users with high DPI monitors rises, more and more players will be met with HUDs and UIs that don't fit their screens well. Being able to determine logical pixel density for players will allow developers to intelligently scale menus, fonts, and the like to appropriately present them.

gspetrou commented 6 years ago

Please!

h3xcat commented 6 years ago

Can't you use this? http://wiki.garrysmod.com/page/Global/ScreenScale

robotboy655 commented 6 years ago

No

KingofBeast commented 6 years ago

That only scales based on resolution, not pixel density

h3xcat commented 6 years ago

Ah, you're right.

meepen commented 5 years ago

Any news on this? Would be great.

Tripperful commented 4 years ago

Bump

h3xcat commented 4 years ago

Wrote this proof-of-concept function that correctly retrieves DPI in Windows with the help of SDL. GMod already uses SDL, so it shouldn't be a huge problem to implement it. The function is also cross-compatible.

The issue of using SDL_GetDisplayDPI by itself is that it returns logical DPI on Windows, which is defined by Windows and not the monitor hardware.

Instead of using the function, you can also modify the SDL library itself and change MDT_EFFECTIVE_DPI to MDT_RAW_DPI, which would make the SDL_GetDisplayDPI work properly on Windows.

I have tested SDL_GetDisplayDPI on Linux and it seemed to work properly. I haven't tested this on Mac though.

float GetWindowDisplayDPI(SDL_Window* window) {
#ifdef _WIN32
    typedef enum MONITOR_DPI_TYPE {
        MDT_EFFECTIVE_DPI = 0,
        MDT_ANGULAR_DPI = 1,
        MDT_RAW_DPI = 2,
        MDT_DEFAULT = MDT_EFFECTIVE_DPI
    } MONITOR_DPI_TYPE;

    static HRESULT(WINAPI * GetDpiForMonitor)(HMONITOR hmonitor,
        MONITOR_DPI_TYPE dpiType,
        UINT * dpiX,
        UINT * dpiY) = 0;

    static int initialized = 0;
    if (!initialized) {
        void* shcoreDLL = SDL_LoadObject("SHCORE.DLL");
        if (shcoreDLL) {
            GetDpiForMonitor = (HRESULT(WINAPI*)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*)) SDL_LoadFunction(shcoreDLL, "GetDpiForMonitor");
        }
        else {
            SDL_ClearError();
        }
        initialized = 1;
    }

    float dpi = 0;

    if (GetDpiForMonitor != NULL) {
        SDL_SysWMinfo info; 
        SDL_VERSION(&info.version);
        SDL_GetWindowWMInfo(window, &info);
        HMONITOR hmonitor = MonitorFromWindow(info.info.win.window, MONITOR_DEFAULTTONEAREST);
        UINT dpiX, dpiY;
        GetDpiForMonitor(hmonitor, MDT_RAW_DPI, &dpiX, &dpiY);
        dpi = (float)dpiX;
    } else {
        SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(window), NULL, &dpi, NULL);
    }
    return dpi;
#else
    float dpi = 0;
    SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(window), NULL, &dpi, NULL);
    return dpi;
#endif
}
GrahamBest commented 4 years ago

Wrote this proof-of-concept function that correctly retrieves DPI in Windows with the help of SDL. GMod already uses SDL, so it shouldn't be a huge problem to implement it. The function is also cross-compatible. ...

That's effective, and quality code. Make sure you "unload" the pointer shcoreDLL though. Use SDL_UnloadObject

Could definitely be of use. Great work @h3xcat !

Implementing it should be simple.

h3xcat commented 4 years ago

Make sure you "unload" the pointer shcoreDLL though. Use SDL_UnloadObject

I'm not sure how I would achieve that within the function without reloading the library during each call, probably it's better to handle library loading and unloading outside the function in that case.

GrahamBest commented 4 years ago

Make sure you "unload" the pointer shcoreDLL though. Use SDL_UnloadObject

I'm not sure how I would achieve that within the function without reloading the library during each call, probably it's better to handle library loading and unloading outside the function in that case.

That could work. Or make it a const static void pointer then unload it at the end. So it retains its value each time its called.