Important tip: getting the desktop resolution

Share your advanced PureBasic knowledge/code with the community.
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Important tip: getting the desktop resolution

Post by Dude »

Just discovered this weird issue (bug?) with the GetSystemMetrics_() API on Windows.

My PC's desktop is 1280 x 1024 pixels. I normally use GetSystemMetrics_() to get the width and height of it, and it normally correctly returns 1280 and 1024 accordingly; however today I switched to Large fonts (150%) and noted the results were now incorrect. :( So a tip / heads-up to anyone using GetSystemMetrics_(): drop it!

[Edit] I advocated using ExamineDesktops() instead, but discovered today for myself that it's totally unreliable; so see my new post further below which works reliably instead. For me, anyway. ;)
Last edited by Dude on Wed May 31, 2017 12:14 pm, edited 2 times in total.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6161
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: Important tip: getting the desktop resolution

Post by blueznl »

Yup. But even then output isn't always consistent.

http://www.ninelizards.com/purebasic/pu ... 17.htm#top

Once you start messing with DPI settings even DesktopWidth() can give you the wrong number...
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Important tip: getting the desktop resolution

Post by Dude »

blueznl wrote:Once you start messing with DPI settings even DesktopWidth() can give you the wrong number...
So how can we 100% reliably get the desktop size? Take a screenshot and check the image w/h? That works, but it's damn overkill...
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6161
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: Important tip: getting the desktop resolution

Post by blueznl »

Dude wrote:
blueznl wrote:Once you start messing with DPI settings even DesktopWidth() can give you the wrong number...
So how can we 100% reliably get the desktop size? Take a screenshot and check the image w/h? That works, but it's damn overkill...
Dunno :-)

There's probably a good way to do it, but if there is not one way would be to create an invisible window and make it maximised, then get its size, that would probably work regardless of settings.

You'd still have to accommodate for font size versus DPI issues, I guess. It's a mess, this whole DPI thing on Windows. I haven't worked out the changes in Windows 10 yet, have been very busy with all sorts of (shock!) non-purebasic things :-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Important tip: getting the desktop resolution

Post by Dude »

[Deleted as was pointless]
Last edited by Dude on Sun Sep 24, 2017 8:01 am, edited 1 time in total.
User avatar
chi
Addict
Addict
Posts: 1028
Joined: Sat May 05, 2007 5:31 pm
Location: Linz, Austria

Re: Important tip: getting the desktop resolution

Post by chi »

I normally use GetSystemMetrics_() to get the width and height of it, and it normally correctly returns 1280 and 1024 accordingly; however today I switched to Large fonts (150%) and noted the results were now incorrect.
The important thing here is, you MUST build your program DPI-Aware! GetSystemMetrics_(), GetDeviceCaps_(), ... return wrong values if you don't.

Just check following sample and comment/uncomment the OpenLibrary part:

Code: Select all

Global txt$

Procedure _IsProcessDPIAware() : EndProcedure
Prototype _IsProcessDPIAware()

Procedure _SetProcessDPIAware() : EndProcedure
Prototype _SetProcessDPIAware()

