Skip to content

gtk: support fractional surface scaling - #14269

Merged
mitchellh merged 9 commits into
ghostty-org:mainfrom
and-rs:gtk-fractional-scale
Sep 25, 2026
Merged

mitchellh merged 9 commits into
ghostty-org:mainfrom
and-rs:gtk-fractional-scale

Conversation

@and-rs

@and-rs and-rs commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

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):
image

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.

@and-rs
and-rs requested review from a team as code owners September 17, 2026 04:40
@and-rs and-rs changed the title Gtk fractional scale gtk: support fractional surface scaling Sep 17, 2026
@jcollie

jcollie commented Sep 17, 2026

Copy link
Copy Markdown
Member

GTK is older than 4.12.

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 jcollie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's drop all of the comptime checks for GTK version. As stated in another comment, 4.18 is our baseline now.

@dmitry-salin

Copy link
Copy Markdown

In my case (2560 x 2880, Cosmic Wayland, 1.75x scaling), the text becomes blurrier after this change, though it is hard to notice in the screenshot. Terminal names were added over the original screenshots.

Before:
before

After:
after

@and-rs

and-rs commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

Thanks this is good testing. I will validate

@neobrain

neobrain commented Sep 17, 2026 •

Copy link
Copy Markdown

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:

before Screenshot_2026-09-17_16-37-10
after Screenshot_2026-09-17_16-37-03

@and-rs

and-rs commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

Got it. both use COSMIC so at this point I will debug it on COSMIC myself too and report findings

@and-rs

and-rs commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

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:

  • 175% on 2560×1600
  • GTK 4.22.4 / Adwaita 1.9.3
