Allow disabling scale detection (#128)

* Allow disabling scale factor detection. Do not use factors higher than 2 on Linux

* Add note about window size

* Move scale detection comment to appDisableAutoScale

* Attempt to use SDL_GetDisplayDPI when detecting scale factor on Linux

* Minor changes to appDisableAutoScale documentation

* Update Changelog

* Remove duplicate documentation related to Linux scale factor detection

* Add safeguard to Windows dpi detection
This commit is contained in:
Francisco Vallarino 2022-04-25 18:40:52 +02:00 committed by GitHub
parent add5a31560
commit 1ab5971bef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 21 deletions

View File

@ -35,6 +35,7 @@
- `compositeMergeEvents`, for completeness ([PR #114](https://github.com/fjvallarino/monomer/pull/114)).
- Support for symbols and other keys in `keystroke` ([PR #117](https://github.com/fjvallarino/monomer/pull/117)).
- New constructor (`buttonD_`) and `ignoreParentEvts` configuration option to `button` ([PR #123](https://github.com/fjvallarino/monomer/pull/123)).
- Allow disabling auto scale detection with `appDisableAutoScale` ([PR #128](https://github.com/fjvallarino/monomer/pull/128)).
### Changed

View File

@ -68,10 +68,15 @@ initSDLWindow config = do
platform <- getPlatform
initDpiAwareness
factor <- case platform of
baseFactor <- case platform of
"Windows" -> getWindowsFactor
"Linux" -> getLinuxFactor
_ -> return 1 -- macOS
let factor
| disableAutoScale = 1
| otherwise = baseFactor
let (winW, winH) = (factor * fromIntegral baseW, factor * fromIntegral baseH)
window <-
@ -128,6 +133,7 @@ initSDLWindow config = do
}
compositingFlag = fromMaybe False (_apcDisableCompositing config)
userScaleFactor = fromMaybe 1 (_apcScaleFactor config)
disableAutoScale = _apcDisableAutoScale config == Just True
(baseW, baseH) = case _apcWindowState config of
Just (MainWindowNormal size) -> size
_ -> defaultWindowSize
@ -209,28 +215,32 @@ getDisplayDPI =
vdpi <- peek pvdpi
return (realToFrac ddpi, realToFrac hdpi, realToFrac vdpi)
-- | Returns the default resize factor for Windows
-- | Returns the default resize factor for Windows.
getWindowsFactor :: IO Double
getWindowsFactor = do
(ddpi, hdpi, vdpi) <- getDisplayDPI
return (hdpi / 96)
getWindowsFactor = max 1 <$> getDisplayDPIFactor
{-|
Returns a resizing factor to handle HiDPI on Linux. Currently only tested on
Wayland (Ubuntu 21.04).
-}
-- | Returns the default resize factor for Linux.
getLinuxFactor :: IO Double
getLinuxFactor =
getLinuxFactor = do
dpiFactor <- getDisplayDPIFactor
alloca $ \pmode -> do
Raw.getCurrentDisplayMode 0 pmode
mode <- peek pmode
let width = Raw.displayModeW mode
-- Applies scale in half steps (1, 1.5, 2, etc)
let baseFactor = 2 * fromIntegral width / 1920
if width <= 1920
then return 1
else return (fromIntegral (ceiling baseFactor) / 2)
let width = Raw.displayModeW mode
let detectedDPI
| dpiFactor > 0 = dpiFactor
| width <= 1920 = 1
| otherwise = 2
return detectedDPI
-- | Returns DPI scaling factor using SDL_GetDisplayDPI.
getDisplayDPIFactor :: IO Double
getDisplayDPIFactor = do
(ddpi, hdpi, vdpi) <- getDisplayDPI
return (hdpi / 96)
setDisableCompositorHint :: Bool -> IO ()
setDisableCompositorHint disable = void $

View File

@ -165,11 +165,15 @@ data AppConfig e = AppConfig {
_apcMaxFps :: Maybe Int,
{-|
Scale factor to apply. This factor only affects the content, not the size of
the window. It is applied in addition to the OS zoom in plaforms where it is
reliably detected (i.e., system scaling may not be detected reliably on Linux)
the window. It is applied in addition to the detected display scaling.
-}
_apcScaleFactor :: Maybe Double,
{-|
Whether display scaling detection should not be attempted. If set to True, the
display scale will be set to 1. This works together with 'appScaleFactor'.
-}
_apcDisableAutoScale :: Maybe Bool,
{-|
Available fonts to the application. An empty list will make it impossible to
render text.
-}
@ -206,6 +210,7 @@ instance Default (AppConfig e) where
_apcUseRenderThread = Nothing,
_apcMaxFps = Nothing,
_apcScaleFactor = Nothing,
_apcDisableAutoScale = Nothing,
_apcFonts = [],
_apcTheme = Nothing,
_apcInitEvent = [],
@ -229,6 +234,7 @@ instance Semigroup (AppConfig e) where
_apcUseRenderThread = _apcUseRenderThread a2 <|> _apcUseRenderThread a1,
_apcMaxFps = _apcMaxFps a2 <|> _apcMaxFps a1,
_apcScaleFactor = _apcScaleFactor a2 <|> _apcScaleFactor a1,
_apcDisableAutoScale = _apcDisableAutoScale a2 <|> _apcDisableAutoScale a1,
_apcFonts = _apcFonts a1 ++ _apcFonts a2,
_apcTheme = _apcTheme a2 <|> _apcTheme a1,
_apcInitEvent = _apcInitEvent a1 ++ _apcInitEvent a2,
@ -301,15 +307,58 @@ appMaxFps fps = def {
}
{-|
Scale factor to apply. This factor only affects the content, not the size of the
window. It is applied in addition to the OS zoom in plaforms where it is
reliably detected (i.e., system scaling may not be detected reliably on Linux).
Scale factor to apply to the viewport. This factor only affects the content, not
the size of the window. It is applied in addition to the detected display scale
factor, and can be useful if the detected value is not the desired.
-}
appScaleFactor :: Double -> AppConfig e
appScaleFactor factor = def {
_apcScaleFactor = Just factor
}
{-|
Whether display scaling detection should not be attempted. If set to True, the
display scale will be set to 1. This flag does not cause an effect on macOS.
Disabling auto scaling also affects window size on Linux and Windows in the
cases where the library would have applied scaling. This happens because window
and viewport size are the same in those operating systems. Window size can be
adjusted with 'appWindowState'.
The logic for detecting display scaling varies depending on the platform:
__macOS__
Scaling can be detected based on the window size and viewport size; the ratio
between these two give the scaling factor.
Using window and viewport size for detecting DPI only works on macOS; both
Windows and Linux return the same value for window and viewport size.
__Windows__
SDL_GetDisplayDPI returns the DPI of the screen, and dividing by 96 gives the
scaling factor. This factor is used to scale the window size and the content.
__Linux__
The situation is more complex, since SDL_GetDisplayDPI does not always return
valid information. There is not a practical DPI/scale detection solution that
works for all combinations of Linux display servers and window managers. Even
when using the most popular window managers, the scaling factor may be handled
differently by the distribution (GNOME in Ubuntu). For a reference of some of
the existing options for DPI scaling detection, check here:
https://wiki.archlinux.org/title/HiDPI.
Considering the above, when SDL_GetDisplayDPI fails, the library assumes that a
screen width larger than 1920 belongs to an HiDPI display and uses a scale
factor of 2. This factor is used to scale the window size and the content.
-}
appDisableAutoScale :: Bool -> AppConfig e
appDisableAutoScale disable = def {
_apcDisableAutoScale = Just disable
}
{-|
Available fonts to the application. An empty list will make it impossible to
render text.