Define user32 = OpenLibrary(#PB_Any, "user32.dll")
If user32
  Define IsProcessDPIAware__._IsProcessDPIAware = GetFunction(user32, "IsProcessDPIAware")
  If IsProcessDPIAware__ = 0 : IsProcessDPIAware__ = @_IsProcessDPIAware() : EndIf
  
  Define SetProcessDPIAware__._SetProcessDPIAware = GetFunction(user32, "SetProcessDPIAware")
  If SetProcessDPIAware__ = 0 : SetProcessDPIAware__ = @_SetProcessDPIAware() : EndIf
  
  If IsProcessDPIAware__() = #False
    SetProcessDPIAware__()
  EndIf
  
  CloseLibrary(user32)
EndIf


Procedure MonitorEnumProc(hMonitor, hdcMonitor, lprcMonitor, dwData)
  
  ;Protected mi.MONITORINFOEX\cbSize = SizeOf(MONITORINFOEX)
  ;GetMonitorInfo_(hMonitor, mi) 
  ;Debug PeekS(@mi\szDevice)    
  
  Protected *monRect.RECT = lprcMonitor
  txt$ +  "hMon: " + Chr(9) + Str(hMonitor) +Chr(10)
  txt$ +  "left: " + Chr(9) + *monRect\left +Chr(10)
  txt$ +  "top: " + Chr(9) + *monRect\top +Chr(10)
  txt$ +  "right: " + Chr(9) + *monRect\right +Chr(10)
  txt$ +  "bottom: " + Chr(9) + *monRect\bottom +Chr(10) +Chr(10)
  
  ProcedureReturn #True
EndProcedure

;Debug MonitorFromWindow_(66560, #MONITOR_DEFAULTTONEAREST)

txt$ + "DPI: " + Str(GetDeviceCaps_(GetDC_(0), #LOGPIXELSX)) +Chr(10) +Chr(10)

txt$ +  "SystemMetrics W: " + Chr(9) + GetSystemMetrics_(0) +Chr(10)
txt$ +  "SystemMetrics H: " + Chr(9) + GetSystemMetrics_(1) +Chr(10) +Chr(10)

ExamineDesktops()
txt$ +  "DesktopWidth: " + Chr(9) + DesktopWidth(0) +Chr(10)
txt$ +  "DesktopHeight: " + Chr(9) + DesktopHeight(0) +Chr(10) +Chr(10)

EnumDisplayMonitors_(#Null, #Null, @MonitorEnumProc(), 0)

MessageRequester("", txt$)
I still don't know why DPI-awareness isn't handled automatically by PB...
Et cetera is my worst enemy
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Important tip: getting the desktop resolution

Post by Dude »

Dude wrote:The error said something like "ExamineDesktops() must be called before DesktopWidth()" or such... which is weird because I did it already. :shock:
Here's my bug report about this, and screenshot as proof:

http://www.purebasic.fr/english/viewtop ... =4&t=68632
mestnyi
Addict
Addict
Posts: 995
Joined: Mon Nov 25, 2013 6:41 am

Re: Important tip: getting the desktop resolution

Post by mestnyi »

Do you have the same behavior in ekzaminedesktop?
Returns the width and height is not correct.
if you uncomment the lines then it starts to return correctly.

Code: Select all

; ; Uncomment ExamineDesktops work very good
; Debug "DPI: " + Str(GetDeviceCaps_(GetDC_(#Null), #LOGPIXELSX)) ; 96
; Procedure _IsProcessDPIAware() : EndProcedure
; Prototype _IsProcessDPIAware()
; 
; Procedure _SetProcessDPIAware() : EndProcedure
; Prototype _SetProcessDPIAware()
; 
; Define user32 = OpenLibrary(#PB_Any, "user32.dll")
; If user32
;   Define IsProcessDPIAware__._IsProcessDPIAware = GetFunction(user32, "IsProcessDPIAware")
;   If IsProcessDPIAware__ = 0 : IsProcessDPIAware__ = @_IsProcessDPIAware() : EndIf
;   
;   Define SetProcessDPIAware__._SetProcessDPIAware = GetFunction(user32, "SetProcessDPIAware")
;   If SetProcessDPIAware__ = 0 : SetProcessDPIAware__ = @_SetProcessDPIAware() : EndIf
;   
;   If IsProcessDPIAware__() = #False
;     SetProcessDPIAware__()
;   EndIf
;   
;   CloseLibrary(user32)
; EndIf


Debug "DPI: " + Str(GetDeviceCaps_(GetDC_(#Null), #LOGPIXELSX)) ; 96 if uncomment 120
;// Для получения разрешения экрана  напишите следующий код:
hDCScreen =  GetDC_(#Null);
Horres.i = GetDeviceCaps_(hDCScreen,  #HORZRES);
Vertres.i = GetDeviceCaps_(hDCScreen,  #VERTRES);
ReleaseDC_(#Null, hDCScreen);
;// в Horres  содержится разрешение экрана по горизонтали,
;// в Vertres содержится  разрешение экрана по вертикали.

If OpenWindow(0,0,0,500,250,"Window 0")
ButtonGadget(-1, 10,10,80,30,"Button")
   ResizeWindow(0, (Horres-WindowWidth(0, #PB_Window_FrameCoordinate))/2, (Vertres-WindowHeight(0, #PB_Window_FrameCoordinate))/2, #PB_Ignore, #PB_Ignore)
EndIf

Horres.i = GetSystemMetrics_(#SM_CXSCREEN)
Vertres.i = GetSystemMetrics_(#SM_CYSCREEN)
If OpenWindow(20,0,0,500,250,"Window 20")
ButtonGadget(-1, 10,10,80,30,"Button")
   ResizeWindow(20, (Horres-WindowWidth(20, #PB_Window_FrameCoordinate))/2, (Vertres-WindowHeight(20, #PB_Window_FrameCoordinate))/2, #PB_Ignore, #PB_Ignore)
EndIf

ExamineDesktops()
If OpenWindow(10,0,0,500,250,"Window 10")
  ButtonGadget(-1, 10,10,80,30,"Button")
 ResizeWindow(10, (DesktopWidth(0)-WindowWidth(10, #PB_Window_FrameCoordinate))/2, (DesktopHeight(0)-WindowHeight(10, #PB_Window_FrameCoordinate))/2, #PB_Ignore, #PB_Ignore)
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Last edited by mestnyi on Sun Sep 24, 2017 6:48 pm, edited 1 time in total.
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: Important tip: getting the desktop resolution

Post by Thunder93 »

PureBasic language doesn't do 'natively' DPI-aware applications. chi called it!
Really no reason why PB shouldn't now be supporting it natively.

I've personally observed visual issues making applications with PureBasic which aren't DPI-Aware. To quote from Microsoft page.

"Applications that are not DPI–aware but are running on a high-DPI display setting can suffer from many visual artifacts, including incorrect scaling of UI elements, clipped text, and blurry images. By adding support in your application for DPI awareness, you ensure that the presentation of your application's UI is more predictable, making it more visually appealing to users. This gives the user the best possible experience on any display."


GetSystemMetrics_(), GetDeviceCaps_()... is returning logical vs. physical pixels. And there's no bug with PureBasic or Windows APIs. If your desktop resolution is 1280 x 1024 and your DPI is set to 150%, 1280 would now be 853 and 1024 would now be 682 for Not DPI-Aware application. 853 x 682 is roughly your return using GetSystemMetrics?

If you making system info utilities, then getting the physical information would be important. However you trying to make windows and controls then It is easier to work with the logical rather than the physical returns while making not DPI-Aware PB applications. As anyone can clearly see that PureBasic natively included commands such as DesktopWidth() reports physical and using that in Not DPI-Aware code is mind baffling to people. The help file isn't exactly any help to avoid confusion that is repeatedly shown again and again.

Using DesktopWidth() return to make PB Window with maximum desktop width is no help, and no way to calculate properly to my knowledge without also being able to grab the DPI information. I don't believe PB offers such support yet? Therefore OS specific API calls to determine the DPI and do the calculations.

Code: Select all

Procedure CreateWindow(WinID, x, y, iw, ih, backcolor)
  OpenWindow(WinID, x, y, iw, ih, "", #PB_Window_BorderLess)
  SetWindowColor(WinID, backcolor)
EndProcedure

Result = ExamineDesktops()
DesktopWidth = DesktopWidth(0)
DesktopHeight = DesktopHeight(0)
DesktopWidth2 = GetSystemMetrics_(#SM_CXSCREEN)
DesktopHeight2 = GetSystemMetrics_(#SM_CYSCREEN)

Debug "PB_Native: Desktop: "+ Str(DesktopWidth) + "x" + Str(DesktopHeight)
Debug "Win_API: Desktop: "+ Str(DesktopWidth2) + "x" + Str(DesktopHeight2)

; Top left
CreateWindow(0, 0, 0, 30, 30, RGB(255,0,0))
CreateWindow(1, 0, 40, 30, 30, RGB(0, 92, 253))

; Top right
CreateWindow(2, DesktopWidth-30, 0, 30, 30, RGB(255,0,0))
CreateWindow(3, DesktopWidth2-30, 40, 30, 30, RGB(0, 92, 253))

Debug #LF$+"Red blocks to demonstrate using PB Native DesktopWidth() command."
Debug #LF$+"Blue blocks to demonstrate using Windows GetSystemMetrics_()"
Debug #LF$+"... Goal is to have the blocks attached to the top-left & top-right edge of the screen,"+#LF$+
      "With the exception that the blue blocks are lowered by design."

Repeat
  Event = WindowEvent()  
Until Event = #PB_Event_CloseWindow
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
mestnyi
Addict
Addict
Posts: 995
Joined: Mon Nov 25, 2013 6:41 am

Re: Important tip: getting the desktop resolution

Post by mestnyi »

I do not know what to happen, but I have an api to determine the wines correctly and there is no purebasik. DesktopWidth (0) then at least must have a flag. :cry:
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Important tip: getting the desktop resolution

Post by IdeasVacuum »

Has anyone tried using the Vector Lib for this purpose?
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
TeddyLM
Enthusiast
Enthusiast
Posts: 133
Joined: Wed Apr 30, 2003 2:04 pm
Location: Germany (French expat)

Re: Important tip: getting the desktop resolution

Post by TeddyLM »

For the function GetDeviceCaps_() not all the possible values appear in the documentation.
You can use #DESKTOPHORZRES and #DESKTOPVERTRES (instead of #HORZRES and #VERTRES) to retrieve the width and height of the Screen.
Try this code with scaling >100%

Code: Select all

#DESKTOPVERTRES = $75
#DESKTOPHORZRES = $76
hDCScreen.l = GetDC_(#Null)
 ;Returns the scaled size 
Debug GetDeviceCaps_(hDCScreen, #HORZRES)  
Debug GetDeviceCaps_(hDCScreen, #VERTRES)    
;Returns the physical size
Debug GetDeviceCaps_(hDCScreen, #DESKTOPHORZRES)  
Debug GetDeviceCaps_(hDCScreen, #DESKTOPVERTRES))  
ReleaseDC_(#Null, hDCScreen)
Hope this helps

Tested on Windows 10 x64 only
TeddyLM
Post Reply