PureBasic Forum
https://www.purebasic.fr/english/

Important tip: getting the desktop resolution
https://www.purebasic.fr/english/viewtopic.php?f=12&t=68542
Page 1 of 1

Author:  Dude [ Sat May 27, 2017 1:48 am ]
Post subject:  Important tip: getting the desktop resolution

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. ;)

Author:  blueznl [ Mon May 29, 2017 10:56 am ]
Post subject:  Re: Important tip: getting the desktop resolution

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...

Author:  Dude [ Mon May 29, 2017 12:21 pm ]
Post subject:  Re: Important tip: getting the desktop resolution

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...

Author:  blueznl [ Mon May 29, 2017 1:50 pm ]
Post subject:  Re: Important tip: getting the desktop resolution

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 :-)

Author:  Dude [ Wed May 31, 2017 12:12 pm ]
Post subject:  Re: Important tip: getting the desktop resolution

[Deleted as was pointless]

Author:  chi [ Wed May 31, 2017 1:51 pm ]
Post subject:  Re: Important tip: getting the desktop resolution

Quote:
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:
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...

Author:  Dude [ Tue Jun 13, 2017 1:31 pm ]
Post subject:  Re: Important tip: getting the desktop resolution

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:

viewtopic.php?f=4&t=68632

Author:  mestnyi [ Sun Sep 24, 2017 5:20 am ]
Post subject:  Re: Important tip: getting the desktop resolution

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:
; ; 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

Author:  Thunder93 [ Sun Sep 24, 2017 5:49 pm ]
Post subject:  Re: Important tip: getting the desktop resolution

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:
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

Author:  mestnyi [ Sun Sep 24, 2017 6:52 pm ]
Post subject:  Re: Important tip: getting the desktop resolution

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:

Author:  IdeasVacuum [ Mon Sep 25, 2017 12:34 am ]
Post subject:  Re: Important tip: getting the desktop resolution

Has anyone tried using the Vector Lib for this purpose?

Author:  TeddyLM [ Thu Sep 28, 2017 12:09 pm ]
Post subject:  Re: Important tip: getting the desktop resolution

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:
#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

Page 1 of 1 All times are UTC + 1 hour
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/