New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mixed-DPI groundwork #2053
base: master
Are you sure you want to change the base?
Mixed-DPI groundwork #2053
Conversation
4323d91
to
3c62c42
Compare
Codecov Report
@@ Coverage Diff @@
## master #2053 +/- ##
==========================================
+ Coverage 79.55% 79.67% +0.12%
==========================================
Files 404 405 +1
Lines 27652 27713 +61
Branches 991 991
==========================================
+ Hits 21998 22081 +83
+ Misses 5218 5198 -20
+ Partials 436 434 -2
Continue to review full report at Codecov.
|
1e7a7a8
to
b6210ca
Compare
tests/test-screen-changes.lua
Outdated
| if #fake_screen == 0 then | ||
| return true | ||
| end | ||
| return #fake_screen == 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test runner (tests/_runner.lua) does the following after a step ran:
- If it returned true, it continues with the next step
- If it returns false, the whole test is considered to have failed
- If it returns nil, this step is retried after 0.1 seconds, but only up to 5 times. Then the test is considered to have failed.
So, before this commit, the garbage collector had up to 5 calls to collectgarbage("collect") time to get rid of the old screen. Now the screen must be removed on the first collection cycle.
I guess this change is the reason why the Travis build failed. Also, this cannot stall, since the test runner only tries up to five times and then gives up.
Please remove this commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Travis build was failing even before I introduced this commit, due to (I think) references to screen being held in the new font cache. But thanks for the explanation, I'll remove the commit.
lib/beautiful/xresources.lua
Outdated
| if dpi_per_screen[s] then | ||
| return dpi_per_screen[s] | ||
| if s then | ||
| return dpi_per_screen[s] or s.dpi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I query the DPI of some screen (which has no outputs), then the result will always be 96, since this is what the get_dpi function above returns. Shouldn't this rather use some of the calculations below instead if no outputs are available?
So, my proposal:
if dpi_per_screen[s] then
return dpi_per_screen[s]
end
if #s.outputs > 0 then
return s.dpi
end
lib/beautiful/xresources.lua
Outdated
| if dpi_per_screen[s] then | ||
| return dpi_per_screen[s] | ||
| if s then | ||
| return dpi_per_screen[s] or s.dpi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And the next problem: People who have wrong dimensions in their EDID data and overrode this via Xft.dpi will now have Xft.dpi being ignored. Since Xft.dpi is some explicit configuration, I think it should have higher priority than auto-configuration.
Meaning that I would propose the following order of settings:
- Forced settings via Lua:
dpi_per_screen[s] - Forced settings via xrdb:
Xft.dpi - Automatically computed from outputs:
s.outputs - Automatically computed from the size of the root window (the code below)
- Good old
96
lib/beautiful/xresources.lua
Outdated
| -- 96 DPI, according to the function used | ||
| -- @tparam function func a function to be applied to the DPI scaling factor | ||
| function xresources.set_dpi_rounding(func) | ||
| dpi_scale_rounding = func |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, but this function is useless. It gets as argument the value dpi/96. If you want to e.g. ceil this, you could just do set_dpi(s, 96*math.ceil(get_dpi(s)/96)) in your rc.lua (or just compute the wanted DPI value by hand). So, this does not add any new possibilities.
Put differently: Isn't it enough to be able to override DPI per-screen?
If you want a rounding function, I would more expect the user to override the round call in apply_dpi. However, that function must return integers, so this might not be all that useful either.
lib/beautiful/xresources.lua
Outdated
| @@ -145,6 +145,7 @@ end | |||
| -- @tparam[opt] integer|screen s The screen. | |||
| -- @treturn integer Resulting size (rounded to integer). | |||
| function xresources.apply_dpi(size, s) | |||
| if not size then return size end | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed? Which code would pass a nil (or false) here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in my mixed-dpi branch I remove a number of uses of the global apply_dpi, shifting the computation of the dpi scaling at use time rather than define time. There are a few cases in which the value is optional with a number of fallbacks that can all be nil (local somevar = args.stuff or default.stuff or blah.stuff). Rather than leaving that and then adding a new somevar = somevar and dpi(somevar, s) or nil line I decided to just wrap everything in a dpi(..., s), which then needs to pass through nil values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, but I guess then this behaviour should be added to the documentation of this function.
lib/beautiful/xresources.lua
Outdated
| @@ -64,6 +64,9 @@ function xresources.get_current_theme() | |||
| end | |||
|
|
|||
|
|
|||
| --- Store the cached or user-set per-screen DPI | |||
| -- Values are tables with key dpi (the dpi) and set_from (user, auto, xrdb) | |||
| -- The same is done for xresources.dpi | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What this commit actually does is to add a cache to DPI values. Why? Isn't the existing state of recomputing this "all the time" good enough? Do you have some example where adding a cache here speeds something up significantly?
lib/beautiful/init.lua
Outdated
| return fonts[name] | ||
| local fonts_key = {name, screen} | ||
| if fonts[fonts_key] then | ||
| return fonts[fonts_key] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops. I totally forgot about this function ignoring DPI.
Anyway, the above cache check does not work. Lua tables are only equal to themselves, i.e. the index operator does not do a deep comparison:
$ lua -e 't={} t[{}] = 42 print(t[{}])'
nil
Thus, the above doesn't cache check does not work.
Also, I wonder if it makes sense to have a version of this function that gets DPI as argument and another that gets a screen object, gets the screen's DPI and then just calls the other function (same for get_font_height). Thoughts?
(At least the cache only needs the DPI, not the screen itself)
ced6bdf
to
1994a6f
Compare
bbfbab8
to
6c8ce09
Compare
Otherwise we don't get the events that mark a runtime DPI update.
When picking the font height, offer the possibility to compute it based on the DPI of a specific screen, to improve support for mixed-DPI environments.
4b41b2b
to
1f9e1ff
Compare
In the process of deprecation of the old xresources.*dpi API, the global font DPI management is moved to beautiful itself. (awful.screen was also a good candidate, but this led to some pretty messy nearly irresolvable interdependencies between awful and beautiful.)
Allow consistent font scaling in mixed-DPI environments.
The per-screen DPI does not make use of Xft.dpi now, and if RANDR information is not available we fall back to the core DPI.
Scale a length to account for the DPI of the given screen.
Not all user appreciated fractional scaling, make it easier for them to snap to integers.
Values should be defined unscaled, and scaled based on the DPI of the screen where the popup appears.
1f9e1ff
to
b823218
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dunno if I really like this or not, but here are some comments. What do others think? @Elv13 @blueyed @actionless
lib/gears/dpi.lua
Outdated
| end | ||
| end | ||
| return dpi | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a good idea to have? If we want to have per screen DPIs, there should be no "global" DPI that could accidentally be used in code instead.
| @@ -98,6 +100,41 @@ local active_font | |||
| --- The current theme path (if any) | |||
| -- @tfield string beautiful.theme_path | |||
|
|
|||
| --- Font DPI API | |||
| -- @section font_dpi | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be a good idea why for fonts the number of pixels per inch of your monitor is different than for non-fonts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since I fail to parse my own sentence: The docs here could explain the concept for "fonts get a different DPI" a bit here.
| function beautiful.screen_font_dpi(scr) | ||
| scr = scr and screen[scr] or screen.primary | ||
| return beautiful.get_font_dpi()*scr.dpi/screen.primary.dpi | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Elv13 What do you think of this?
lib/beautiful/init.lua
Outdated
| @@ -255,7 +256,7 @@ end | |||
| -- @tparam[opt] screen scr Screen to get the DPI information from. | |||
| -- If unspecified, the global font DPI setting will be used. | |||
| function beautiful.get_font_height(name, scr) | |||
| return load_font(name, beautiful.xresources.get_dpi(scr)).height | |||
| return load_font(name, beautiful.screen_font_dpi(scr)).height | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, this opens quite a can of worms.
Primary screen changed? Font height changed on all screens!
Primary screen changed DPI value? Font height changed on all screens!
Some random screen changed DPI value? Font height changed for that screen.
I bet that nothing will handle these correctly.
| -- So we offer the possibility for the user to choose a global policy, | ||
| -- and all DPI usage will take this into consideration. | ||
| -- Note that rounding is only applied to autocomputed DPI values, never | ||
| -- when the user sets the DPI themselves. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't say I'm convinced that this is a good idea, but at least the default is identity.
@Elv13 What do you think?
|
|
||
| runner.run_steps(steps) | ||
|
|
||
| -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yay for tests! :-)
I think this should be done as an unit test instead of integration (i.e. as spec/gears/dpi_spec.lua). gears.dpi does not use much APIs, so that can easily be mocked (it just needs root, I think).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll look into this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to ping me if needed.
| end | ||
| local dpi = beautiful.screen_font_dpi(s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes the semantics of which DPI value is used when this function is called with s=nil. Previously, it used some concept of "core DPI" while now it uses the DPI of the primary screen.
@Elv13 Does that count as a regression?
| if s then | ||
| return s:dpi_scale(size) | ||
| else | ||
| return round(size * require("beautiful").screen_font_dpi()/96) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this use font DPI? If you just use the font DPI everywhere, then s.dpi is not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an issue about “what has this call being used for in legacy code” and “how do we transition from there to the API we want now”.
Among other things, this means that the only case where you access the DPI without a screen specification is if you want to know the user-specified font scaling, since there is (or should not be) any concept of “global DPI” (otherwise).
| if s then | ||
| dpi = screen[s].dpi | ||
| else | ||
| if not s then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is with :fit and :draw? This commits make the "non-widget" functions use a different DPI value than the actual drawing does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocked by #2091, we should first decide about the route before we go hiking.
Some more groundwork to improve support for mixed-DPI setups.
It pre-computes the per-screen DPI, while still allowing overrides, it allows choosing a rounding method for the used DPI, and font loading now allows an associated screen, to be used for height computation.
(Some of these make more sense view in terms of the subsequent fixes spread around the lib part, the work is pushed to my
mixed-dpibranch, but still needs a more thorough review.)