How I make DPI-aware apps

Share your advanced PureBasic knowledge/code with the community.
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: How I make DPI-aware apps

Post by mk-soft »

I just tested it with the module ScaleGadget.

Adapt the windows and gadgets quite well with the DPI-Aware option.
Also the image will be adjusted with the modules :wink:

Code: Select all

;-TOP

IncludeFile "Modul_ScaleGadgets.pb"

UseModule ScaleGadgets

SetScale(DesktopResolutionX())

...
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: How I make DPI-aware apps

Post by Little John »

chi wrote:
Little John wrote:Well ... what is the advantage of that code?
DPI-Awareness is only available on Windows right now... So this is a multi-platform approach ;)
As I just showed in my previous post, that "multiplatform approach" does not work on Windows with PB versions prior to 5.70. And I suppose nobody actually has tested it on the two other platforms ...

PS:
I have tested the code in my previous code now with PB 5.70 on Linux Mint Cinnamon 19.1. Regardless of the DPI setting, the result always is
1.0
1.0
This is not actiually surprising, since on Linux (as well as on Mac OS) PureBasic is not aware of DPI settings.

I wonder why some people do not even test their code before posting it in public ...
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: How I make DPI-aware apps

Post by Dude »

chi wrote:ppi = 96
You can't hard-code a value of PPI like that... it changes depending on your PC's setup! :shock: That's why I calculate it at runtime with GetDeviceCaps_() in my first post of this thread.
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: How I make DPI-aware apps

Post by Dude »

Just saw this interesting post by Danilo in 2015 about DPI scaling (with screenshots):

viewtopic.php?p=462177#p462177

Is that all we need to do, then? :shock:
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 664
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Re: How I make DPI-aware apps

Post by Kurzer »

bbanelli wrote:...
I think there's more to it. Process Explorer states application as DPI unaware, primarily because you didn't use SetProcessDpiAwareness().
Excuse the short offtopic, but I can't enable this option in the process explorer at all.
I have Windows 7 x64 running here. Is this the reason that this column option is disabled in process explorer?

Image
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2023: 56y
"Happiness is a pet." | "Never run a changing system!"
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How I make DPI-aware apps

Post by srod »

Dude wrote:
chi wrote:ppi = 96
You can't hard-code a value of PPI like that... it changes depending on your PC's setup! :shock: That's why I calculate it at runtime with GetDeviceCaps_() in my first post of this thread.
But in effect that is what you have done because of your use of the following :

Code: Select all

x=10*dpi
w=155*dpi
h=20*dpi
and the values 10, 155, 20 which you hard-coded for a certain DPI value (your code suggests 100dpi, but I suspect you actually used 96DPI as the 'base').

If you are going to tackle DPI by scaling a user-interface then the scaling has to refer to some initial 'baseline measure', whether this is an assumed initial 96 DPI setting or otherwise. It matters not because it all boils down to the same thing.
Even Danilo's code does this by comparing the actual DPI with a base value of 96 DPI which he then uses to scale his UI elements based on gadget sizes initially determined under a 96 DPI setting.
An alternative way would be to simply pull out the current DPI settings and position UI elements based upon text/font metrics etc, but this would probably not be very satisfactory when then dragging between monitors with differing DPIs and the like. Scaling is undoubtedly the best way.
In your case, you need to ensure that Windows does not assume your app is completely DPI unware as it will then (unless DPI virtualisation is disabled) scale the UI itself via one large bitmap whenever the DPI changes from an assumed default of 96 as this is all your app will see (a 96 DPI). Either set the DPIAware option or use the SetProcessDPIAwareness() API (Win 8.1+) or a manifest entry. Mind you, with the DPIAware option you have the readily available scale factors anyhow via DesktopResolutionY() etc.
I may look like a mule, but I'm not a complete ass.
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: How I make DPI-aware apps

Post by Dude »

srod wrote:your code suggests 100dpi, but I suspect you actually used 96DPI as the 'base'
The "/100" in my tip does not refer to "100 DPI", but rather "/100%" of DPI; so my gadget scalings are based on a percentage of the current system DPI setting.

Also, just to clarify something: when I use exclamation marks ("it changes depending on your PC's setup!") I'm not yelling or angry; I'm just expressing shock. :)
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How I make DPI-aware apps

Post by srod »

Either way it amounts to the same thing, although, honestly, the use of 100 is flawed unless the sizes of 10, 155, 20 were obtained from a system running at 100 DPI. The likely fact is that you would have originally been running at 96 DPI and so you wouldn't notice any difference.

'100% of DPI' is meaningless because these values of 10, 155, 20 were only relevant on your initial setup (96 DPI). Had you originally calculated these values on a system running at 300 DPI (say) then this snippet of yours would produce erroneous results even at 300 DPI since you would then be calculating a scale factor of 3! In such a case you would need to use :

Code: Select all

Global dpi.d=GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)/300
So the important figure is 96, not 100!

I am using a similar approach myself, but like Danillo's code I am comparing with an original setting of 96 DPI as I have set everything out on a system running at this DPI. I even switched across to Ubuntu and the UI still scaled correctly - which surprised me a little.

I am using something similar to :

