Page 2 of 2

Re: How to handle Windows UI scaling?

Posted: Sun Apr 12, 2015 1:29 pm
by Danilo

Code: Select all

;
; http://en.wikipedia.org/wiki/Dots_per_inch
; http://en.wikipedia.org/wiki/Dots_per_inch#Computer_monitor_DPI_standards
; http://en.wikipedia.org/wiki/Pixel_density
; http://en.wikipedia.org/wiki/Dot_pitch
; http://en.wikipedia.org/wiki/Pixel_geometry
;
; http://en.wikipedia.org/wiki/Resolution_independence
;
; Writing DPI-Aware Desktop and Win32 Applications
; - https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx
; How to Write High-DPI Applications (MSDN, 2001)
; - https://msdn.microsoft.com/en-us/library/ms969894.aspx
; Designing for Varying Display Sizes
; - https://msdn.microsoft.com/en-us/library/ms695587(v=vs.85).aspx
; Resolving High-DPI Scaling Issues for Windows Apps
; - https://software.intel.com/en-us/blogs/2014/04/14/dpi-scaling-issues
;
; Follow-up on High DPI resolution
; - http://blogs.msdn.com/b/e7/archive/2008/09/13/follow-up-on-high-dpi-resolution.aspx
; More Follow up to discussion about High DPI
; - http://blogs.msdn.com/b/e7/archive/2008/09/16/more-follow-up-to-discussion-about-high-dpi.aspx
;
Prototype protoGetDpiForMonitor(hmonitor, dpiType.l, *dpiX.Long, *dpiY.Long)
Global GetDpiForMonitor.protoGetDpiForMonitor