Please paste it like this after running the ghostty bin from the latest commit
λ ./zig-out/bin/ghostty
info: ghostty version=1.3.2-gtk-fractional-scale-+074fef6ba
info: ghostty build optimize=ReleaseSafe
info: runtime=.gtk
info: font_backend=.fontconfig_freetype
info: dependency harfbuzz=11.0.0
info: dependency fontconfig=21803
info: renderer=renderer.generic.Renderer(renderer.OpenGL)
info: libxev default backend=io_uring
info(os_locale): setlocale from env result=LC_CTYPE=en_US.UTF-8;LC_NUMERIC=es_CO.UTF-8;LC_TIME=es_CO.UTF-8;LC_COLLATE=en_US.UTF-8;LC_MONETARY=es_CO.UTF-8;LC_MESSAGES=en_US.UTF-8;LC_PAPER=es_CO.UTF-8;LC_NAME=es_CO.UTF-8;LC_ADDRESS=es_CO.UTF-8;LC_TELEPHONE=es_CO.UTF-8;LC_MEASUREMENT=es_CO.UTF-8;LC_IDENTIFICATION=es_CO.UTF-8
info(gtk): GTK version build=4.22.4 runtime=4.22.4
info(gtk): libadwaita version build=1.9.3 runtime=1.9.3
info: reading configuration file path=/home/and-rs/.config/ghostty/config.ghostty
info: reading configuration file path=/home/and-rs/.config/ghostty/fonts.ghostty
info: reading configuration file path=/home/and-rs/.config/ghostty/themes/current
warning(gtk_ghostty_application): setting GDK_DEBUG=
warning(gtk_ghostty_application): setting GDK_DISABLE=
warning(glib): WARNING: Adwaita: The resource style-dark.css is deprecated and shouldn't be used anymore. Use style.css with media queries instead.
warning(glib): WARNING: Adwaita: The resource style-hc.css is deprecated and shouldn't be used anymore. Use style.css with media queries instead.
warning(glib): WARNING: Adwaita: The resource style-hc-dark.css is deprecated and shouldn't be used anymore. Use style.css with media queries instead.
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GeForce RTX 3070 Ti Laptop GPU
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [2] Intel(R) Iris(R) Xe Graphics (ADL GT2)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GeForce RTX 3070 Ti Laptop GPU
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] Intel(R) Iris(R) Xe Graphics (ADL GT2)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [2] llvmpipe (LLVM 21.1.8, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GeForce RTX 3070 Ti Laptop GPU
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [2] Intel(R) Iris(R) Xe Graphics (ADL GT2)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GeForce RTX 3070 Ti Laptop GPU
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] Intel(R) Iris(R) Xe Graphics (ADL GT2)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [2] llvmpipe (LLVM 21.1.8, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GeForce RTX 3070 Ti Laptop GPU
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [2] Intel(R) Iris(R) Xe Graphics (ADL GT2)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GeForce RTX 3070 Ti Laptop GPU
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] Intel(R) Iris(R) Xe Graphics (ADL GT2)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [2] llvmpipe (LLVM 21.1.8, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GeForce RTX 3070 Ti Laptop GPU
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [2] Intel(R) Iris(R) Xe Graphics (ADL GT2)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GeForce RTX 3070 Ti Laptop GPU
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] Intel(R) Iris(R) Xe Graphics (ADL GT2)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [2] llvmpipe (LLVM 21.1.8, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_gfxstream.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_nouveau.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_panfrost.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_intel_hasvk.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_broadcom.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_freedreno.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_virtio.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_asahi.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_powervr_mesa.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_radeon.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Failed to find vkGetDeviceProcAddr in layer "/nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libVkLayer_MESA_device_select.so"
info(glib): INFO: Gdk: Vulkan: Loader Message: vkCreateDevice layer callstack setup to:
info(glib): INFO: Gdk: Vulkan: Loader Message:    <Application>
info(glib): INFO: Gdk: Vulkan: Loader Message:      ||
info(glib): INFO: Gdk: Vulkan: Loader Message:    <Loader>
info(glib): INFO: Gdk: Vulkan: Loader Message:      ||
info(glib): INFO: Gdk: Vulkan: Loader Message:    <Device>
info(glib): INFO: Gdk: Vulkan: Loader Message:        Using "Intel(R) Iris(R) Xe Graphics (ADL GT2)" with driver: "/nix/store/4cvv9wbvhz36a1fhd9px1rvjr8j61ycr-mesa-26.2.1/lib/libvulkan_intel.so"
info(gtk_render_surface): surface scale connected: css=0x0 device=0x0 surface=1.75 effective=1.75 surface_factor=2 widget_factor=2
info(gtk_ghostty_surface): surface content scale: x=1.75 y=1.75 dpi=168x168 font_size=12
info(font_shared_grid_set): font regular: Rec Mono Linear
info(font_shared_grid_set): font bold: Rec Mono Linear Bold
info(font_shared_grid_set): font italic: Rec Mono Casual Italic
info(font_shared_grid_set): font bold_italic: Rec Mono Casual Bold Italic
info(opengl): EGL vendor=Mesa Project
info(opengl): EGL extensions=EGL_ANDROID_blob_cache EGL_ANDROID_native_fence_sync EGL_CHROMIUM_sync_control EGL_ANGLE_sync_control_rate EGL_EXT_buffer_age EGL_EXT_config_select_group EGL_EXT_create_context_robustness EGL_EXT_image_dma_buf_import EGL_EXT_image_dma_buf_import_modifiers EGL_EXT_query_reset_notification_strategy EGL_EXT_surface_compression EGL_EXT_swap_buffers_with_damage EGL_IMG_context_priority EGL_KHR_cl_event2 EGL_KHR_config_attribs EGL_KHR_context_flush_control EGL_KHR_create_context EGL_KHR_create_context_no_error EGL_KHR_fence_sync EGL_KHR_get_all_proc_addresses EGL_KHR_gl_colorspace EGL_KHR_gl_renderbuffer_image EGL_KHR_gl_texture_2D_image EGL_KHR_gl_texture_3D_image EGL_KHR_gl_texture_cubemap_image EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap EGL_KHR_no_config_context EGL_KHR_partial_update EGL_KHR_reusable_sync EGL_KHR_surfaceless_context EGL_KHR_swap_buffers_with_damage EGL_EXT_pixel_format_float EGL_KHR_wait_sync EGL_MESA_configless_context EGL_MESA_gl_interop EGL_MESA_image_dma_buf_export EGL_MESA_query_driver EGL_MESA_x11_native_visual_id EGL_NOK_texture_from_pixmap
info(io_exec): found Ghostty resources dir: /nix/store/qjjgd1n16sv9ilx0a3iwjhq2p829i8sh-ghostty-1.3.2-dev+8facc9a-nix/share/ghostty
info(io_exec): shell integration automatically injected shell=.nushell
warning(gtk_ghostty_application): unimplemented action=.cell_size
info(gtk_render_surface): surface scale allocated: css=719x754 device=1259x1320 surface=1.75 effective=1.75 surface_factor=2 widget_factor=2
info(io_exec): started subcommand path=/bin/sh pid=19453
info(opengl): loaded OpenGL 4.6
info(io_exec): pty fd closed, read thread exiting
info(surface): surface closed id=8773d1a61b22a2c2
Screenshots comparing with alacritty. I could not see blurry box characters. Perhaps just difference in font rendering? But not like the screenshots above comparison comparison2

@and-rs

and-rs commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

ok I have also tested x11 i3

log results:

surface=1 effective=1 surface_factor=1 widget_factor=1
css=1280x1552 device=1280x1552
content scale=1.5 dpi=144
  • so i3 provides no fractional GDK surface scale.
  • The 1.5 scale is only GTK’s gtk-xft-dpi font-DPI scaling, which ghostty applies to font metrics. buffer size would still be 1:1
  • i'd say this is expected and different to COSMIC’s: surface=1.75 effective=1.75 surface_factor=2

This is not to say that it wouldn't be great for someone else testing x11 c:

2026-09-17_19-20-37

@dmitry-salin

dmitry-salin commented Sep 18, 2026 •

Copy link
Copy Markdown

My distro includes GTK version 4.14.5 — that might be the reason.

Log
❯ ./zig-out/bin/ghostty
info: ghostty version=1.3.2-HEAD-+3888c7e31
info: ghostty build optimize=ReleaseSafe
info: runtime=.gtk
info: font_backend=.fontconfig_freetype
info: dependency harfbuzz=11.0.0
info: dependency fontconfig=21803
info: renderer=renderer.generic.Renderer(renderer.OpenGL)
info: libxev default backend=io_uring
info(os_locale): setlocale from env result=LC_CTYPE=en_US.UTF-8;LC_NUMERIC=ru_RU.UTF-8;LC_TIME=ru_RU.UTF-8;LC_COLLATE=en_US.UTF-8;LC_MONETARY=ru_RU.UTF-8;LC_MESSAGES=en_US.UTF-8;LC_PAPER=ru_RU.UTF-8;LC_NAME=ru_RU.UTF-8;LC_ADDRESS=ru_RU.UTF-8;LC_TELEPHONE=ru_RU.UTF-8;LC_MEASUREMENT=ru_RU.UTF-8;LC_IDENTIFICATION=ru_RU.UTF-8
info(gtk): GTK version build=4.14.5 runtime=4.14.5
info(gtk): libadwaita version build=1.5.0 runtime=1.5.0
info: reading configuration file path=/home/dmitry/.config/ghostty/config.ghostty
info(config): default shell src=passwd value=/bin/bash
info(config): default working directory src=passwd value=/home/dmitry
warning(gtk_ghostty_application): setting GDK_DEBUG=gl-no-fractional
warning(gtk_ghostty_application): setting GDK_DISABLE=color-mgmt
warning(glib): WARNING: Gtk: Theme parser error: style.css:140:17-19: Unexpected data at end of hsl() argument
warning(glib): WARNING: Gtk: Theme parser error: style.css:164:5-8: Expected a valid color.
warning(glib): WARNING: Gtk: Theme parser error: style.css:174:5-8: Expected a valid color.
warning(glib): WARNING: Gtk: Theme parser error: style.css:184:5-8: Expected a valid color.
warning(glib): WARNING: Gtk: Theme parser error: style.css:194:5-8: Expected a valid color.
info(gtk_ghostty_application): libxev manual backend=io_uring
info(gtk_render_surface): surface scale connected: css=0x0 device=0x0 surface=1.75 effective=1.75 surface_factor=2 widget_factor=2
info(gtk_ghostty_surface): surface content scale: x=1.75 y=1.75 dpi=168x168 font_size=9.25
info(font_shared_grid_set): font regular: PragmataPro Mono Regular
info(font_shared_grid_set): font bold: PragmataPro Mono Bold
info(font_shared_grid_set): font italic: PragmataPro Mono Italic
info(font_shared_grid_set): font bold_italic: PragmataPro Mono Bold Italic
info(opengl): EGL vendor=Mesa Project
info(opengl): EGL extensions=EGL_ANDROID_blob_cache EGL_ANDROID_native_fence_sync EGL_CHROMIUM_sync_control EGL_ANGLE_sync_control_rate EGL_EXT_buffer_age EGL_EXT_config_select_group EGL_EXT_create_context_robustness EGL_EXT_image_dma_buf_import EGL_EXT_image_dma_buf_import_modifiers EGL_EXT_protected_surface EGL_EXT_query_reset_notification_strategy EGL_EXT_surface_compression EGL_EXT_swap_buffers_with_damage EGL_IMG_context_priority EGL_KHR_cl_event2 EGL_KHR_config_attribs EGL_KHR_context_flush_control EGL_KHR_create_context EGL_KHR_create_context_no_error EGL_KHR_fence_sync EGL_KHR_get_all_proc_addresses EGL_KHR_gl_colorspace EGL_KHR_gl_renderbuffer_image EGL_KHR_gl_texture_2D_image EGL_KHR_gl_texture_3D_image EGL_KHR_gl_texture_cubemap_image EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap EGL_KHR_no_config_context EGL_KHR_reusable_sync EGL_KHR_surfaceless_context EGL_KHR_swap_buffers_with_damage EGL_EXT_pixel_format_float EGL_KHR_wait_sync EGL_MESA_configless_context EGL_MESA_gl_interop EGL_MESA_image_dma_buf_export EGL_MESA_query_driver EGL_MESA_x11_native_visual_id EGL_NOK_texture_from_pixmap
info(io_exec): found Ghostty resources dir: /home/dmitry/git/ghostty/zig-out/share/ghostty
info(io_exec): shell integration automatically injected shell=.bash
warning(gtk_ghostty_application): unimplemented action=.cell_size
info(gtk_render_surface): surface scale allocated: css=1463x1452 device=2561x2541 surface=1.75 effective=1.75 surface_factor=2 widget_factor=2
info(opengl): loaded OpenGL 4.6
info(io_exec): started subcommand path=/bin/sh pid=209464
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] llvmpipe (LLVM 20.1.2, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] AMD Radeon 780M Graphics (RADV PHOENIX)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] AMD Radeon 780M Graphics (RADV PHOENIX)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 20.1.2, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] llvmpipe (LLVM 20.1.2, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] AMD Radeon 780M Graphics (RADV PHOENIX)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] AMD Radeon 780M Graphics (RADV PHOENIX)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 20.1.2, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] llvmpipe (LLVM 20.1.2, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] AMD Radeon 780M Graphics (RADV PHOENIX)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] AMD Radeon 780M Graphics (RADV PHOENIX)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 20.1.2, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] llvmpipe (LLVM 20.1.2, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] AMD Radeon 780M Graphics (RADV PHOENIX)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] AMD Radeon 780M Graphics (RADV PHOENIX)
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 20.1.2, 256 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: Failed to find vkGetDeviceProcAddr in layer "libVkLayer_MESA_device_select.so"
info(glib): INFO: Gdk: Vulkan: Loader Message: vkCreateDevice layer callstack setup to:
info(glib): INFO: Gdk: Vulkan: Loader Message:    <Application>
info(glib): INFO: Gdk: Vulkan: Loader Message:      ||
info(glib): INFO: Gdk: Vulkan: Loader Message:    <Loader>
info(glib): INFO: Gdk: Vulkan: Loader Message:      ||
info(glib): INFO: Gdk: Vulkan: Loader Message:    <Device>
info(glib): INFO: Gdk: Vulkan: Loader Message:        Using "AMD Radeon 780M Graphics (RADV PHOENIX)" with driver: "libvulkan_radeon.so"
info(io_exec): pty fd closed, read thread exiting
info(surface): surface closed id=9ed1b45acc1e10c1

