Well after some test here are summary:
1st variant: it scales both font size and whole UI, so perfectly fits to any DPI setting. For now I didn't found any side-effects.
2nd variant: it doesn't scale UI and doesn't allow Windows to scale up fonts. So program UI is always fixed, which means if you made it too small, it will look bad on high DPI; if you made it too large, it will look bad on lower DPIs. Generally it is acceptable solution, it requires only to control used fonts sizes (which is done automatically by code posted previously).
3rd variant: without any scaling. Windows scales font size automatically but UI remains fixed, and your app looks like a crap. Or if no event manifest (DPIAware) set, Windows 8-10 scales whole UI, it becomes blured and looks even worst than crap.
So 1st variant should be perfect one, but it is complicated to finish it (need to add wrapper for every PB function which does something related with gadget/window width, height and probably x/y too. I've added most of such functions already, but there are still enough. However I think I'll continue with that, unless there is no such fix built-in at PB functions itself. Nice If someone like to participate too ^^
DPI Aware Application
Re: DPI Aware Application
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
- Roger Hågensen
- User
- Posts: 47
- Joined: Wed Mar 25, 2015 1:06 pm
- Location: Norway
Re: DPI Aware Application
I'm hoping down the road that Fred and Freak will add DPI support to all GUI functions (it needs to be combined with a DPI aware manifest as well).
Your 2nd variant fights against the purpose of a high DPI rendering.
As an example think of a game rendered rather than in text/font sizes.
With a high DPI display you'd normally want everything rendered with more details/pixels, this gets rid of the need for antialiasing. With 5K (and larger) monitors you really do not need antialiasing anymore at "normal" viewing distances.
The user may also tell the OS (Windows) that they'd like larger/smaller text, and your application should honor that wish.
You may also want to provide a DPI override/customization option in your program in case the user want your GUI to appear larger or smaller than other apps.
Your 2nd variant fights against the purpose of a high DPI rendering.
As an example think of a game rendered rather than in text/font sizes.
With a high DPI display you'd normally want everything rendered with more details/pixels, this gets rid of the need for antialiasing. With 5K (and larger) monitors you really do not need antialiasing anymore at "normal" viewing distances.
The user may also tell the OS (Windows) that they'd like larger/smaller text, and your application should honor that wish.
You may also want to provide a DPI override/customization option in your program in case the user want your GUI to appear larger or smaller than other apps.
4 music albums under CC BY license available for free (any use, even commercial) at Skuldwyrm.no
Re: DPI Aware Application
For the moment I don’t need glasses. I have 23″ Monitor, using 150% DPI. Running your examples I can barely see the text.
Like Roger Hågensen said, you should honour peoples choices. If you programming for personal use, do whatever.
Back in 2015 I’ve posted my modified version of Rescator code. Once again, here it is;
100% DPI
150% DPI
Like Roger Hågensen said, you should honour peoples choices. If you programming for personal use, do whatever.
Back in 2015 I’ve posted my modified version of Rescator code. Once again, here it is;
Code: Select all
;;;;; DPI-Aware Application ;;;;;
; Posted by Rescator on Sat Jan 02, 2010 - http://forums.purebasic.com/english/viewtopic.php?f=12&t=40507
; Platforms: Windows Only
;Placed in the Public Domain by Roger Hågensen.
#PB_Compiler_Exe=#True ;This does not exist (yet?)
;http://msdn.microsoft.com/en-us/library/dd464660%28VS.85%29.aspx
Global.f _ScaleDPI_X_ = 1.0, _ScaleDPI_Y_ = 1.0, Font$, FontSize.b = 9
#DefaultDPIX = 96.0 ;Different platforms might have different default DPI, Windows is 96 DPI.
#DefaultDPIY = 96.0
Procedure InitScaleDPI() ;Windows 5.0 or higher needed for minimum functionality of this procedure.
Protected.i dc, lpx, lpy, IsUser32DLL, *SetProcessDPIAware, *IsProcessDPIAware, DPIAware.l = #False , ncm.NONCLIENTMETRICS, Font;, Font$, FontSize.b = 9
;This part is Windows 6.x+ only (Vista etc.) and must be done before we use devcaps.
;http://msdn.microsoft.com/en-us/library/dd464660%28VS.85%29.aspx#declaring_dpi_awareness
;You really should use the DPI aware manifest instead of SetProcessDPIAware() when possible.
;On Windows 2000 and XP the manifest has no effect and set dpi aware is not available,
;however Devicecaps still returns usefull info that can be used.
;Note! If the dpi aware manifest is missing on Vista and Win7 then the OS will lie on devicecaps and will autoscale the entire app window.
CompilerIf #PB_Compiler_Exe ;Only use this in exes, as DLLs inherit DPI from the calling process.
;If the exe or the calling exe in case of this being a DLLs is allready dpi aware (like through a manifest),
;then we skip using the the set dpi aware function, a DLLs should never use the set function, but it should check if the process id dpi aware
;and apply the proper modifiers where appropriate obviously.
IsUser32DLL = OpenLibrary(#PB_Any, "user32.dll")
If IsUser32DLL
*IsProcessDPIAware = GetFunction(IsUser32DLL, "IsProcessDPIAware")
If *IsProcessDPIAware
DPIAware = CallFunctionFast(*IsProcessDPIAware)
EndIf
If Not DPIAware
*SetProcessDPIAware = GetFunction(IsUser32DLL, "SetProcessDPIAware")
If *SetProcessDPIAware
CallFunctionFast(*SetProcessDPIAware)
EndIf
EndIf
EndIf
CompilerEndIf
dc = GetDC_(#Null)
If dc
lpx = GetDeviceCaps_(dc, #LOGPIXELSX)
lpy = GetDeviceCaps_(dc, #LOGPIXELSY)
If lpx>0
_ScaleDPI_X_ = lpx/#DefaultDPIX
EndIf
If lpy>0
_ScaleDPI_Y_ = lpy/#DefaultDPIY
EndIf
ReleaseDC_(#Null, dc)
EndIf
;Get the system font for message boxes etc.
;We default to a size of 9, which is also the Vista and Win7 default size.
;The OS will automatically (Vista and Win7 at least) scale the font per the current user's DPI setting.
ncm\cbSize = SizeOf(NONCLIENTMETRICS)
If SystemParametersInfo_(#SPI_GETNONCLIENTMETRICS, SizeOf(NONCLIENTMETRICS), ncm, #Null)
Font$ = PeekS(@ncm\lfMessageFont\lfFaceName)
If OSVersion() < #PB_OS_Windows_Vista : FontSize = 8 : EndIf
FontSize = IntQ(FontSize*_ScaleDPI_Y_) ;: Debug FontSize
; scaleFactorY = MulDiv_(lpy, 100, 96) ; Actual Scale factor (g.e. 150%)
Font = LoadFont(#PB_Any, Font$, FontSize, #PB_Font_HighQuality)
If Font
SetGadgetFont(#PB_Default, FontID(Font))
EndIf
EndIf
EndProcedure : InitScaleDPI()
Macro Dx(x) : (x)*_ScaleDPI_X_ : EndMacro
Macro Dy(y) : (y)*_ScaleDPI_Y_ : EndMacro
Macro pDx(x) : Int((x)/_ScaleDPI_X_) : EndMacro
Macro pDy(y) : Int((y)/_ScaleDPI_Y_) : EndMacro
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;-> Procedures for Macros associates
Procedure _OpenWindow(WindowID.l, x.l, y.l, Width.l, Height.l, Title$, Flags.l, ParentWinID.l)
ProcedureReturn OpenWindow(WindowID, x, y, Width, Height, Title$, Flags, ParentWinID)
EndProcedure
Procedure _StringGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
ProcedureReturn StringGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure
Procedure _ButtonGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
ProcedureReturn ButtonGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure
Procedure _FrameGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
ProcedureReturn FrameGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure
Procedure _PanelGadget(GadgetID.l, x.l, y.l, Width.l, Height.l)
ProcedureReturn PanelGadget(GadgetID, x, y, Width, Height)
EndProcedure
Procedure _TrackBarGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Minimum, Maximum, Flags)
ProcedureReturn TrackBarGadget(GadgetID, x, y, Width, Height, Minimum, Maximum, Flags)
EndProcedure
Procedure _CheckBoxGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
ProcedureReturn CheckBoxGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure
Procedure _ComboBoxGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Flags.l)
ProcedureReturn ComboBoxGadget(GadgetID, x, y, Width, Height, Flags)
EndProcedure
Procedure _OptionGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$)
ProcedureReturn OptionGadget(GadgetID, x, y, Width, Height, Text$)
EndProcedure
Procedure _TextGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Text$, Flags.l)
ProcedureReturn TextGadget(GadgetID, x, y, Width, Height, Text$, Flags)
EndProcedure
Procedure _ListIconGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Title$, TitleWidth.l, Flags.l)
ProcedureReturn ListIconGadget(GadgetID, x, y, Width, Height, Title$, TitleWidth, Flags)
EndProcedure
Procedure _ListViewGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Flags.l)
ProcedureReturn ListViewGadget(GadgetID, x, y, Width, Height, Flags)
EndProcedure
Procedure _EditorGadget(GadgetID.l, x.l, y.l, Width.l, Height.l, Flags.l)
ProcedureReturn EditorGadget(GadgetID, x, y, Width, Height, Flags)
EndProcedure
; Procedure _WindowBound(WindowID.l, MinimumWidth.l, MinimumHeight.l, MaximumWidth.l, MaximumHeight.l)
; ProcedureReturn WindowBounds(WindowID, MinimumWidth, MinimumHeight, MaximumWidth, MaximumHeight)
; EndProcedure
;-> Macros for Procedures associates
Macro OpenWindow(WindowID, x, y, Width, Height, Title, Flags = #PB_Window_SystemMenu, ParentWinID = 0)
_OpenWindow(WindowID, Dx(x), Dy(y), Dx(Width), Dy(Height), Title, Flags, ParentWinID)
EndMacro
Macro StringGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
_StringGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro
Macro ButtonGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
_ButtonGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro
Macro FrameGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
_FrameGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro
Macro PanelGadget(GadgetID, x, y, Width, Height)
_PanelGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height))
EndMacro
Macro TrackBarGadget(GadgetID, x, y, Width, Height, Minimum, Maximum, Flags = 0)
_TrackBarGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Minimum, Maximum, Flags)
EndMacro
Macro CheckBoxGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
_CheckBoxGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro
Macro ComboBoxGadget(GadgetID, x, y, Width, Height, Flags = 0)
_ComboBoxGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Flags)
EndMacro
Macro OptionGadget(GadgetID, x, y, Width, Height, Text)
_OptionGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text)
EndMacro
Macro TextGadget(GadgetID, x, y, Width, Height, Text, Flags = 0)
_TextGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Text, Flags)
EndMacro
Macro ListIconGadget(GadgetID, x, y, Width, Height, Title, TitleWidth, Flags = 0)
_ListIconGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Title, Dx(TitleWidth), Flags)
EndMacro
Macro ListViewGadget(GadgetID, x, y, Width, Height, Flags = 0)
_ListViewGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Flags)
EndMacro
Macro EditorGadget(GadgetID, x, y, Width, Height, Flags)
_EditorGadget(GadgetID, Dx(x), Dy(y), Dx(Width), Dy(Height), Flags)
EndMacro
; Macro WindowBound(WindowID, MinimumWidth, MinimumHeight, MaximumWidth, MaximumHeight)
; _WindowBound(WindowID, Dx(MinimumWidth), Dy(MinimumHeight), Dx(MaximumWidth), Dy(MaximumHeight))
; EndMacro
CompilerIf #PB_Compiler_IsMainFile
;The Gadget example from PureBasic manual.
#WindowWidth = 390
#WindowHeight = 350
If OpenWindow(0, 100, 200, #WindowWidth, #WindowHeight, "PureBasic - Gadget Demonstration", #PB_Window_MinimizeGadget)
Debug WindowWidth(0)
Debug pDx(WindowWidth(0))
Debug WindowHeight(0)
Debug pDx(WindowHeight(0))
Top = 10
GadgetHeight = 24
FrameGadget(#PB_Any, 10, Top, 370, 290, "Player...") : Top+25
StringGadget(0, 20, Top, 200, GadgetHeight, "")
ButtonGadget(1, 223, Top, 72, GadgetHeight, "Play")
ButtonGadget(2, 295, Top, 72, GadgetHeight, "Stop") : Top+35
DisableGadget(2,1)
GadgetToolTip(1,"Play the current song")
PanelGadget(3, 20, Top, #WindowWidth-50, #WindowHeight-Top-55)
AddGadgetItem(3, 0, "MP3 PlayList")
ListViewGadget(4, 6, 10, 230, 148)
For k=0 To 30
AddGadgetItem(4, -1, "Music Song n° "+Str(k))
Next
ButtonGadget(5, 250, 10, 80, GadgetHeight, "Add")
ButtonGadget(6, 250, 38, 80, GadgetHeight, "Remove")
ButtonGadget(7, 250, 66, 80, GadgetHeight, "Select")
GadgetToolTip(7, "Select the current song")
TrackBarGadget(17, 10, pDx(GadgetHeight(4))+10, 310, 30, 0, 100)
AddGadgetItem(3, 1, "Options")
Top = 10
CheckBoxGadget(10, 10, Top, 250, GadgetHeight, "Enable low-pass filter") : Top+30
CheckBoxGadget(11, 10, Top, 250, GadgetHeight, "Enable visual plug-in") : Top+30
ComboBoxGadget(12, 10, Top, 250, 30) : Top+30
AddGadgetItem(12, -1, "FireWorks")
AddGadgetItem(12, -1, "OpenGL spectrum")
AddGadgetItem(12, -1, "Bump bass")
SetGadgetState(12,0)
DisableGadget(12,1)
OptionGadget(13, 10, Top, 81, GadgetHeight, "640*480") : Top+20
OptionGadget(14, 10, Top, 81, GadgetHeight, "800*600") : Top+20
OptionGadget(15, 10, Top, 81, GadgetHeight, "1024*768")
SetGadgetState(13, 1)
ButtonGadget(16, 150, Top, 80, GadgetHeight, "Info")
CloseGadgetList()
TextGadget (9, 10, #WindowHeight-30, 250, 24, "PureBasic - Gadget demonstration")
ButtonGadget(8, #WindowWidth-100, #WindowHeight-36, 80, 24, "Quit")
SetGadgetState(3, 0)
Repeat
EventID = WaitWindowEvent()
If EventID = #PB_Event_Gadget
Select EventGadget()
Case 0
If EventType() = #PB_EventType_ReturnKey
MessageRequester("Info", "Return key pressed", 0)
SetActiveGadget(0)
EndIf
Case 1 ; Play
DisableGadget(2,0) ; Enable the 'Stop' gadget
DisableGadget(1,1) ; Disable the 'Play' Gadget
Case 2 ; Stop
DisableGadget(1,0) ; Enable the 'Play' gadget
DisableGadget(2,1) ; Disable the 'Stop' Gadget
Case 4
If EventType() = 2
SetGadgetText(0, GetGadgetText(4)) ; Get the current item from the ListView..
EndIf
Case 5 ; Add
AddGadgetItem(4, -1, "New Item Added...")
Case 6 ; Remove
RemoveGadgetItem(4, GetGadgetState(4)) ; Remove the current element of the ListView
Case 7 ; Select
SetGadgetText(0, GetGadgetText(4)) ; Get the current item from the ListView..
Case 8 ; Quit...
EventID = #PB_Event_CloseWindow
Case 11 ; Enable PlugIn..
DisableGadget(12, 1-GetGadgetState(11))
Case 16
If GetGadgetState(13) : Result$ = GetGadgetText(13) : EndIf
If GetGadgetState(14) : Result$ = GetGadgetText(14) : EndIf
If GetGadgetState(15) : Result$ = GetGadgetText(15) : EndIf
MessageRequester("Info", "Selected screen mode: "+Result$, 0)
Case 17
SetGadgetText(0, Str(GetGadgetState(17)))
EndSelect
EndIf
Until EventID = #PB_Event_CloseWindow
EndIf
End
CompilerEndIf
150% DPI
ʽʽ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
Re: DPI Aware Application
There is fatal flaw in first example in situations like following.
The only solution of this seems to be usage of scaling macros manually everytime (or append extra switches to a wrappers).
This is painful and error-full way, leading you to dirty and illogical code and not perfect result (there is scaling problem caused by non-integral values rounding, @Roger Hågensen mentioned it in other thread too. this problem cannot be resolved while calculating pixels [not twips, etc], and will not let you to make pixel-perfect fancy compact guis with controls are placed side to side, etc. btw past year I've encountered same problem while doing smooth scaling in 2D game).
Thus second variants wins :) At least for now. I hope it will not show similar "fatal flaws".
To Roger Hågensen: the purpose of fixed UI is that you setting DPI for it at design stage. So you have statistics by average user display sizes, use this info when designing your program and that should give enough honour to what people like to see. And of course it can be designed specially for 5k displays as well.
That's how apps were made before and I thing this is right way, unless there are no acceptable way to design true DPI-aware GUIs without wasting undue efforts and getting huge code-related problems from it.
Code: Select all
; imagine you are toggling UI height to hide part of window or show it
If Not OptionWindowIsLarge
; here you resizing gui height to absolute value
; asume this value is actual for 100% scaling (96 DPI)
; ResizeWindow is replaced by macro, so it scales 290 up and result is OK
ResizeWindow(WndMain, #PB_Ignore, #PB_Ignore, #PB_Ignore, 290)
Else
; and here you resizing gui height to relative value
; this value received from some gadget Y coordinate
; the result is shit, because ResizeWindow is replaced by macro
; and scales up all passed params
; and gadget Y was already scaled on gadget creation
; you need to pass here Y coordinate without applied macro to it for result to be OK
ResizeWindow(WndMain, #PB_Ignore, #PB_Ignore, #PB_Ignore, GadgetY(SomeGadget))
EndIf
This is painful and error-full way, leading you to dirty and illogical code and not perfect result (there is scaling problem caused by non-integral values rounding, @Roger Hågensen mentioned it in other thread too. this problem cannot be resolved while calculating pixels [not twips, etc], and will not let you to make pixel-perfect fancy compact guis with controls are placed side to side, etc. btw past year I've encountered same problem while doing smooth scaling in 2D game).
Thus second variants wins :) At least for now. I hope it will not show similar "fatal flaws".
To Roger Hågensen: the purpose of fixed UI is that you setting DPI for it at design stage. So you have statistics by average user display sizes, use this info when designing your program and that should give enough honour to what people like to see. And of course it can be designed specially for 5k displays as well.
That's how apps were made before and I thing this is right way, unless there are no acceptable way to design true DPI-aware GUIs without wasting undue efforts and getting huge code-related problems from it.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
Re: DPI Aware Application
Side-effects I've noticed already:Thus second variants wins At least for now. I hope it will not show similar "fatal flaws".
- window title font is not remaining fixed
- same with window buttons, scrollbars and probably several other controls (which font is not controled by application, or which images have different versions for different DPIs in windows theme file).
So the program doesn't looks fine anyway with 125% / 150% scaling (and will look much worst with larger).
Here is example for 150%.
http://s000.tinyupload.com/index.php?fi ... 1890217672
However, that seems to be most acceptable solution for now ^^
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
- Roger Hågensen
- User
- Posts: 47
- Joined: Wed Mar 25, 2015 1:06 pm
- Location: Norway
Re: DPI Aware Application
Lunasole wrote:...(there is scaling problem caused by non-integral values rounding, @Roger Hågensen mentioned it in other thread too. this problem cannot be resolved while calculating pixels [not twips, etc], and will not let you to make pixel-perfect fancy compact guis with controls are placed side to side, etc. btw past year I've encountered same problem while doing smooth scaling in 2D game).
Games and such (particularly fullscreen ones) have the luxury of scaling. Due to the many different monitor/screen (and window sizes) they always are designed for scalability, and air for pixel rounding is included. Some games over the years (more recently?) have begun to provide user options in the game menus where you can scale the HUD/UI of the game to smaller or larger.Lunasole wrote:To Roger Hågensen: the purpose of fixed UI is that you setting DPI for it at design stage. So you have statistics by average user display sizes, use this info when designing your program and that should give enough honour to what people like to see. And of course it can be designed specially for 5k displays as well.
I'm considering doing that for my current and future projects, so along with proper HiDPI support I'll add a scaling option too. This would be a sort of per program DPI setting, defaulting to the system/monitor DPI but letting the user tweak it. I can even add a toggle option "Reset to default if system settings change" as well.
As to pixel rounding issues. You normally have some air between buttons and other interactive elements to avoid the user accidentally clicking the wrong thing. Artwork and decoration can be handled by simply using alpha transparency, and if artwork has to be pixel perfect joined together then simply join then before drawing the artwork on the GUI.
Pixel rounding errors will always have to be designed for/around by the coder/designer. The rest PureBasic could (and should?) handle "behind the scenes".Lunasole wrote:That's how apps were made before and I thing this is right way, unless there are no acceptable way to design true DPI-aware GUIs without wasting undue efforts and getting huge code-related problems from it.
A user may choose a different font size independent of the DPI. Different languages have different length sentences (which brings a whole other headache intro design).Lunasole wrote:Side-effects I've noticed already:
- window title font is not remaining fixed
- same with window buttons, scrollbars and probably several other controls (which font is not controled by application, or which images have different versions for different DPIs in windows theme file).
Some programs use a mix of custom and system buttons and other elements.
Most Windows system GUI stuff "should" be fully Windows 8+ DPI compliant, but there are still a lot of stuff in Windows 10 even that does not handle higher DPI properly.
As higher resolution monitors appear the DPI will keep crawling upwards. Mobiles have passed the "300 DPI is enough" magic line quite some time ago. For a long time monitors was 72PPI (Mac) or 96PPI (Windows). The web now is largely 96PPI and all modern browsers assumes this and will transform this into the OS/display DPI setting, but they also has a scale slider that users can change.
A ~20inch 5K monitor at around arms length is around 300 DPI to your eyes. Larger monitors are usually placed further away. I can't recall what a 8K monitor hung on the other wall in a typical living room would be (with the couch in the middle of the room) but I'm guessing 8K might be a tad overkill if you want to just hit that 300 DPI.
Beyond 300 DPI relatively speaking you are not gaining much in detail. At 300 DPI (or PPI) you no longer need anti-aliasing as the average person can no longer distinguish individual pixels, so "crawling lines" is no longer an issue.
VR is the other way, a screen for VR is closer than arms length, closer than you'd hold a mobile phone (where the 300 DPI thing originated from, thanks Steve Jobs initially) so you need a higher pixel density on the display (and hence a higher DPI slash PPI. VR may need 8K since the display is so close to your face/eyes.
(K is not really in the mainstream market yet. I can't recall seeing 7K nor even 6K. I have seen 5K mentioned here and there. 4K is now the current mainstream HiDPI for monitors.
One advantaged of 5K+ though is that you could lean really close to a screen to see more detail, but you can damage your eyesight in the long run by doing that (what is the point of HiDPI then?), a zoom is much more useful instead.
Other issues are lack of insanely high res content, and how much GPU power you need to render things at a acceptable minimum frame rate (60FPS?) beyond 2K that the average (majority) can afford.
But I'm getting sidetracked. Back to topic. higher DPI is here to stay. Windows 8 (or just 8.1?) and later allows different DPI settings per monitor which I consider groundbreaking, but also damn annoying as a programmer.
You have to design for the possibility that the user drags a window from a low DPI to a high DPI monitor, but once you got the code in you recycle that for all your projects.
4 music albums under CC BY license available for free (any use, even commercial) at Skuldwyrm.no