Repository navigation
gtk: support fractional surface scaling - #14269
Conversation
We only support GTK/libadwaita combinations that are available through the latest stable release of Debian, the latest LTS release of Ubuntu or the latest Fedora. So in practice that means GTK 4.18 is the minimum supported and we can remove (or at least not introduce new) fall backs for older versions. |
jcollie
left a comment
There was a problem hiding this comment.
Let's drop all of the comptime checks for GTK version. As stated in another comment, 4.18 is our baseline now.
|
Thanks this is good testing. I will validate |
|
Just adding another data point for COSMIC Wayland: 1920x1080 with 125% scaling produces blurry fonts for me too. Everything looks washed out (more so than before), and vertical centering for hyphens is off. COSMIC's screenshot tool makes both screenshots look worse than they are in practice, but maybe this helps: |
|
Got it. both use COSMIC so at this point I will debug it on COSMIC myself too and report findings |
|
ok. I have tested on COSMIC Wayland. I could not reproduce the blur. I just pushed a commit that logs valuable info. (I understand that I do not have the same resolution as you guys but still). I did not gate the logs to Debug only but I guess I can do that if necessary later on. My setup:
Please paste it like this after running the ghostty bin from the latest commit |
|
My distro includes GTK version 4.14.5 — that might be the reason. Log |
Logs with 1.25 scale |
|
Since you're on NixOS as well, what package do I need to install to get your font and what do I need to add to config.ghostty to enable it? Not that it would fix the issue, but at least would make the setups more comparable. EDIT: Added |
|
Ye, it's currently a regression but with the logs hopefully it doesn't need to stay that way. Likely over the weekend I will investigate further. But the two cases we have here are/look like actually different artifacts. Gtk version could be the culprit of one? uncertain. Thanks 4 the testing btw. Will report back later |
|
I patched the change locally on my debian testing. And I see an improvement (fwiw). Though the improvements disappears when I create a new tab and I revert to the previous blurriness. Could the issues faced by folks here be related to the GTK components showing? I'm using Niri without any window decorations. So, when I create a new tab, I see the GTK tabs and see blurry fonts again. Edit: If I close the tab, text looks crisp again. Is it because of the difference in actual terminal size when the window chrome is visible/not-visible? |
|
Bro yes! I see the blur with tabs too and I never use them. Will check that out. Prolly the height messes up the calculation or smt like that |
|
Hi everyone. I pushed a fix for the tab/chrome blur. The texture is now snapped to the nearest device pixel based on the widget’s position in the window. I'd appreciate retesting with and without tabs. Now. for the case where GTK < 4.18, that might be another beast. it could be out if scope? given ghostty mininum is 4.18 |
Marvelous, font rendering quality is now fixed and better than 1.3.1 as expected from a proper fix. (Left is 1.3.1, right is 0489046 from this PR; remember to scale down to 80% so it looks "like on my screen")
At first I couldn't point a finger on why it looks better specifically, so here's a few things that stick out in particular:
So yeah, really happy about this. Thanks so much for the work :) EDIT: No visible difference between "tabs" vs "no tabs" in this version btw. |
|
Yes, I think it's noticeable in the screenshots. I will validate. could you please share logs? just in case |
Relevant logs |
1f49b0c to
7f374da
Compare
|
Alright i initially was snapping to the window widget. I think snapping to the GdkSurface is better. I also refactored it a bit. Let me know how it goes |
No change on my end, as good as before! |
pluiedev
left a comment
There was a problem hiding this comment.
Another benefit as far as I can see is that you fixed the stretching/tearing problem during resizes. That was something I was going to fix but I'm glad you beat me to the punch
|
@and-rs can you rebase on the latest main? |
|
@jcollie done i think |
|
@and-rs not quite. it's simpler than most people think: |
976967d to
d1a2fed
Compare
|
ahh got it thanks. I wasn't sure. what about now? |
looks good. |
pluiedev
left a comment
There was a problem hiding this comment.
There are some minor things I'd like to clean up especially regarding signals and stuff, but the immediate benefit is clear enough to me that we should just merge this as-is ![]()
| const surface = native.getSurface() orelse return; | ||
| const layout = scale_util.widgetLayoutForSize(widget, css_width, css_height); | ||
|
|
||
| log.info( |
Use gdk surface scale for device sizes and DPI, keep textures 1:1 with device pixels, and react to scale notify on resize/redraw.
Retry GdkSurface scale notify from size-allocate if realize ran before a native surface existed, and ref the surface so a replaced GdkSurface cannot leave a dangling handler. Skip snapshots when scale is non-positive to avoid dividing by zero. Round DPI in estimateInitialSize so the pre-present font grid matches the realized surface.
RenderSurface previously placed the texture at CSS origin (0,0). That is pixel-aligned only when the widget itself is aligned to the native surface. Tab bars and other chrome shift the widget to a fractional device coordinate, so GTK linearly samples every texel and fonts look blurry.
- I was originally snapping to the window widget - Size the buffer from the snapped span, not ceil(css × scale).
- widgetLayout replaces widgetDeviceSize - snapOffset guards non-positive scale - snappedAxis uses round span, not ceil
- widgetLayout replaces widgetDeviceSize calls - snapOrigin replaces snapshotOrigin method - widgetLayoutForSize replaces snappedDeviceSize site
14d20a6 to
e1916c1
Compare
|
@pluiedev I have demoted log. and I'd like to discuss more refinements in future PRs for sure. ty |
| .points = config.@"font-size", | ||
| .xdpi = @intFromFloat(x_dpi), | ||
| .ydpi = @intFromFloat(y_dpi), | ||
| .xdpi = @intFromFloat(@round(x_dpi)), |
There was a problem hiding this comment.
@round vs ceil/floor has bit us in the ass more times than I can count. Are we sure we want rounding here and not always a ceil or floor? This has been the source of many blurry issues in the past.
There was a problem hiding this comment.
Hi Mitchell.
Given that past context I would say flooring it should be ok too, as that was basically what was done before, although my original reasoning was that less common dpi, scale and res values would lead to floats and that it would be better to just round them up to whatever is closer. In the testing it looks like it worked well.
Id currently doubt that it poses a problem, but let me know if you want me to change it to floor.
There was a problem hiding this comment.
Okay, let's give it a shot. If anyone reports blurriness (in general) my first instinct now is to grep for rounds haha so this will come up.














Fix
This PR aims to use gdk_surface_get_scale() over gtk_widget_get_scale_factor() so device sizes and DPI follow the real fractional scale instead of the ceiled integer. Fall back to get_scale_factor when there is no GdkSurface.
in short: Keep textures 1:1 with device pixels and listen to GdkSurface notify::scale for resize/redraw.
Considerations
Ok the blurry font is in a way subtle and hard to perceive. But a very "easy" way to test this is by looking at box characters. Box chars are supposed to be rendered pixel perfect with sharp edges because they're just boxes I suppose (correct me if I am wrong). You can compare to other terminal emulators, but if you see blurry edges on them, then we're not rendering on fractional scaling properly.
Like this image in the discussion (the box char is much sharper on kitty previous to the fix):

I mention this because I have only tested on 1440p and ultrawide 4k (5120x2160). on Niri Wayland at 1.3x 1.5x and 1.666x.
I have NOT tested yet on X11. I would love is someone could help with that otherwise I will do it, it's just going to take me abit of time. Same for other resolutions.
Important
of course just let me know if I can improve or change anything here in the code. Thanks!
AI Disclosure
I used GPT 5.6 Terra and Grok 4.6 here & there.