Skip to content

fix: render Ghostty surface at GLArea FBO size on fractional scales#83

Open
manuacl wants to merge 1 commit into
am-will:mainfrom
manuacl:fix/fractional-scale-glarea
Open

fix: render Ghostty surface at GLArea FBO size on fractional scales#83
manuacl wants to merge 1 commit into
am-will:mainfrom
manuacl:fix/fractional-scale-glarea

Conversation

@manuacl

@manuacl manuacl commented May 22, 2026

Copy link
Copy Markdown

Summary

Fixes the terminal area rendering into the bottom-left sub-rectangle of the GLArea on Wayland fractional-scale outputs (1.25, 1.5, …). Keystrokes were accepted but invisible because they were drawn outside the visible region.

Root cause and reproduction documented in #82.

Fix

refresh_surface_display used to pass:

  • set_size(w, h) with w, h = GLArea logical allocation (CSS pixels)
  • set_content_scale(scale, scale) with scale = gl_area.scale_factor() (integer)

GTK4 actually allocates the FBO at logical_size × scale_factor() physical pixels and Ghostty treats set_size as the physical-pixel canvas, so the renderer drew into a sub-rectangle of the oversized FBO.

This PR passes the physical-pixel canvas to set_size (logical × scale_factor) and keeps content_scale at the integer scale so font/cell metrics match the compositor's downscale path. At integer scales (1.0, 2.0) the behaviour is unchanged.

Behavior changes

  • surface-health.width_px / surface-health.height_px exposed via the control bridge now report physical pixels instead of logical ones, because they are sourced from ghostty_surface_size() and set_size is now physical. External consumers (cmux, automation scripts) that compare these against GTK allocations or previous expected values will see numbers scaled by scale_factor() on HiDPI displays.

Test plan

  • ./scripts/check.sh — passes (only the known baseline test failure from CLAUDE.md is unaffected)
  • Plasma 6 Wayland @ 1.0: terminal renders correctly (regression check)
  • Plasma 6 Wayland @ 1.25: terminal fills the GLArea, text at the right size, typing visible, mouse selection accurate
  • Plasma 6 Wayland @ 1.5: same as above
  • Window resize at fractional scale: rendering follows
  • Dynamic scale switch (Plasma display settings) while Limux is open: rendering adapts
  • Cross-monitor drag (1.0 ↔ 1.25 extended desktop): rendering stays correct on both screens, no stale scale

Tested on Bazzite Fedora 44, NVIDIA open 595.71.05, GTK 4.22.4 system + bundled libghostty.

Closes #82

Before fix

Image

After fix

Image

manuacl added a commit to manuacl/limux that referenced this pull request May 24, 2026
manuacl added a commit to manuacl/limux that referenced this pull request May 25, 2026
@manuacl manuacl force-pushed the fix/fractional-scale-glarea branch from d85bf4a to 73e6cd6 Compare May 25, 2026 14:55
GTK4 allocates the GLArea backing FBO at `logical_size * scale_factor()`
physical pixels. On Wayland fractional-scale outputs (1.25, 1.5, ...),
the compositor downscales that integer-scaled buffer to the fractional
surface size via wp-fractional-scale-v1.

`refresh_surface_display` was passing the logical CSS allocation as
Ghostty's canvas size, with content_scale set to the integer device
scale factor. Ghostty's renderer treats `set_size` as the physical-
pixel canvas and `set_content_scale` as the HiDPI font/cell-metrics
density, so it ended up drawing into the bottom-left sub-rectangle of
an oversized FBO. Visible symptom: terminal area occupies ~62% (at
1.25) or ~44% (at 1.5) of the pane, the rest is black, keystrokes
are consumed but rendered off-screen.

Pass the physical-pixel canvas to `ghostty_surface_set_size` and keep
`content_scale` at the integer scale factor so font metrics still
match the compositor's downscale path. At integer scales (1.0, 2.0)
the behaviour is unchanged.

Extract the conversion as a pure helper `physical_size_for_scale`
with a regression test pinning the invariant (logical × scale,
including zero-dim edge cases). Update the initial-sizing comment to
point at the helper and drop the redundant width/height gating
(refresh_surface_display already checks > 0 internally). Add a
CLAUDE.md Pitfalls entry on the GTK ↔ Ghostty pixel-unit boundary
so future contributors touching the GLArea sizing path see the
gotcha.

Closes am-will#82

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@manuacl manuacl force-pushed the fix/fractional-scale-glarea branch from 73e6cd6 to 66c002b Compare May 26, 2026 08:04
@xcastdev

Copy link
Copy Markdown

Tested on Hyprland + GTK 4.22 (1.25× scaling), fix works. Thanks!

@jpnurmi

jpnurmi commented May 31, 2026

Copy link
Copy Markdown

Thanks for the fix! 👍

Before After
Screenshot From 2026-05-31 08-59-06 Screenshot From 2026-05-31 09-00-17
interface: 'wl_output',                                  version:  4, name:  3
        name: eDP-1
        description: Built-in display
        x: 0, y: 0, scale: 2,
        physical_width: 290 mm, physical_height: 190 mm,
        make: 'BOE', model: 'NE135A1M-NY1',
        subpixel_orientation: unknown, output_transform: normal,
        mode:
                width: 2880 px, height: 1920 px, refresh: 120.000 Hz,
                flags: current preferred
interface: 'zxdg_output_manager_v1',                     version:  3, name:  4
        xdg_output_v1
                output: 3
                name: 'eDP-1'
                description: 'Built-in display'
                logical_x: 0, logical_y: 0
                logical_width: 1920, logical_height: 1280

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.

Terminal area renders clipped to a sub-rectangle on Wayland fractional scaling (1.25, 1.5, …)

3 participants