@neobrain

Copy link
Copy Markdown
Logs with 1.25 scale
~> EGL_PLATFORM=wayland nix run github:ghostty-org/ghostty/3888c7e
info: ghostty version=1.3.2-dev+3888c7e-nix
info: ghostty build optimize=ReleaseFast
info: runtime=.gtk
info: font_backend=.fontconfig_freetype
info: dependency harfbuzz=13.2.1
info: dependency fontconfig=21802
info: renderer=renderer.generic.Renderer(renderer.OpenGL)
info: libxev default backend=io_uring
info(os_locale): setlocale from env result=en_GB.UTF-8
info(gtk): GTK version build=4.22.4 runtime=4.22.4
info(gtk): libadwaita version build=1.9.3 runtime=1.9.3
warning: error reading optional config file, not loading err=error.FileIsEmpty path=/home/tony/.config/ghostty/config.ghostty
info(config): default shell source=env value=/run/current-system/sw/bin/nu
warning(gtk_ghostty_application): setting GDK_DEBUG=
warning(gtk_ghostty_application): setting GDK_DISABLE=
warning(glib): WARNING: Adwaita: The resource style-dark.css is deprecated and shouldn't be used anymore. Use style.css with media queries instead.
warning(glib): WARNING: Adwaita: The resource style-hc.css is deprecated and shouldn't be used anymore. Use style.css with media queries instead.
warning(glib): WARNING: Adwaita: The resource style-hc-dark.css is deprecated and shouldn't be used anymore. Use style.css with media queries instead.
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GB10
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 128 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GB10
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 128 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GB10
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 128 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GB10
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 128 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GB10
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 128 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GB10
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 128 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: linux_read_sorted_physical_devices:
info(glib): INFO: Gdk: Vulkan: Loader Message:      Original order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GB10
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 128 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message:      Sorted order:
info(glib): INFO: Gdk: Vulkan: Loader Message:            [0] NVIDIA GB10
info(glib): INFO: Gdk: Vulkan: Loader Message:            [1] llvmpipe (LLVM 21.1.8, 128 bits)
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_virtio.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_radeon.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_powervr_mesa.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_panfrost.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_nouveau.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_intel.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_intel_hasvk.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_gfxstream.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_freedreno.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_broadcom.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Removing driver /nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libvulkan_asahi.so due to not having any physical devices
info(glib): INFO: Gdk: Vulkan: Loader Message: Failed to find vkGetDeviceProcAddr in layer "/nix/store/phwxjr6v2y17qi317wghmkiq133i1glf-mesa-26.2.2/lib/libVkLayer_MESA_device_select.so"
info(glib): INFO: Gdk: Vulkan: Loader Message: vkCreateDevice layer callstack setup to:
info(glib): INFO: Gdk: Vulkan: Loader Message:    <Application>
info(glib): INFO: Gdk: Vulkan: Loader Message:      ||
info(glib): INFO: Gdk: Vulkan: Loader Message:    <Loader>
info(glib): INFO: Gdk: Vulkan: Loader Message:      ||
info(glib): INFO: Gdk: Vulkan: Loader Message:    <Device>
info(glib): INFO: Gdk: Vulkan: Loader Message:        Using "NVIDIA GB10" with driver: "/nix/store/z7lxknccgcd6023vy0zvw5kw909wz3h4-nvidia-x11-595.99.02/lib/libGLX_nvidia.so.595.99.02"
info(gtk_render_surface): surface scale connected: css=0x0 device=0x0 surface=1.25 effective=1.25 surface_factor=2 widget_factor=2
info(gtk_ghostty_surface): surface content scale: x=1.25 y=1.25 dpi=120x120 font_size=12
info(opengl): EGL vendor=NVIDIA
info(opengl): EGL extensions=EGL_ANDROID_native_fence_sync EGL_EXT_buffer_age EGL_EXT_client_sync EGL_EXT_create_context_robustness EGL_EXT_image_dma_buf_import EGL_EXT_image_dma_buf_import_modifiers EGL_MESA_image_dma_buf_export EGL_EXT_output_base EGL_EXT_output_drm EGL_EXT_protected_content EGL_EXT_stream_consumer_egloutput EGL_EXT_stream_acquire_mode EGL_EXT_sync_reuse EGL_IMG_context_priority EGL_KHR_config_attribs EGL_KHR_create_context_no_error EGL_KHR_context_flush_control EGL_KHR_create_context EGL_KHR_fence_sync EGL_KHR_get_all_proc_addresses EGL_KHR_partial_update EGL_KHR_swap_buffers_with_damage EGL_KHR_no_config_context EGL_KHR_gl_colorspace EGL_KHR_gl_renderbuffer_image EGL_KHR_gl_texture_2D_image EGL_KHR_gl_texture_3D_image EGL_KHR_gl_texture_cubemap_image EGL_KHR_image EGL_KHR_image_base EGL_KHR_reusable_sync EGL_KHR_stream EGL_KHR_stream_attrib EGL_KHR_stream_consumer_gltexture EGL_KHR_stream_cross_process_fd EGL_KHR_stream_fifo EGL_KHR_stream_producer_eglsurface EGL_KHR_surfaceless_context EGL_KHR_wait_sync EGL_NV_nvrm_fence_sync EGL_NV_quadruple_buffer EGL_NV_stream_consumer_eglimage EGL_NV_stream_cross_display EGL_NV_stream_cross_object EGL_NV_stream_cross_process EGL_NV_stream_cross_system EGL_NV_stream_dma EGL_NV_stream_flush EGL_NV_stream_metadata EGL_NV_stream_remote EGL_NV_stream_reset EGL_NV_stream_socket EGL_NV_stream_socket_inet EGL_NV_stream_socket_unix EGL_NV_stream_sync EGL_NV_stream_fifo_next EGL_NV_stream_fifo_synchronous EGL_NV_stream_consumer_gltexture_yuv EGL_NV_stream_attrib EGL_NV_stream_origin EGL_NV_system_time EGL_NV_output_drm_flip_event EGL_NV_triple_buffer EGL_NV_robustness_video_memory_purge EGL_WL_bind_wayland_display EGL_WL_wayland_eglstream EGL_EXT_present_opaque
info(io_exec): found Ghostty resources dir: /nix/store/s7blm89k8bjpnyiy1vcip33wxa2n6ncd-ghostty-1.3.1/share/ghostty
info(io_exec): shell integration automatically injected shell=.nushell
warning(gtk_ghostty_application): unimplemented action=.cell_size
info(gtk_render_surface): surface scale allocated: css=762x778 device=953x973 surface=1.25 effective=1.25 surface_factor=2 widget_factor=2
info(io_exec): started subcommand path=/bin/sh pid=6262
info(opengl): loaded OpenGL 4.3
info(page_list): adjusting page capacity=.{ .cols = 78, .rows = 593, .styles = 128, .hyperlink_bytes = 384, .grapheme_bytes = 8192, .string_bytes = 2048 }
info(page_list): adjusting page capacity=.{ .cols = 78, .rows = 593, .styles = 128, .hyperlink_bytes = 768, .grapheme_bytes = 8192, .string_bytes = 2048 }
info(page_list): adjusting page capacity=.{ .cols = 78, .rows = 593, .styles = 128, .hyperlink_bytes = 1536, .grapheme_bytes = 8192, .string_bytes = 2048 }
info(page_list): adjusting page capacity=.{ .cols = 78, .rows = 593, .styles = 128, .hyperlink_bytes = 3072, .grapheme_bytes = 8192, .string_bytes = 2048 }
info(io_exec): pty fd closed, read thread exiting
info(surface): surface closed id=a3430dfece6bfbc2