Code: Select all

DPIX.d = 1.0
DPIY.d = 1.0
If StartVectorDrawing(CanvasVectorOutput(#Canvas))
  DPIY = VectorResolutionY() / 96
  DPIX = VectorResolutionX() / 96
  StopVectorDrawing()
EndIf
But this will only have any impact if DPI virtualization is disabled for the program in question or dpiawareness is set.
I may look like a mule, but I'm not a complete ass.
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: How I make DPI-aware apps

Post by Dude »

srod wrote:Had you originally calculated these values on a system running at 300 DPI (say) then this snippet of yours would produce erroneous results even at 300 DPI since you would then be calculating a scale factor of 3!
True, but since I code on the standard default minimum DPI size (96 DPI), then my tip will always work because it can only ever scale up and not down, ie. no matter what DPI the user has set their PC too. You can't set a Windows PC DPI to be less than 96 DPI at all, so there's no problem (unless I've overlooked something).
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How I make DPI-aware apps

Post by srod »

Yes the tip in itself is fine - but the value of 100 is not! :)

For anyone who is laying out a UI which will automatically rescale for use on other systems then they need to take into account the DPI in force at the time they designed the UI. So, if they laid out the UI under a 300 DPI setting then they need :

Code: Select all

Global dpi.d=GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)/300
There is then no problem running this program on other systems (in theory at least) without any changes providing the /300 stays.
I may look like a mule, but I'm not a complete ass.
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 664
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Re: How I make DPI-aware apps

Post by Kurzer »

srod wrote:...
For anyone who is laying out a UI which will automatically rescale for use on other systems then they need to take into account the DPI in force at the time they designed the UI...
Correct. And that's exactly what the PureBasics form designer unfortunately doesn't do. :-( On all systems <> 96 dpi you cannot use it, because the resulting DPI aware EXEs are scaled wrong even on the machine where the form was designed.
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2023: 56y
"Happiness is a pet." | "Never run a changing system!"
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How I make DPI-aware apps

Post by srod »

Well PB's designer draws at the exact pixel dimensions given regardless of DPI settings (fonts are scaled) and so with PB's DPIAware option in mind, all should be fine if you pretend you are indeed running at 96 DPI. However, laying out the UI elements and setting gadget dimensions to accommodate text will screw things up if not designing the UI under a true 96 DPI setting because of the scaled fonts and the like. This is possibly where you are seeing problems with the form designer. The same is true with creating a UI with PB code and the DPIAware option if not designing the UI under a 96 DPI.

I must admit that I like the DPIAware option for simply setting the relevant flag in the exe manifest. As far as then automatically scaling windows and gadgets - that doesn't sit right with me and I am finding it to be a pain, mostly because of the type of work I do. When dealing with Win32 style desktop apps, I see no real substitute for checking out the system DPI settings or DPI settings on a per monitor basis and going from there right at the outset. You lay out the UI elements based upon the DPI settings being applied there and then and incorporate similar logic shown in this and other threads to scale up/down as appropriate when faced with different deployments. Using DesktopResolutionY() for the DPI scale factors assumes the UI has been designed to run natively at 96 DPI which is probably fine for the majority of situations, but it can't be ideal.

Just my opinion.
I may look like a mule, but I'm not a complete ass.
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: How I make DPI-aware apps

Post by luis »

kurzer wrote: Excuse the short offtopic, but I can't enable this option in the process explorer at all.
I have Windows 7 x64 running here. Is this the reason that this column option is disabled in process explorer?
I would say yes, PE must use GetProcessDpiAwareness() at minimum, which is available only from Win 8.1.
"Have you tried turning it off and on again ?"
A little PureBasic review
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: How I make DPI-aware apps

Post by Dude »

srod wrote:For anyone who is laying out a UI which will automatically rescale for use on other systems then they need to take into account the DPI in force at the time they designed the UI. So, if they laid out the UI under a 300 DPI setting then they need :

Code: Select all

Global dpi.d=GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)/300
There is then no problem running this program on other systems (in theory at least) without any changes providing the /300 stays.
I just tested what you said, and you're absolutely correct! :shock: :lol: That is, I set my PC to 125% DPI and designed a new window layout based on that size. Then I set my PC back to 100% DPI and ran the code without changing it, and the layout was still correct. I must admit, I wasn't expecting that. Thanks srod, for the enlightenment! 8)

(Test code and images here for my own future reference).

Image

Image

Code: Select all

Global dpi.d=GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)/125

OpenWindow(0,200,200,210*dpi,105*dpi,"test",#PB_Window_SystemMenu)

x=10*dpi
w=190*dpi
h=25*dpi

TextGadget(1,x,10*dpi,w,h,"This is text that fills the gadget",#PB_Text_Border)

StringGadget(2,x,40*dpi,w,h,"Another small test of fitted text")

ButtonGadget(3,x,70*dpi,w,h,"And last example of exact text")

Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: How I make DPI-aware apps

Post by srod »

If you set your display to 125% DPI then you need to divide by 120 (the actual DPI) not 125. You're still confusing a % scale factor with an actual DPI. :)
I may look like a mule, but I'm not a complete ass.
Post Reply