From 97f57edccc10cb5ccef34d9d4c94276748bbd953 Mon Sep 17 00:00:00 2001 From: Jesse Miller Date: Sat, 29 Aug 2026 08:14:36 -0600 Subject: [PATCH 1/7] renderer: vsync unfocused surfaces while dirty 6ae1784f4 Unfocused surfaces stopped the CVDisplayLink and encoded a GPU frame on every PTY wakeup. A burst of close writes became that many Metal submits instead of one vsync. Keep the link running while the surface is visible and dirty or animating, whether or not it is focused. Idle surfaces still park. Focus continues to gate cursor blink, custom-shader animation, and QoS. --- src/renderer/generic.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/renderer/generic.zig b/src/renderer/generic.zig index 2c9fb1093..2a81f42b4 100644 --- a/src/renderer/generic.zig +++ b/src/renderer/generic.zig @@ -1194,8 +1194,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type { const should_run = // Non-visible windows never vsync self.visible and - // Non-focused windows only render on-demand - self.focused and // Only vsync if we have cell changes or animation (self.cells_rebuilt or self.animationWake() != null); From 166d2fe34d65bd1fa393fd8a213c57bc6119dfb9 Mon Sep 17 00:00:00 2001 From: svector-anu Date: Sat, 29 Aug 2026 16:17:24 +0100 Subject: [PATCH 2/7] build: update Sparkle to 2.9.4 --- .github/workflows/release-tag.yml | 4 ++-- .github/workflows/release-tip.yml | 6 +++--- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index f7b076bf0..60a2e80e9 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -160,7 +160,7 @@ jobs: - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.4 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -328,7 +328,7 @@ jobs: - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.4 run: | mkdir -p .action/sparkle cd .action/sparkle diff --git a/.github/workflows/release-tip.yml b/.github/workflows/release-tip.yml index 5cda284bb..d4e6b7162 100644 --- a/.github/workflows/release-tip.yml +++ b/.github/workflows/release-tip.yml @@ -591,7 +591,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.4 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -847,7 +847,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.4 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -1044,7 +1044,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.4 run: | mkdir -p .action/sparkle cd .action/sparkle diff --git a/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6e450d9bc..feb7b4ba0 100644 --- a/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/sparkle-project/Sparkle", "state" : { - "revision" : "21d8df80440b1ca3b65fa82e40782f1e5a9e6ba2", - "version" : "2.9.0" + "revision" : "b6496a74a087257ef5e6da1c5b29a447a60f5bd7", + "version" : "2.9.4" } } ], From 98cd670c0c2ccdd3f22c40c65a3306e933643ada Mon Sep 17 00:00:00 2001 From: "ghostty-vouch[bot]" <262049992+ghostty-vouch[bot]@users.noreply.github.com> Date: Sun, 30 Aug 2026 02:40:12 +0000 Subject: [PATCH 3/7] Update VOUCHED list (#14079) Triggered by [discussion comment](https://github.com/ghostty-org/ghostty/discussions/14078#discussioncomment-18203195) from @jcollie. Vouch: @and-rs Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .github/VOUCHED.td | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td index 423517a15..387df2f1c 100644 --- a/.github/VOUCHED.td +++ b/.github/VOUCHED.td @@ -46,6 +46,7 @@ alexfeijoo44 alexjuca alosarjos amadeus +and-rs andrejdaskalov anhthang anmitalidev From 090fca451d2c63bc2a5ccec23ea54cedce62c6a6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 29 Aug 2026 19:53:11 -0700 Subject: [PATCH 4/7] terminal/kitty: validate POSIX shared memory names Update shared memory name validation according to the new spec: https://github.com/kovidgoyal/kitty/commit/22042970cf3a4668d02a1a7bcccca778ec864c21 --- src/terminal/kitty/graphics_image.zig | 66 +++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index 489b672e3..945a127db 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -210,6 +210,15 @@ pub const LoadingImage = struct { return error.UnsupportedMedium; } + // POSIX shared memory names must begin with a slash, contain at + // least one character after it, contain no other slashes, and fit + // within NAME_MAX. Some shm_open implementations accept names + // without the leading slash, but the Kitty protocol does not. + if (!validSharedMemoryName(path, posix.NAME_MAX)) { + log.warn("invalid shared memory name", .{}); + return error.InvalidData; + } + // Since we're only supporting posix then max_path_bytes should // be enough to stack allocate the path. var buf: [std.fs.max_path_bytes]u8 = undefined; @@ -831,6 +840,16 @@ pub const Rect = struct { } }; +/// Returns whether a name follows the POSIX shared memory name format. +fn validSharedMemoryName(name: []const u8, name_max: usize) bool { + if (name.len < 2 or name.len > name_max or name[0] != '/') return false; + for (name[1..]) |c| { + if (c == '/' or c == 0) return false; + } + + return true; +} + /// Returns true if `path` is `dir` or is contained within it, requiring a /// path-separator boundary so similarly prefixed directories do not match. fn isPathInDir(dir: []const u8, path: []const u8) bool { @@ -852,6 +871,53 @@ test "temporary file path must be inside directory" { try testing.expect(!isPathInDir("/custom/tmp", "/custom/tmp-suffix/tty-graphics-protocol-image.data")); } +test "shared memory names follow POSIX rules" { + const testing = std.testing; + + try testing.expect(validSharedMemoryName("/kitty", 8)); + try testing.expect(validSharedMemoryName("/1234567", 8)); + + try testing.expect(!validSharedMemoryName("", 8)); + try testing.expect(!validSharedMemoryName("/", 8)); + try testing.expect(!validSharedMemoryName("kitty", 8)); + try testing.expect(!validSharedMemoryName("/kitty/image", 16)); + try testing.expect(!validSharedMemoryName("/kitty\x00image", 16)); + try testing.expect(!validSharedMemoryName("/12345678", 8)); +} + +test "image load rejects invalid POSIX shared memory names" { + if (comptime builtin.abi.isAndroid() or + builtin.target.os.tag == .windows or + !builtin.link_libc) + { + return error.SkipZigTest; + } + + const testing = std.testing; + const alloc = testing.allocator; + + var cmd: command.Command = .{ + .control = .{ .transmit = .{ + .format = .rgb, + .medium = .shared_memory, + .width = 1, + .height = 1, + .image_id = 31, + } }, + .data = try alloc.dupe(u8, "kitty-without-leading-slash"), + }; + defer cmd.deinit(alloc); + + try testing.expectError( + error.InvalidData, + LoadingImage.init(testing.io, alloc, &cmd, .{ + .file = false, + .temporary_file = .disabled, + .shared_memory = true, + }), + ); +} + test "shared memory range with offset and size" { const testing = std.testing; From 6d850fef7780f3461ee526eba16077ea9d7df8a6 Mon Sep 17 00:00:00 2001 From: "ghostty-vouch[bot]" <262049992+ghostty-vouch[bot]@users.noreply.github.com> Date: Sun, 30 Aug 2026 08:12:01 +0000 Subject: [PATCH 5/7] Update VOUCHED list (#14084) Triggered by [discussion comment](https://github.com/ghostty-org/ghostty/discussions/14083#discussioncomment-18204946) from @jcollie. Vouch: @mgsloan Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .github/VOUCHED.td | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td index 387df2f1c..33e39a19e 100644 --- a/.github/VOUCHED.td +++ b/.github/VOUCHED.td @@ -212,6 +212,7 @@ marrocco-simone masterflitzer matkotiric mattn +mgsloan micaeljarniac michielvk miguelelgallo From 3e2c0fa2db39215ee3b8098181baca7feb04ec27 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Sun, 30 Aug 2026 02:00:27 -0600 Subject: [PATCH 6/7] gtk: do not warn when gtk-xft-dpi is -1 Before this change, ghostty frequently logs the following warning, even though a `gtk-xft-dpi` value of `-1` is valid and indicates default scaling. ``` warning(gtk_ghostty_surface): gtk-xft-dpi has invalid value (-1), using default ``` From [the gtk docs](https://docs.gtk.org/gtk4/property.Settings.gtk-xft-dpi.html): > The font resolution, in 1024 * dots/inch. > > -1 to use the default value. --- src/apprt/gtk/class/surface.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/apprt/gtk/class/surface.zig b/src/apprt/gtk/class/surface.zig index 59d8ed0f7..408935125 100644 --- a/src/apprt/gtk/class/surface.zig +++ b/src/apprt/gtk/class/surface.zig @@ -1572,12 +1572,13 @@ pub const Surface = extern struct { break :xft_scale 1.0; }; - // Use a value of 1.0 for the XFT DPI scale if the setting is <= 0 - // See: - // https://gitlab.gnome.org/GNOME/libadwaita/-/commit/a7738a4d269bfdf4d8d5429ca73ccdd9b2450421 + // -1 specifies default scale. See: + // https://docs.gtk.org/gtk4/property.Settings.gtk-xft-dpi.html // https://gitlab.gnome.org/GNOME/libadwaita/-/commit/9759d3fd81129608dd78116001928f2aed974ead if (gtk_xft_dpi <= 0) { - log.warn("gtk-xft-dpi has invalid value ({}), using default", .{gtk_xft_dpi}); + if (gtk_xft_dpi != -1) { + log.warn("gtk-xft-dpi has invalid value ({}), using default", .{gtk_xft_dpi}); + } break :xft_scale 1.0; } From 860cfb1d7958d0c5af09ff23488cfa6ea6665b46 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Sun, 30 Aug 2026 02:31:42 -0600 Subject: [PATCH 7/7] Address review feedback --- src/apprt/gtk/class/surface.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/apprt/gtk/class/surface.zig b/src/apprt/gtk/class/surface.zig index 408935125..9d45bc6ce 100644 --- a/src/apprt/gtk/class/surface.zig +++ b/src/apprt/gtk/class/surface.zig @@ -1572,10 +1572,13 @@ pub const Surface = extern struct { break :xft_scale 1.0; }; - // -1 specifies default scale. See: - // https://docs.gtk.org/gtk4/property.Settings.gtk-xft-dpi.html + // Use a value of 1.0 for the XFT DPI scale if the setting is <= 0 + // See: + // https://gitlab.gnome.org/GNOME/libadwaita/-/commit/a7738a4d269bfdf4d8d5429ca73ccdd9b2450421 // https://gitlab.gnome.org/GNOME/libadwaita/-/commit/9759d3fd81129608dd78116001928f2aed974ead if (gtk_xft_dpi <= 0) { + // -1 is a valid value which specifies default scale. + // https://docs.gtk.org/gtk4/property.Settings.gtk-xft-dpi.html if (gtk_xft_dpi != -1) { log.warn("gtk-xft-dpi has invalid value ({}), using default", .{gtk_xft_dpi}); }