@neobrain

neobrain commented Sep 18, 2026 •

Copy link
Copy Markdown

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 recursive to fonts.packages and added font-family = Rec Mono Linear to config.ghostty, but it's equally washed out with the PR.

@neobrain

neobrain commented Sep 18, 2026 •

Copy link
Copy Markdown

I've also tried in GNOME now and adding a few non-ghostty apps for reference (all configured for the Rec Mono Linear font @ 12pt):

Top-left: Konsole
Top-Right: Firefox
Bottom-left: Ghostty 1.3.1
Bottom-right: This PR

Hard to say if any of the first three is markedly better (especially with diverging colors), but the current PR is a clear regression :(

Screenshot From 2026-09-18 10-31-23

@and-rs

and-rs commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

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

@adwaymalhotra

adwaymalhotra commented Sep 18, 2026 •

Copy link
Copy Markdown

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?

@and-rs

and-rs commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

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

@and-rs

and-rs commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

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

@neobrain

neobrain commented Sep 20, 2026 •

Copy link
Copy Markdown

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.

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")

Screenshot_2026-09-20_17-26-30

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:

  • the UTF-8 border characters are no longer blurry and instead look uniform across all edges
  • the two consecutive "m"s in "common-updater" no longer are rendered differently
  • the "Sep" text in the time stamps is much more readable now
  • the "1" in the "19:xx" time stamps is no longer washed out
  • the "misc" text (line 10) was previously very washed out, making its overall font weight appear lower

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.

@pluiedev

pluiedev commented Sep 20, 2026 •

Copy link
Copy Markdown
Member

Hmm. Looks like this patch made things blurrier on my end. You can see that the edges of each character seems to more smoothed out and less clear cut. This is especially noticable for glyphs like %. Rendering on 1600x900, 133.33%
after

Versus before this PR:
before

@and-rs

and-rs commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

Yes, I think it's noticeable in the screenshots. I will validate. could you please share logs? just in case

@pluiedev

pluiedev commented Sep 20, 2026 •

Copy link
Copy Markdown
Member
Relevant logs
debug(app): mailbox message=surface_message
info(gtk_render_surface): surface scale changed: css=823x471 device=1098x628 surface=1.3333333333333333 effective=1.3333333333333333 surface_factor=2 widget_factor=2
debug(gtk_ghostty_surface): gl resize width=1098 height=628 scale=1.3333333333333333 scale_factor=2
debug(surface): set font size size=10
debug(font_shared_grid_set): initializing new grid for font config
info(font_shared_grid_set): font regular: Iosevka
info(font_shared_grid_set): font regular: Iosevka
info(font_shared_grid_set): font bold: Iosevka Bold
info(font_shared_grid_set): font bold: Iosevka Bold
info(font_shared_grid_set): font italic: Iosevka Italic
info(font_shared_grid_set): font italic: Iosevka Italic
info(font_shared_grid_set): font bold_italic: Iosevka Bold Italic
info(font_shared_grid_set): font bold_italic: Iosevka Bold Italic
debug(font_face): variation axes font=JetBrains Mono
debug(font_face): variation axis: name=Weight id=wght min=100 max=800 def=400
debug(font_face): variation axes font=JetBrains Mono
debug(font_face): variation axis: name=Weight id=wght min=100 max=800 def=400
debug(font_face): variation axes font=JetBrains Mono
debug(font_face): variation axis: name=Weight id=wght min=100 max=800 def=400
debug(font_face): variation axes font=JetBrains Mono
debug(font_face): variation axis: name=Weight id=wght min=100 max=800 def=400
warning(gtk_ghostty_application): unimplemented action=.cell_size
debug(io_thread): mailbox message=resize
debug(io_thread): mailbox message=resize
debug(io_thread): mailbox message=resize
debug(renderer_thread): mailbox message=.{ .font_grid = .{ .grid = .{ .codepoints = .{ ... }, .glyphs = .{ ... }, .atlas_grayscale = .{ ... }, .atlas_color = .{ ... }, .resolver = .{ ... }, .metrics = .{ ... }, .lock = .{ ... } }, .set = .{ .alloc = .{ ... }, .map = .{ ... }, .font_lib = .{ ... }, .font_discover = .{ ... }, .lock = .{ ... } }, .new_key = .{ .arena = .{ ... }, .descriptors = { ... }, .style_offsets = { ... }, .codepoint_map = .{ ... }, .metric_modifiers = .{ ... }, .font_size = .{ ... }, .freetype_load_flags = .{ ... } }, .old_key = .{ .arena = .{ ... }, .descriptors = { ... }, .style_offsets = { ... }, .codepoint_map = .{ ... }, .metric_modifiers = .{ ... }, .font_size = .{ ... }, .freetype_load_flags = .{ ... } } } }
Resizing EGL window to 1164 819
Resizing EGL window to 1164 759
debug(gtk_ghostty_surface): gl resize width=1098 height=568 scale=1.3333333333333333 scale_factor=2
debug(io_thread): mailbox message=resize
debug(renderer_thread): mailbox message=.{ .resize = .{ .screen = .{ .width = 1029, .height = 589 }, .cell = .{ .width = 9, .height = 22 }, .padding = .{ .top = 3, .bottom = 3, .right = 3, .left = 3 } } }
debug(generic_renderer): screen size size=.{ .screen = .{ .width = 1029, .height = 589 }, .cell = .{ .width = 9, .height = 22 }, .padding = .{ .top = 3, .bottom = 3, .right = 3, .left = 3 } }
debug(renderer_thread): mailbox message=.{ .resize = .{ .screen = .{ .width = 1098, .height = 628 }, .cell = .{ .width = 9, .height = 22 }, .padding = .{ .top = 3, .bottom = 3, .right = 3, .left = 3 } } }
debug(generic_renderer): screen size size=.{ .screen = .{ .width = 1098, .height = 628 }, .cell = .{ .width = 9, .height = 22 }, .padding = .{ .top = 3, .bottom = 3, .right = 3, .left = 3 } }
debug(renderer_thread): mailbox message=.{ .resize = .{ .screen = .{ .width = 1098, .height = 568 }, .cell = .{ .width = 9, .height = 22 }, .padding = .{ .top = 3, .bottom = 3, .right = 3, .left = 3 } } }
debug(generic_renderer): screen size size=.{ .screen = .{ .width = 1098, .height = 568 }, .cell = .{ .width = 9, .height = 22 }, .padding = .{ .top = 3, .bottom = 3, .right = 3, .left = 3 } }
debug(app): mailbox message=surface_message
debug(io_thread): mailbox message=write_small
debug(io_thread): mailbox message=write_stable
debug(io_handler): setting kitty keyboard mode: set .{ .disambiguate = false, .report_events = false, .report_alternates = false, .report_all = false, .report_associated = false }
debug(renderer_thread): mailbox message=.{ .resize = .{ .screen = .{ .width = 1098, .height = 568 }, .cell = .{ .width = 9, .height = 22 }, .padding = .{ .top = 3, .bottom = 3, .right = 3, .left = 3 } } }
debug(generic_renderer): screen size size=.{ .screen = .{ .width = 1098, .height = 568 }, .cell = .{ .width = 9, .height = 22 }, .padding = .{ .top = 3, .bottom = 3, .right = 3, .left = 3 } }
debug(renderer_thread): mailbox message=.{ .reset_cursor_blink = void }
debug(app): mailbox message=surface_message
debug(surface): changing title "~"
debug(io_handler): setting kitty keyboard mode: set .{ .disambiguate = true, .report_events = false, .report_alternates = true, .report_all = false, .report_associated = false }

@and-rs
and-rs force-pushed the gtk-fractional-scale branch from 1f49b0c to 7f374da Compare September 20, 2026 17:36
@and-rs

and-rs commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

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

@neobrain

Copy link
Copy Markdown

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

Copy link
Copy Markdown
Member

Oh that is much better. (Now 1440p, 1.5x)

AfterBefore
image image

@pluiedev pluiedev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@dmitry-salin

Copy link
Copy Markdown

Looks good now

Before:
before

After:
After

@jcollie jcollie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing my block for the GTK versioning. I have not run it myself so I won't merge without the go-ahead from @pluiedev .

@jcollie

jcollie commented Sep 21, 2026

Copy link
Copy Markdown
Member

@and-rs can you rebase on the latest main?

@and-rs

and-rs commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

@jcollie done i think

@jcollie

jcollie commented Sep 21, 2026

Copy link
Copy Markdown
Member

@and-rs not quite. it's simpler than most people think:

git remote add ghostty https://github.com/ghostty-org/ghostty # add a remote called 'ghostty' to fetch upstream
git fetch ghostty # make sure that you have the latest from ghostty
git switch gtk-fractional-scale
git rebase ghostty/main # you'll need to fix up conflicts here
git push --force

@and-rs
and-rs force-pushed the gtk-fractional-scale branch from 976967d to d1a2fed Compare September 21, 2026 15:00
@and-rs

and-rs commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

ahh got it thanks. I wasn't sure. what about now?

@jcollie

jcollie commented Sep 21, 2026

Copy link
Copy Markdown
Member

ahh got it thanks. I wasn't sure. what about now?

looks good.

@pluiedev pluiedev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 :shipit:

Comment thread src/apprt/gtk/class/render_surface.zig Outdated
const surface = native.getSurface() orelse return;
const layout = scale_util.widgetLayoutForSize(widget, css_width, css_height);

log.info(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be log.debug IMO

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix

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
@and-rs
and-rs force-pushed the gtk-fractional-scale branch from 14d20a6 to e1916c1 Compare September 24, 2026 03:22
@and-rs

and-rs commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

@pluiedev I have demoted log. and I'd like to discuss more refinements in future PRs for sure. ty

@mitchellh mitchellh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One question.

Comment thread src/Surface.zig
.points = config.@"font-size",
.xdpi = @intFromFloat(x_dpi),
.ydpi = @intFromFloat(y_dpi),
.xdpi = @intFromFloat(@round(x_dpi)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@mitchellh
mitchellh merged commit 56dbc4a into ghostty-org:main Sep 25, 2026
102 checks passed
@github-actions github-actions Bot added this to the 1.4.0 milestone Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants