Page 1 of 1

Important tip: getting the desktop resolution

Posted: Sat May 27, 2017 1:48 am
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. ;)

Re: Important tip: getting the desktop resolution

Posted: Mon May 29, 2017 10:56 am
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...

Re: Important tip: getting the desktop resolution

Posted: Mon May 29, 2017 12:21 pm
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...

Re: Important tip: getting the desktop resolution

Posted: Mon May 29, 2017 1:50 pm
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 :-)

Re: Important tip: getting the desktop resolution

Posted: Wed May 31, 2017 12:12 pm
by Dude
[Deleted as was pointless]

Re: Important tip: getting the desktop resolution

Posted: Wed May 31, 2017 1:51 pm
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...

Re: Important tip: getting the desktop resolution

Posted: Tue Jun 13, 2017 1:31 pm
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

Re: Important tip: getting the desktop resolution

Posted: Sun Sep 24, 2017 5:20 am
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

Re: Important tip: getting the desktop resolution

Posted: Sun Sep 24, 2017 5:49 pm
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

Re: Important tip: getting the desktop resolution

Posted: Sun Sep 24, 2017 6:52 pm
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:

Re: Important tip: getting the desktop resolution

Posted: Mon Sep 25, 2017 12:34 am
by IdeasVacuum
Has anyone tried using the Vector Lib for this purpose?

Re: Important tip: getting the desktop resolution

Posted: Thu Sep 28, 2017 12:09 pm
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