libShcore = OpenLibrary(#PB_Any, "Shcore.dll")
If libShcore
    ; https://msdn.microsoft.com/en-us/library/dn280510(v=vs.85).aspx
    GetDpiForMonitor = GetFunction(libShcore,"GetDpiForMonitor") ; Win8.1+
EndIf

Enumeration MONITOR_DPI_TYPE
  #MDT_Effective_DPI  = 0
  #MDT_Angular_DPI    = 1
  #MDT_Raw_DPI        = 2
  #MDT_Default        = #MDT_Effective_DPI
EndEnumeration

Structure MONITORINFOEX_ Align #PB_Structure_AlignC
    cbSize.l
    rcMonitor.RECT
    rcWork.RECT
    dwFlags.l
    szDevice.c[#CCHDEVICENAME]
EndStructure

Procedure MonitorEnumProc(hMonitor, hdcMonitor, *lprcMonitor.RECT, dwData.l)
    Protected dpiX.l, dpiY.l, mi.MONITORINFOEX_, deviceName.s
    Protected dotPitchX.d, dotPitchY.d, monitorHeight.d, monitorWidth.d, monitorDiagonal.d, monitorDiagonalInch.d
    Debug "Monitor Info: "
    Debug "  hMonitor: "+hMonitor
    mi\cbSize = SizeOf( MONITORINFOEX_ )
    GetMonitorInfo_(hMonitor,@mi)
    deviceName = PeekS(@mi\szDevice, #CCHDEVICENAME)
    Debug "  Primary Monitor: "+#TAB$+Bool(mi\dwFlags & #MONITORINFOF_PRIMARY)
    Debug "  Monitor Rect:    "+#TAB$+mi\rcMonitor\left + "," + mi\rcMonitor\top + "," + mi\rcMonitor\right + "," + mi\rcMonitor\bottom
    Debug "  Workarea Rect:   "+#TAB$+mi\rcWork\left + "," + mi\rcWork\top + "," + mi\rcWork\right + "," + mi\rcWork\bottom
    Debug "  Device Name:     "+#TAB$+deviceName
    If GetDpiForMonitor
        GetDpiForMonitor(hmonitor, #MDT_Effective_DPI, @dpiX, @dpiY)
        Debug "  Effective DPI: "+#TAB$+dpiX+"x"+dpiY
        GetDpiForMonitor(hmonitor, #MDT_Angular_DPI  , @dpiX, @dpiY)
        Debug "  Angular DPI:   "+#TAB$+dpiX+"x"+dpiY
        GetDpiForMonitor(hmonitor, #MDT_Raw_DPI, @dpiX, @dpiY)
        Debug "  Raw DPI:       "+#TAB$+dpiX+"x"+dpiY
        dotPitchX = 25.4/dpiX
        Debug "  DotPitchX:     "+#TAB$+StrD(dotPitchX,3)+" mm"
        dotPitchY = 25.4/dpiY
        Debug "  DotPitchY:     "+#TAB$+StrD(dotPitchY,3)+" mm"
        ;monitorWidth = dotPitchX * Abs(mi\rcMonitor\left + mi\rcMonitor\right)
        ;Debug "  Monitor Width: "+#TAB$+StrD(monitorWidth,1)+" mm"
        ;monitorHeight = dotPitchY * Abs(mi\rcMonitor\top + mi\rcMonitor\bottom)
        ;Debug "  Monitor Height: "+#TAB$+StrD(monitorHeight,1)+" mm"
        ;monitorDiagonal = Sqr( Pow(monitorWidth,2) + Pow(monitorHeight,2) )
        ;monitorDiagonalInch = monitorDiagonal / 25.4
        ;Debug "  Monitor Diagonal: "+#TAB$+StrD(monitorDiagonal,1)+" mm ("+StrD(monitorDiagonalInch,1)+#DQUOTE$+")"
        ;Debug "  Virtual PPI:      "+#TAB$+Str( Sqr( Pow(Abs(mi\rcMonitor\left + mi\rcMonitor\right),2) + Pow(Abs(mi\rcMonitor\top + mi\rcMonitor\bottom),2) ) / monitorDiagonalInch )
    EndIf
    ProcedureReturn #True
EndProcedure

EnumDisplayMonitors_(0,0,@MonitorEnumProc(),0)
Example output on Windows 8.1:

Code: Select all

Monitor Info: 
  hMonitor: 131075
  Primary Monitor:  1
  Monitor Rect:     0,0,2560,1440
  Workarea Rect:    0,0,2467,1406
  Device Name:      \\.\DISPLAY1
  Effective DPI: 	120x120
  Angular DPI:   	125x111
  Raw DPI:       	125x112
  DotPitchX:     	0.203 mm
  DotPitchY:     	0.227 mm
Monitor Info: 
  hMonitor: 131073
  Primary Monitor:  0
  Monitor Rect:     -2560,0,0,1440
  Workarea Rect:    -2560,0,0,1406
  Device Name:      \\.\DISPLAY2
  Effective DPI: 	120x120
  Angular DPI:   	125x111
  Raw DPI:       	125x112
  DotPitchX:     	0.203 mm
  DotPitchY:     	0.227 mm
Raw DPI on X & Y are different (AFAIK for most displays), meaning dot pitch is different. Windows calculates rounded PPI from it (Effective DPI).

Re: How to handle Windows UI scaling?

Posted: Sun Apr 12, 2015 4:35 pm
by Justin
If you download this sample from msdn it only uses the xfactor:
https://msdn.microsoft.com/en-us/librar ... S.85).aspx

InitInstance() gets the dpi

In linux there is gdk_screen_get_resolution() that returns a single dpi value (only works in GTK3)

What is the api in MacOS so we can have a true crossplatform dpi aware?

Re: How to handle Windows UI scaling?

Posted: Mon Apr 13, 2015 3:50 pm
by Little John
Danilo, once again very comprehensive and useful information from you. :-)

Thank you very much!

Re: How to handle Windows UI scaling?

Posted: Tue Apr 14, 2015 7:34 am
by Thunder93
I've updated my previous Windows-specific code (http://www.purebasic.fr/english/viewtop ... 99#p463799) to include the missing ComboBoxGadget() support, which is used in the included Gadget Demonstration example code. I've also included ListIconGadget() Macro addition, and cleaned up the ProcedureReturns of some unnecessaries. __ Just demonstration, not all the PB gadgets are covered / included in this.

When using, it's good idea to store the original Windows widths and heights into variables, and for Gadgets that are depended on for widths and heights. If you going to make gadget to almost width of the opened window, and using WindowWidth([WindowID]). It'll return now the scaled width of the Window. Then creating new gadget, which is set to automatically scale the parameters. The WindowWidth() is now scaled x2. Double scaling, and causing problems. __ I've added two additional pDy(), pDx() Macros to retrieve the original / non-scaled widths and heights.