From 75cc4b29fdfeead0e19416fcc1cc71b40f3e135d Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Wed, 19 Mar 2025 16:50:38 -0500 Subject: [PATCH 01/49] gtk: require blueprint-compiler 0.16 for building Changes: 1. Require `blueprint-compiler` 0.16.0 (or newer) for building from a git checkout. With #6822 distributions that can't meet that requirement can use generated source tarballs to build. 2. Remove all `.ui` files as they are unnecessary. 3. Simplify the `Builder` interface since raw `.ui` files are no longer used. 4. Removed build-time check of raw `.ui` files. --- src/apprt/gtk/Builder.zig | 107 +++-------- src/apprt/gtk/ClipboardConfirmationWindow.zig | 12 +- src/apprt/gtk/ConfigErrorsDialog.zig | 4 +- src/apprt/gtk/Surface.zig | 2 +- src/apprt/gtk/Window.zig | 2 +- src/apprt/gtk/blueprint_compiler.zig | 177 ++++++++++++++---- src/apprt/gtk/builder_check.zig | 32 ---- src/apprt/gtk/gresource.zig | 49 ++--- src/apprt/gtk/menu.zig | 2 +- src/apprt/gtk/ui/1.2/ccw-osc-52-read.ui | 77 -------- src/apprt/gtk/ui/1.2/ccw-osc-52-write.ui | 77 -------- src/apprt/gtk/ui/1.2/ccw-paste.ui | 77 -------- src/apprt/gtk/ui/1.2/config-errors-dialog.ui | 36 ---- src/apprt/gtk/ui/README.md | 28 ++- src/build/SharedDeps.zig | 28 --- 15 files changed, 200 insertions(+), 510 deletions(-) delete mode 100644 src/apprt/gtk/builder_check.zig delete mode 100644 src/apprt/gtk/ui/1.2/ccw-osc-52-read.ui delete mode 100644 src/apprt/gtk/ui/1.2/ccw-osc-52-write.ui delete mode 100644 src/apprt/gtk/ui/1.2/ccw-paste.ui delete mode 100644 src/apprt/gtk/ui/1.2/config-errors-dialog.ui diff --git a/src/apprt/gtk/Builder.zig b/src/apprt/gtk/Builder.zig index 028629200..dbd765ba3 100644 --- a/src/apprt/gtk/Builder.zig +++ b/src/apprt/gtk/Builder.zig @@ -18,88 +18,37 @@ pub fn init( /// The minor version of the minimum Adwaita version that is required to use /// this resource. comptime minor: u16, - /// `blp` signifies that the resource is a Blueprint that has been compiled - /// to GTK Builder XML at compile time. `ui` signifies that the resource is - /// a GTK Builder XML file that is included in the Ghostty source (perhaps - /// because the Blueprint compiler on some target platforms cannot compile a - /// Blueprint that generates the necessary resources). - comptime kind: enum { blp, ui }, ) Builder { const resource_path = comptime resource_path: { const gresource = @import("gresource.zig"); - switch (kind) { - .blp => { - // Check to make sure that our file is listed as a - // `blueprint_file` in `gresource.zig`. If it isn't Ghostty - // could crash at runtime when we try and load a nonexistent - // GResource. - for (gresource.blueprint_files) |file| { - if (major != file.major or minor != file.minor or !std.mem.eql(u8, file.name, name)) continue; - // Use @embedFile to make sure that the `.blp` file exists - // at compile time. Zig _should_ discard the data so that - // it doesn't end up in the final executable. At runtime we - // will load the data from a GResource. - const blp_filename = std.fmt.comptimePrint( - "ui/{d}.{d}/{s}.blp", - .{ - file.major, - file.minor, - file.name, - }, - ); - _ = @embedFile(blp_filename); - break :resource_path std.fmt.comptimePrint( - "/com/mitchellh/ghostty/ui/{d}.{d}/{s}.ui", - .{ - file.major, - file.minor, - file.name, - }, - ); - } else @compileError("missing blueprint file '" ++ name ++ "' in gresource.zig"); - }, - .ui => { - // Check to make sure that our file is listed as a `ui_file` in - // `gresource.zig`. If it isn't Ghostty could crash at runtime - // when we try and load a nonexistent GResource. - for (gresource.ui_files) |file| { - if (major != file.major or minor != file.minor or !std.mem.eql(u8, file.name, name)) continue; - // Use @embedFile to make sure that the `.ui` file exists - // at compile time. Zig _should_ discard the data so that - // it doesn't end up in the final executable. At runtime we - // will load the data from a GResource. - const ui_filename = std.fmt.comptimePrint( - "ui/{d}.{d}/{s}.ui", - .{ - file.major, - file.minor, - file.name, - }, - ); - _ = @embedFile(ui_filename); - // Also use @embedFile to make sure that a matching `.blp` - // file exists at compile time. Zig _should_ discard the - // data so that it doesn't end up in the final executable. - const blp_filename = std.fmt.comptimePrint( - "ui/{d}.{d}/{s}.blp", - .{ - file.major, - file.minor, - file.name, - }, - ); - _ = @embedFile(blp_filename); - break :resource_path std.fmt.comptimePrint( - "/com/mitchellh/ghostty/ui/{d}.{d}/{s}.ui", - .{ - file.major, - file.minor, - file.name, - }, - ); - } else @compileError("missing ui file '" ++ name ++ "' in gresource.zig"); - }, - } + // Check to make sure that our file is listed as a + // `blueprint_file` in `gresource.zig`. If it isn't Ghostty + // could crash at runtime when we try and load a nonexistent + // GResource. + for (gresource.blueprint_files) |file| { + if (major != file.major or minor != file.minor or !std.mem.eql(u8, file.name, name)) continue; + // Use @embedFile to make sure that the `.blp` file exists + // at compile time. Zig _should_ discard the data so that + // it doesn't end up in the final executable. At runtime we + // will load the data from a GResource. + const blp_filename = std.fmt.comptimePrint( + "ui/{d}.{d}/{s}.blp", + .{ + file.major, + file.minor, + file.name, + }, + ); + _ = @embedFile(blp_filename); + break :resource_path std.fmt.comptimePrint( + "/com/mitchellh/ghostty/ui/{d}.{d}/{s}.ui", + .{ + file.major, + file.minor, + file.name, + }, + ); + } else @compileError("missing blueprint file '" ++ name ++ "' in gresource.zig"); }; return .{ diff --git a/src/apprt/gtk/ClipboardConfirmationWindow.zig b/src/apprt/gtk/ClipboardConfirmationWindow.zig index a28b7ddd4..583a58a2c 100644 --- a/src/apprt/gtk/ClipboardConfirmationWindow.zig +++ b/src/apprt/gtk/ClipboardConfirmationWindow.zig @@ -71,14 +71,14 @@ fn init( ) !void { var builder = switch (DialogType) { adw.AlertDialog => switch (request) { - .osc_52_read => Builder.init("ccw-osc-52-read", 1, 5, .blp), - .osc_52_write => Builder.init("ccw-osc-52-write", 1, 5, .blp), - .paste => Builder.init("ccw-paste", 1, 5, .blp), + .osc_52_read => Builder.init("ccw-osc-52-read", 1, 5), + .osc_52_write => Builder.init("ccw-osc-52-write", 1, 5), + .paste => Builder.init("ccw-paste", 1, 5), }, adw.MessageDialog => switch (request) { - .osc_52_read => Builder.init("ccw-osc-52-read", 1, 2, .ui), - .osc_52_write => Builder.init("ccw-osc-52-write", 1, 2, .ui), - .paste => Builder.init("ccw-paste", 1, 2, .ui), + .osc_52_read => Builder.init("ccw-osc-52-read", 1, 2), + .osc_52_write => Builder.init("ccw-osc-52-write", 1, 2), + .paste => Builder.init("ccw-paste", 1, 2), }, else => unreachable, }; diff --git a/src/apprt/gtk/ConfigErrorsDialog.zig b/src/apprt/gtk/ConfigErrorsDialog.zig index c10f8e679..7a245a1a0 100644 --- a/src/apprt/gtk/ConfigErrorsDialog.zig +++ b/src/apprt/gtk/ConfigErrorsDialog.zig @@ -30,8 +30,8 @@ pub fn maybePresent(app: *App, window: ?*Window) void { if (app.config._diagnostics.empty()) return; var builder = switch (DialogType) { - adw.AlertDialog => Builder.init("config-errors-dialog", 1, 5, .blp), - adw.MessageDialog => Builder.init("config-errors-dialog", 1, 2, .ui), + adw.AlertDialog => Builder.init("config-errors-dialog", 1, 5), + adw.MessageDialog => Builder.init("config-errors-dialog", 1, 2), else => unreachable, }; defer builder.deinit(); diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index e99fe29ce..4ad2eeb13 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1061,7 +1061,7 @@ pub fn promptTitle(self: *Surface) !void { if (!adw_version.atLeast(1, 5, 0)) return; const window = self.container.window() orelse return; - var builder = Builder.init("prompt-title-dialog", 1, 5, .blp); + var builder = Builder.init("prompt-title-dialog", 1, 5); defer builder.deinit(); const entry = builder.getObject(gtk.Entry, "title_entry").?; diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 20ac3d955..e130cd1be 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -248,7 +248,7 @@ pub fn init(self: *Window, app: *App) !void { btn.as(gtk.Widget).setTooltipText(i18n._("New Tab")); btn.setDropdownTooltip(i18n._("New Split")); - var builder = Builder.init("menu-headerbar-split_menu", 1, 0, .blp); + var builder = Builder.init("menu-headerbar-split_menu", 1, 0); defer builder.deinit(); btn.setMenuModel(builder.getObject(gio.MenuModel, "menu")); diff --git a/src/apprt/gtk/blueprint_compiler.zig b/src/apprt/gtk/blueprint_compiler.zig index 7a0442e92..9bc515655 100644 --- a/src/apprt/gtk/blueprint_compiler.zig +++ b/src/apprt/gtk/blueprint_compiler.zig @@ -4,62 +4,157 @@ pub const c = @cImport({ @cInclude("adwaita.h"); }); +const adwaita_version = std.SemanticVersion{ + .major = c.ADW_MAJOR_VERSION, + .minor = c.ADW_MINOR_VERSION, + .patch = c.ADW_MICRO_VERSION, +}; +const required_blueprint_version = std.SemanticVersion{ + .major = 0, + .minor = 16, + .patch = 0, +}; + pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const alloc = gpa.allocator(); + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer _ = debug_allocator.deinit(); + const alloc = debug_allocator.allocator(); var it = try std.process.argsWithAllocator(alloc); defer it.deinit(); _ = it.next(); - const major = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMajorVersion, 10); - const minor = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMinorVersion, 10); + const required_adwaita_version = std.SemanticVersion{ + .major = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMajorVersion, 10), + .minor = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMinorVersion, 10), + .patch = 0, + }; const output = it.next() orelse return error.NoOutput; const input = it.next() orelse return error.NoInput; - if (c.ADW_MAJOR_VERSION < major or (c.ADW_MAJOR_VERSION == major and c.ADW_MINOR_VERSION < minor)) { - // If the Adwaita version is too old, generate an "empty" file. - const file = try std.fs.createFileAbsolute(output, .{ - .truncate = true, - }); - try file.writeAll( - \\ - \\ - ); - defer file.close(); - - return; + if (adwaita_version.order(required_adwaita_version) == .lt) { + std.debug.print( + \\`libadwaita` is too old. + \\ + \\Ghostty requires a version {} or newer of `libadwaita` to + \\compile this blueprint. Please install it, ensure that it is + \\available on your PATH, and then retry building Ghostty. + , .{required_adwaita_version}); + std.posix.exit(1); } - var compiler = std.process.Child.init( - &.{ - "blueprint-compiler", - "compile", - "--output", - output, - input, - }, - alloc, - ); + { + var stdout: std.ArrayListUnmanaged(u8) = .empty; + defer stdout.deinit(alloc); + var stderr: std.ArrayListUnmanaged(u8) = .empty; + defer stderr.deinit(alloc); - const term = compiler.spawnAndWait() catch |err| switch (err) { - error.FileNotFound => { - std.log.err( - \\`blueprint-compiler` not found. + var blueprint_compiler = std.process.Child.init( + &.{ + "blueprint-compiler", + "--version", + }, + alloc, + ); + blueprint_compiler.stdout_behavior = .Pipe; + blueprint_compiler.stderr_behavior = .Pipe; + try blueprint_compiler.spawn(); + try blueprint_compiler.collectOutput( + alloc, + &stdout, + &stderr, + std.math.maxInt(u16), + ); + const term = blueprint_compiler.wait() catch |err| switch (err) { + error.FileNotFound => { + std.debug.print( + \\`blueprint-compiler` not found. + \\ + \\Ghostty requires version {} or newer of + \\`blueprint-compiler` as a build-time dependency starting + \\from version 1.2. Please install it, ensure that it is + \\available on your PATH, and then retry building Ghostty. + \\ + , .{required_blueprint_version}); + std.posix.exit(1); + }, + else => return err, + }; + switch (term) { + .Exited => |rc| { + if (rc != 0) std.process.exit(1); + }, + else => std.process.exit(1), + } + + const version = try std.SemanticVersion.parse(std.mem.trim(u8, stdout.items, &std.ascii.whitespace)); + if (version.order(required_blueprint_version) == .lt) { + std.debug.print( + \\`blueprint-compiler` is the wrong version. \\ - \\Ghostty requires `blueprint-compiler` as a build-time dependency starting from version 1.2. - \\Please install it, ensure that it is available on your PATH, and then retry building Ghostty. - , .{}); + \\Ghostty requires version {} or newer of + \\`blueprint-compiler` as a build-time dependency starting + \\from version 1.2. Please install it, ensure that it is + \\available on your PATH, and then retry building Ghostty. + \\ + , .{required_blueprint_version}); std.posix.exit(1); - }, - else => return err, - }; + } + } - switch (term) { - .Exited => |rc| { - if (rc != 0) std.process.exit(1); - }, - else => std.process.exit(1), + { + var stdout: std.ArrayListUnmanaged(u8) = .empty; + defer stdout.deinit(alloc); + var stderr: std.ArrayListUnmanaged(u8) = .empty; + defer stderr.deinit(alloc); + + var blueprint_compiler = std.process.Child.init( + &.{ + "blueprint-compiler", + "compile", + "--output", + output, + input, + }, + alloc, + ); + blueprint_compiler.stdout_behavior = .Pipe; + blueprint_compiler.stderr_behavior = .Pipe; + try blueprint_compiler.spawn(); + try blueprint_compiler.collectOutput( + alloc, + &stdout, + &stderr, + std.math.maxInt(u16), + ); + const term = blueprint_compiler.wait() catch |err| switch (err) { + error.FileNotFound => { + std.debug.print( + \\`blueprint-compiler` not found. + \\ + \\Ghostty requires version {} or newer of + \\`blueprint-compiler` as a build-time dependency starting + \\from version 1.2. Please install it, ensure that it is + \\available on your PATH, and then retry building Ghostty. + \\ + , .{required_blueprint_version}); + std.posix.exit(1); + }, + else => return err, + }; + + switch (term) { + .Exited => |rc| { + if (rc != 0) { + std.debug.print("{s}", .{stderr.items}); + std.process.exit(1); + } + }, + else => { + std.debug.print("{s}", .{stderr.items}); + std.process.exit(1); + }, + } } } diff --git a/src/apprt/gtk/builder_check.zig b/src/apprt/gtk/builder_check.zig deleted file mode 100644 index 015c6310d..000000000 --- a/src/apprt/gtk/builder_check.zig +++ /dev/null @@ -1,32 +0,0 @@ -const std = @import("std"); -const build_options = @import("build_options"); - -const gtk = @import("gtk"); -const adw = @import("adw"); - -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const alloc = gpa.allocator(); - - const filename = filename: { - var it = try std.process.argsWithAllocator(alloc); - defer it.deinit(); - - _ = it.next() orelse return error.NoFilename; - break :filename try alloc.dupeZ(u8, it.next() orelse return error.NoFilename); - }; - defer alloc.free(filename); - - const data = try std.fs.cwd().readFileAllocOptions(alloc, filename, std.math.maxInt(u16), null, 1, 0); - defer alloc.free(data); - - if (gtk.initCheck() == 0) { - std.debug.print("{s}: skipping builder check because we can't connect to display!\n", .{filename}); - return; - } - - adw.init(); - - const builder = gtk.Builder.newFromString(data.ptr, @intCast(data.len)); - defer builder.unref(); -} diff --git a/src/apprt/gtk/gresource.zig b/src/apprt/gtk/gresource.zig index 7ced9fc45..a1db8ac62 100644 --- a/src/apprt/gtk/gresource.zig +++ b/src/apprt/gtk/gresource.zig @@ -53,19 +53,6 @@ const icons = [_]struct { }, }; -pub const VersionedBuilderXML = struct { - major: u16, - minor: u16, - name: []const u8, -}; - -pub const ui_files = [_]VersionedBuilderXML{ - .{ .major = 1, .minor = 2, .name = "config-errors-dialog" }, - .{ .major = 1, .minor = 2, .name = "ccw-osc-52-read" }, - .{ .major = 1, .minor = 2, .name = "ccw-osc-52-write" }, - .{ .major = 1, .minor = 2, .name = "ccw-paste" }, -}; - pub const VersionedBlueprint = struct { major: u16, minor: u16, @@ -81,16 +68,21 @@ pub const blueprint_files = [_]VersionedBlueprint{ .{ .major = 1, .minor = 5, .name = "ccw-osc-52-read" }, .{ .major = 1, .minor = 5, .name = "ccw-osc-52-write" }, .{ .major = 1, .minor = 5, .name = "ccw-paste" }, + .{ .major = 1, .minor = 2, .name = "config-errors-dialog" }, + .{ .major = 1, .minor = 2, .name = "ccw-osc-52-read" }, + .{ .major = 1, .minor = 2, .name = "ccw-osc-52-write" }, + .{ .major = 1, .minor = 2, .name = "ccw-paste" }, }; pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const alloc = gpa.allocator(); + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer _ = debug_allocator.deinit(); + const alloc = debug_allocator.allocator(); - var extra_ui_files = std.ArrayList([]const u8).init(alloc); + var extra_ui_files: std.ArrayListUnmanaged([]const u8) = .empty; defer { for (extra_ui_files.items) |item| alloc.free(item); - extra_ui_files.deinit(); + extra_ui_files.deinit(alloc); } var it = try std.process.argsWithAllocator(alloc); @@ -98,7 +90,7 @@ pub fn main() !void { while (it.next()) |argument| { if (std.mem.eql(u8, std.fs.path.extension(argument), ".ui")) { - try extra_ui_files.append(try alloc.dupe(u8, argument)); + try extra_ui_files.append(alloc, try alloc.dupe(u8, argument)); } } @@ -132,16 +124,11 @@ pub fn main() !void { \\ \\ ); - for (ui_files) |ui_file| { - try writer.print( - " src/apprt/gtk/ui/{0d}.{1d}/{2s}.ui\n", - .{ ui_file.major, ui_file.minor, ui_file.name }, - ); - } for (extra_ui_files.items) |ui_file| { - const stem = std.fs.path.stem(ui_file); for (blueprint_files) |file| { - if (!std.mem.eql(u8, file.name, stem)) continue; + const expected = try std.fmt.allocPrint(alloc, "/{d}.{d}/{s}.ui", .{ file.major, file.minor, file.name }); + defer alloc.free(expected); + if (!std.mem.endsWith(u8, ui_file, expected)) continue; try writer.print( " {s}\n", .{ file.major, file.minor, file.name, ui_file }, @@ -157,7 +144,7 @@ pub fn main() !void { } pub const dependencies = deps: { - const total = css_files.len + icons.len + ui_files.len + blueprint_files.len; + const total = css_files.len + icons.len + blueprint_files.len; var deps: [total][]const u8 = undefined; var index: usize = 0; for (css_files) |css_file| { @@ -168,14 +155,6 @@ pub const dependencies = deps: { deps[index] = std.fmt.comptimePrint("images/icons/icon_{s}.png", .{icon.source}); index += 1; } - for (ui_files) |ui_file| { - deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{d}.{d}/{s}.ui", .{ - ui_file.major, - ui_file.minor, - ui_file.name, - }); - index += 1; - } for (blueprint_files) |blueprint_file| { deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{d}.{d}/{s}.blp", .{ blueprint_file.major, diff --git a/src/apprt/gtk/menu.zig b/src/apprt/gtk/menu.zig index d0a93b80d..f63a0eb5f 100644 --- a/src/apprt/gtk/menu.zig +++ b/src/apprt/gtk/menu.zig @@ -41,7 +41,7 @@ pub fn Menu( else => unreachable, }; - var builder = Builder.init("menu-" ++ object_type ++ "-" ++ menu_name, 1, 0, .blp); + var builder = Builder.init("menu-" ++ object_type ++ "-" ++ menu_name, 1, 0); defer builder.deinit(); const menu_model = builder.getObject(gio.MenuModel, "menu").?; diff --git a/src/apprt/gtk/ui/1.2/ccw-osc-52-read.ui b/src/apprt/gtk/ui/1.2/ccw-osc-52-read.ui deleted file mode 100644 index 82512e3a2..000000000 --- a/src/apprt/gtk/ui/1.2/ccw-osc-52-read.ui +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - Authorize Clipboard Access - An application is attempting to read from the clipboard. The current clipboard contents are shown below. - - Deny - Allow - - cancel - cancel - - - - - - 500 - 250 - - - false - false - true - 8 - 8 - 8 - 8 - - - - - - - - false - 2 - 1 - 12 - 12 - - - view-reveal-symbolic - - - - - - - false - 2 - 1 - 12 - 12 - - - - view-conceal-symbolic - - - - - - - - \ No newline at end of file diff --git a/src/apprt/gtk/ui/1.2/ccw-osc-52-write.ui b/src/apprt/gtk/ui/1.2/ccw-osc-52-write.ui deleted file mode 100644 index 195fb1de1..000000000 --- a/src/apprt/gtk/ui/1.2/ccw-osc-52-write.ui +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - Authorize Clipboard Access - An application is attempting to write to the clipboard. The current clipboard contents are shown below. - - Deny - Allow - - cancel - cancel - - - - - - 500 - 250 - - - false - false - true - 8 - 8 - 8 - 8 - - - - - - - - false - 2 - 1 - 12 - 12 - - - view-reveal-symbolic - - - - - - - false - 2 - 1 - 12 - 12 - - - - view-conceal-symbolic - - - - - - - - \ No newline at end of file diff --git a/src/apprt/gtk/ui/1.2/ccw-paste.ui b/src/apprt/gtk/ui/1.2/ccw-paste.ui deleted file mode 100644 index 342c767e6..000000000 --- a/src/apprt/gtk/ui/1.2/ccw-paste.ui +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - Warning: Potentially Unsafe Paste - Pasting this text into the terminal may be dangerous as it looks like some commands may be executed. - - Cancel - Paste - - cancel - cancel - - - - - - 500 - 250 - - - false - false - true - 8 - 8 - 8 - 8 - - - - - - - - false - 2 - 1 - 12 - 12 - - - view-reveal-symbolic - - - - - - - false - 2 - 1 - 12 - 12 - - - - view-conceal-symbolic - - - - - - - - \ No newline at end of file diff --git a/src/apprt/gtk/ui/1.2/config-errors-dialog.ui b/src/apprt/gtk/ui/1.2/config-errors-dialog.ui deleted file mode 100644 index 1d7517f7a..000000000 --- a/src/apprt/gtk/ui/1.2/config-errors-dialog.ui +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - Configuration Errors - One or more configuration errors were found. Please review the errors below, and either reload your configuration or ignore these errors. - - Ignore - Reload Configuration - - - - 500 - 100 - - - false - false - 8 - 8 - 8 - 8 - - - - - - - - - diff --git a/src/apprt/gtk/ui/README.md b/src/apprt/gtk/ui/README.md index 08f3f367c..b9dc732b6 100644 --- a/src/apprt/gtk/ui/README.md +++ b/src/apprt/gtk/ui/README.md @@ -1,21 +1,15 @@ # GTK UI files -This directory is for storing GTK resource definitions. With one exception, the -files should be be in the Blueprint markup language. +This directory is for storing GTK blueprints. GTK blueprints are compiled into +GTK resource builder `.ui` files by `blueprint-compiler` at build time and then +converted into an embeddable resource by `glib-compile-resources`. -Resource files should be stored in directories that represent the minimum -Adwaita version needed to use that resource. Resource files should also be -formatted using `blueprint-compiler format` as well to ensure consistency. +Blueprint files should be stored in directories that represent the minimum +Adwaita version needed to use that resource. Blueprint files should also be +formatted using `blueprint-compiler format` as well to ensure consistency +(formatting will be checked in CI). -The one exception to files being in Blueprint markup language is when Adwaita -features are used that the `blueprint-compiler` on a supported platform does not -compile. For example, Debian 12 includes Adwaita 1.2 and `blueprint-compiler` -0.6.0. Adwaita 1.2 includes support for `MessageDialog` but `blueprint-compiler` -0.6.0 does not. In cases like that the Blueprint markup should be compiled on a -platform that provides a new enough `blueprint-compiler` and the resulting `.ui` -file should be committed to the Ghostty source code. Care should be taken that -the `.blp` file and the `.ui` file remain in sync. - -In all other cases only the `.blp` should be committed to the Ghostty source -code. The build process will use `blueprint-compiler` to generate the `.ui` -files necessary at runtime. +`blueprint-compiler` version 0.16.0 or newer is required to compile Blueprint +files. If your system does not have `blueprint-compiler` or does not have a +new enough version you can use the generated source tarballs, which contain +precompiled versions of the blueprints. diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index 4f9373adb..4b97298f7 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -662,34 +662,6 @@ fn addGTK( } { - // For our actual build, we validate our GTK builder files if we can. - { - const gtk_builder_check = b.addExecutable(.{ - .name = "gtk_builder_check", - .root_source_file = b.path("src/apprt/gtk/builder_check.zig"), - .target = b.graph.host, - }); - gtk_builder_check.root_module.addOptions("build_options", self.options); - if (gobject_) |gobject| { - gtk_builder_check.root_module.addImport( - "gtk", - gobject.module("gtk4"), - ); - gtk_builder_check.root_module.addImport( - "adw", - gobject.module("adw1"), - ); - } - - for (gresource.dependencies) |pathname| { - const extension = std.fs.path.extension(pathname); - if (!std.mem.eql(u8, extension, ".ui")) continue; - const check = b.addRunArtifact(gtk_builder_check); - check.addFileArg(b.path(pathname)); - step.step.dependOn(&check.step); - } - } - // Get our gresource c/h files and add them to our build. const dist = gtkDistResources(b); step.addCSourceFile(.{ .file = dist.resources_c.path(b), .flags = &.{} }); From 334093a9ea1ea91d6ae3c562cb182969fe5576fa Mon Sep 17 00:00:00 2001 From: Aaron Ruan Date: Fri, 25 Apr 2025 15:59:44 +0800 Subject: [PATCH 02/49] feat: implement toggleMaximize for macOS --- macos/Sources/App/macOS/AppDelegate.swift | 6 ++++++ .../Command Palette/TerminalCommandPalette.swift | 1 - macos/Sources/Ghostty/Ghostty.App.swift | 11 +++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 682099e92..8849ddb75 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -807,6 +807,12 @@ class AppDelegate: NSObject, setSecureInput(.toggle) } + @IBAction func toggleMaximize(_ sender: Any) { + if NSApp.isActive, let keyWindow = NSApp.keyWindow { + keyWindow.zoom(self) + } + } + @IBAction func toggleQuickTerminal(_ sender: Any) { if quickController == nil { quickController = QuickTerminalController( diff --git a/macos/Sources/Features/Command Palette/TerminalCommandPalette.swift b/macos/Sources/Features/Command Palette/TerminalCommandPalette.swift index 2e895d4d9..7820e45fa 100644 --- a/macos/Sources/Features/Command Palette/TerminalCommandPalette.swift +++ b/macos/Sources/Features/Command Palette/TerminalCommandPalette.swift @@ -29,7 +29,6 @@ struct TerminalCommandPaletteView: View { let key = String(cString: c.action_key) switch (key) { case "toggle_tab_overview", - "toggle_maximize", "toggle_window_decorations": return false default: diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index 677129960..8f2624df4 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -523,6 +523,9 @@ extension Ghostty { case GHOSTTY_ACTION_TOGGLE_COMMAND_PALETTE: toggleCommandPalette(app, target: target) + case GHOSTTY_ACTION_TOGGLE_MAXIMIZE: + toggleMaximize(app, target: target) + case GHOSTTY_ACTION_TOGGLE_QUICK_TERMINAL: toggleQuickTerminal(app, target: target) @@ -767,6 +770,14 @@ extension Ghostty { } } + private static func toggleMaximize( + _ app: ghostty_app_t, + target: ghostty_target_s + ) { + guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return } + appDelegate.toggleMaximize(self) + } + private static func toggleVisibility( _ app: ghostty_app_t, target: ghostty_target_s From 1ec3e331dedaa4db92d5556e1eec0a31fac17bd5 Mon Sep 17 00:00:00 2001 From: Aaron Ruan Date: Sun, 27 Apr 2025 08:48:06 +0800 Subject: [PATCH 03/49] Refactor toggleMaximize to use notifications --- macos/Sources/App/macOS/AppDelegate.swift | 6 ------ .../Terminal/BaseTerminalController.swift | 10 ++++++++++ macos/Sources/Ghostty/Ghostty.App.swift | 19 +++++++++++++++++-- macos/Sources/Ghostty/Package.swift | 3 +++ 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 8849ddb75..682099e92 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -807,12 +807,6 @@ class AppDelegate: NSObject, setSecureInput(.toggle) } - @IBAction func toggleMaximize(_ sender: Any) { - if NSApp.isActive, let keyWindow = NSApp.keyWindow { - keyWindow.zoom(self) - } - } - @IBAction func toggleQuickTerminal(_ sender: Any) { if quickController == nil { quickController = QuickTerminalController( diff --git a/macos/Sources/Features/Terminal/BaseTerminalController.swift b/macos/Sources/Features/Terminal/BaseTerminalController.swift index b502e56e0..59cef503d 100644 --- a/macos/Sources/Features/Terminal/BaseTerminalController.swift +++ b/macos/Sources/Features/Terminal/BaseTerminalController.swift @@ -115,6 +115,11 @@ class BaseTerminalController: NSWindowController, selector: #selector(ghosttyCommandPaletteDidToggle(_:)), name: .ghosttyCommandPaletteDidToggle, object: nil) + center.addObserver( + self, + selector: #selector(toggleMaximize), + name: .ghosttyToggleMaximize, + object: nil) // Listen for local events that we need to know of outside of // single surface handlers. @@ -548,6 +553,11 @@ class BaseTerminalController: NSWindowController, window.performClose(sender) } + @IBAction func toggleMaximize(_ sender: Any) { + guard let window = window else { return } + window.zoom(self) + } + @IBAction func splitRight(_ sender: Any) { guard let surface = focusedSurface?.surface else { return } ghostty.split(surface: surface, direction: GHOSTTY_SPLIT_DIRECTION_RIGHT) diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index 8f2624df4..e72acc05e 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -774,8 +774,23 @@ extension Ghostty { _ app: ghostty_app_t, target: ghostty_target_s ) { - guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return } - appDelegate.toggleMaximize(self) + switch (target.tag) { + case GHOSTTY_TARGET_APP: + Ghostty.logger.warning("toggle maximize does nothing with an app target") + return + + case GHOSTTY_TARGET_SURFACE: + guard let surface = target.target.surface else { return } + guard let surfaceView = self.surfaceView(from: surface) else { return } + NotificationCenter.default.post( + name: .ghosttyToggleMaximize, + object: surfaceView + ) + + + default: + assertionFailure() + } } private static func toggleVisibility( diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/Package.swift index e2c770899..ee0073aa1 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/Package.swift @@ -257,6 +257,9 @@ extension Notification.Name { /// Ring the bell static let ghosttyBellDidRing = Notification.Name("com.mitchellh.ghostty.ghosttyBellDidRing") static let ghosttyCommandPaletteDidToggle = Notification.Name("com.mitchellh.ghostty.commandPaletteDidToggle") + + /// Toggle maximize of current window + static let ghosttyToggleMaximize = Notification.Name("com.mitchellh.ghostty.toggleMaximize") } // NOTE: I am moving all of these to Notification.Name extensions over time. This From 13f776d483d5120e6b7fab7b04ed2fd5894e77e6 Mon Sep 17 00:00:00 2001 From: Aaron Ruan Date: Mon, 28 Apr 2025 10:20:29 +0800 Subject: [PATCH 04/49] Rename maximize notification and refine handler --- .../Terminal/BaseTerminalController.swift | 16 +++++++++------- macos/Sources/Ghostty/Ghostty.App.swift | 2 +- macos/Sources/Ghostty/Package.swift | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/macos/Sources/Features/Terminal/BaseTerminalController.swift b/macos/Sources/Features/Terminal/BaseTerminalController.swift index 59cef503d..d6b0433ac 100644 --- a/macos/Sources/Features/Terminal/BaseTerminalController.swift +++ b/macos/Sources/Features/Terminal/BaseTerminalController.swift @@ -117,8 +117,8 @@ class BaseTerminalController: NSWindowController, object: nil) center.addObserver( self, - selector: #selector(toggleMaximize), - name: .ghosttyToggleMaximize, + selector: #selector(ghosttyMaximizeDidToggle(_:)), + name: .ghosttyMaximizeDidToggle, object: nil) // Listen for local events that we need to know of outside of @@ -239,6 +239,13 @@ class BaseTerminalController: NSWindowController, toggleCommandPalette(nil) } + @objc private func ghosttyMaximizeDidToggle(_ notification: Notification) { + guard let window else { return } + guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return } + guard surfaceTree?.contains(view: surfaceView) ?? false else { return } + window.zoom(nil) + } + // MARK: Local Events private func localEventHandler(_ event: NSEvent) -> NSEvent? { @@ -553,11 +560,6 @@ class BaseTerminalController: NSWindowController, window.performClose(sender) } - @IBAction func toggleMaximize(_ sender: Any) { - guard let window = window else { return } - window.zoom(self) - } - @IBAction func splitRight(_ sender: Any) { guard let surface = focusedSurface?.surface else { return } ghostty.split(surface: surface, direction: GHOSTTY_SPLIT_DIRECTION_RIGHT) diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index e72acc05e..f7f5f475c 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -783,7 +783,7 @@ extension Ghostty { guard let surface = target.target.surface else { return } guard let surfaceView = self.surfaceView(from: surface) else { return } NotificationCenter.default.post( - name: .ghosttyToggleMaximize, + name: .ghosttyMaximizeDidToggle, object: surfaceView ) diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/Package.swift index ee0073aa1..80223776c 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/Package.swift @@ -259,7 +259,7 @@ extension Notification.Name { static let ghosttyCommandPaletteDidToggle = Notification.Name("com.mitchellh.ghostty.commandPaletteDidToggle") /// Toggle maximize of current window - static let ghosttyToggleMaximize = Notification.Name("com.mitchellh.ghostty.toggleMaximize") + static let ghosttyMaximizeDidToggle = Notification.Name("com.mitchellh.ghostty.maximizeDidToggle") } // NOTE: I am moving all of these to Notification.Name extensions over time. This From 529129204717f2321a8d6b8ac3bbd9a8d3a1d495 Mon Sep 17 00:00:00 2001 From: Morgan Date: Wed, 30 Apr 2025 15:47:00 +0900 Subject: [PATCH 05/49] Add `runtimeUntil`, fix remove GDK_DEBUG `gl-no-fractional` --- src/apprt/gtk/App.zig | 6 ++++-- src/apprt/gtk/gtk_version.zig | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 72c0d7509..0ef9f33c6 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -190,7 +190,6 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { // For the remainder of "why" see the 4.14 comment below. gdk_disable.@"gles-api" = true; gdk_disable.vulkan = true; - gdk_debug.@"gl-no-fractional" = true; break :environment; } if (gtk_version.runtimeAtLeast(4, 14, 0)) { @@ -201,8 +200,11 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { // // Upstream issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/6589 gdk_debug.@"gl-disable-gles" = true; - gdk_debug.@"gl-no-fractional" = true; gdk_debug.@"vulkan-disable" = true; + + if (gtk_version.runtimeUntil(4, 17, 5)) { + gdk_debug.@"gl-no-fractional" = true; + } break :environment; } // Versions prior to 4.14 are a bit of an unknown for Ghostty. It diff --git a/src/apprt/gtk/gtk_version.zig b/src/apprt/gtk/gtk_version.zig index 59d7a5782..5d75fb4fe 100644 --- a/src/apprt/gtk/gtk_version.zig +++ b/src/apprt/gtk/gtk_version.zig @@ -87,10 +87,23 @@ pub inline fn runtimeAtLeast( }) != .lt; } +pub inline fn runtimeUntil( + comptime major: u16, + comptime minor: u16, + comptime micro: u16, +) bool { + const runtime_version = getRuntimeVersion(); + return runtime_version.order(.{ + .major = major, + .minor = minor, + .patch = micro, + }) == .lt; +} + test "atLeast" { const testing = std.testing; - const funs = &.{ atLeast, runtimeAtLeast }; + const funs = &.{ atLeast, runtimeAtLeast, runtimeUntil }; inline for (funs) |fun| { try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION)); From 6f4fe56b931bc17b1a0f5e590fb538e6a8a13188 Mon Sep 17 00:00:00 2001 From: Morgan Date: Wed, 30 Apr 2025 23:48:15 +0900 Subject: [PATCH 06/49] Add comment about `gl-no-fractional` --- src/apprt/gtk/App.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 0ef9f33c6..9bd61939a 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -159,6 +159,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { opengl: bool = false, /// disable GLES, Ghostty can't use GLES @"gl-disable-gles": bool = false, + // GTK's new renderer can cause blurry font when using fractional scaling. @"gl-no-fractional": bool = false, /// Disabling Vulkan can improve startup times by hundreds of /// milliseconds on some systems. We don't use Vulkan so we can just @@ -203,6 +204,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { gdk_debug.@"vulkan-disable" = true; if (gtk_version.runtimeUntil(4, 17, 5)) { + // Removed at GTK v4.17.5 gdk_debug.@"gl-no-fractional" = true; } break :environment; From 0b5160e9f01b6d939a1c213478cbcbec2982e3aa Mon Sep 17 00:00:00 2001 From: Maciej Bartczak <39600846+maciekbartczak@users.noreply.github.com> Date: Thu, 1 May 2025 18:51:24 +0200 Subject: [PATCH 07/49] implement dark/light theme filtering in theme preview --- src/cli/list_themes.zig | 45 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/cli/list_themes.zig b/src/cli/list_themes.zig index 8ebac4487..54e71b96f 100644 --- a/src/cli/list_themes.zig +++ b/src/cli/list_themes.zig @@ -24,6 +24,12 @@ pub const Options = struct { /// If true, force a plain list of themes. plain: bool = false, + /// If true, print only the dark themes. + dark: bool = false, + + /// If true, print only the light themes. + light: bool = false, + pub fn deinit(self: Options) void { _ = self; } @@ -137,11 +143,30 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 { if (std.mem.eql(u8, entry.name, ".DS_Store")) continue; count += 1; - try themes.append(.{ - .location = loc.location, - .path = try std.fs.path.join(alloc, &.{ loc.dir, entry.name }), - .theme = try alloc.dupe(u8, entry.name), - }); + + const path = try std.fs.path.join(alloc, &.{ loc.dir, entry.name }); + // if there is no need to filter just append the theme to the list + if (!opts.dark and !opts.light) { + try themes.append(.{ + .path = path, + .location = loc.location, + .theme = try alloc.dupe(u8, entry.name), + }); + continue; + } + + // otherwise check if the theme should be included based on the provided options + var config = try Config.default(alloc); + defer config.deinit(); + try config.loadFile(config._arena.?.allocator(), path); + + if (shouldIncludeTheme(opts, config)) { + try themes.append(.{ + .path = path, + .location = loc.location, + .theme = try alloc.dupe(u8, entry.name), + }); + } }, else => {}, } @@ -1594,3 +1619,13 @@ fn preview(allocator: std.mem.Allocator, themes: []ThemeListElement) !void { defer app.deinit(); try app.run(); } + +fn shouldIncludeTheme(opts: Options, theme_config: Config) bool { + const rf = @as(f32, @floatFromInt(theme_config.background.r)) / 255.0; + const gf = @as(f32, @floatFromInt(theme_config.background.g)) / 255.0; + const bf = @as(f32, @floatFromInt(theme_config.background.b)) / 255.0; + const luminance = 0.2126 * rf + 0.7152 * gf + 0.0722 * bf; + const is_dark = luminance < 0.5; + + return (opts.dark and is_dark) or (opts.light and !is_dark); +} From 418c46538c740c202ae7c35e5756b90d9fcd91a9 Mon Sep 17 00:00:00 2001 From: Maciej Bartczak <39600846+maciekbartczak@users.noreply.github.com> Date: Thu, 1 May 2025 19:41:02 +0200 Subject: [PATCH 08/49] use enum for the color scheme args --- src/cli/list_themes.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cli/list_themes.zig b/src/cli/list_themes.zig index 54e71b96f..54f4c0969 100644 --- a/src/cli/list_themes.zig +++ b/src/cli/list_themes.zig @@ -24,11 +24,8 @@ pub const Options = struct { /// If true, force a plain list of themes. plain: bool = false, - /// If true, print only the dark themes. - dark: bool = false, - - /// If true, print only the light themes. - light: bool = false, + /// Specifies the color scheme of the themes to include in the list. + color: enum { all, dark, light } = .all, pub fn deinit(self: Options) void { _ = self; @@ -99,6 +96,9 @@ const ThemeListElement = struct { /// * `--path`: Show the full path to the theme. /// /// * `--plain`: Force a plain listing of themes. +/// +/// * `--color`: Specify the color scheme of the themes included in the list. +/// This can be `dark`, `light`, or `all`. The default is `all`. pub fn run(gpa_alloc: std.mem.Allocator) !u8 { var opts: Options = .{}; defer opts.deinit(); @@ -146,7 +146,7 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 { const path = try std.fs.path.join(alloc, &.{ loc.dir, entry.name }); // if there is no need to filter just append the theme to the list - if (!opts.dark and !opts.light) { + if (opts.color == .all) { try themes.append(.{ .path = path, .location = loc.location, @@ -1627,5 +1627,5 @@ fn shouldIncludeTheme(opts: Options, theme_config: Config) bool { const luminance = 0.2126 * rf + 0.7152 * gf + 0.0722 * bf; const is_dark = luminance < 0.5; - return (opts.dark and is_dark) or (opts.light and !is_dark); + return (opts.color == .dark and is_dark) or (opts.color == .light and !is_dark); } From cfedd477b2843c4624aac88b76ec24cd7ecd36d6 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Wed, 30 Apr 2025 14:02:46 -0600 Subject: [PATCH 09/49] font/freetype: introduce mutexes to ensure thread safety of Library and Face For details see comments and FreeType docs @ https://freetype.org/freetype2/docs/reference/ft2-library_setup.html#ft_library https://freetype.org/freetype2/docs/reference/ft2-face_creation.html#ft_face tl;dr: FT_New_Face and FT_Done_Face require the Library to be locked for thread safety, and FT_Load_Glyph and FT_Render_Glyph and friends need the face to be locked for thread safety, since we're sharing faces across threads. --- src/font/CodepointResolver.zig | 6 +-- src/font/Collection.zig | 20 ++++---- src/font/DeferredFace.zig | 4 +- src/font/SharedGrid.zig | 2 +- src/font/SharedGridSet.zig | 2 +- src/font/face/coretext.zig | 17 ++++--- src/font/face/freetype.zig | 87 +++++++++++++++++++++++++--------- src/font/library.zig | 24 ++++++++-- src/font/opentype/svg.zig | 2 +- src/font/shaper/coretext.zig | 2 +- src/font/shaper/harfbuzz.zig | 2 +- 11 files changed, 115 insertions(+), 53 deletions(-) diff --git a/src/font/CodepointResolver.zig b/src/font/CodepointResolver.zig index 326ca0186..37093b59a 100644 --- a/src/font/CodepointResolver.zig +++ b/src/font/CodepointResolver.zig @@ -380,7 +380,7 @@ test getIndex { const testEmoji = font.embedded.emoji; const testEmojiText = font.embedded.emoji_text; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = Collection.init(); @@ -461,7 +461,7 @@ test "getIndex disabled font style" { var atlas_grayscale = try font.Atlas.init(alloc, 512, .grayscale); defer atlas_grayscale.deinit(alloc); - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = Collection.init(); @@ -513,7 +513,7 @@ test "getIndex box glyph" { const testing = std.testing; const alloc = testing.allocator; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); const c = Collection.init(); diff --git a/src/font/Collection.zig b/src/font/Collection.zig index cfc633b04..66e13c109 100644 --- a/src/font/Collection.zig +++ b/src/font/Collection.zig @@ -78,8 +78,8 @@ pub const AddError = Allocator.Error || error{ /// next in priority if others exist already, i.e. it'll be the _last_ to be /// searched for a glyph in that list. /// -/// The collection takes ownership of the face. The face will be deallocated -/// when the collection is deallocated. +/// If no error is encountered then the collection takes ownership of the face, +/// in which case face will be deallocated when the collection is deallocated. /// /// If a loaded face is added to the collection, it should be the same /// size as all the other faces in the collection. This function will not @@ -700,7 +700,7 @@ test "add full" { const alloc = testing.allocator; const testFont = font.embedded.regular; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = init(); @@ -746,7 +746,7 @@ test getFace { const alloc = testing.allocator; const testFont = font.embedded.regular; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = init(); @@ -770,7 +770,7 @@ test getIndex { const alloc = testing.allocator; const testFont = font.embedded.regular; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = init(); @@ -801,7 +801,7 @@ test completeStyles { const alloc = testing.allocator; const testFont = font.embedded.regular; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = init(); @@ -828,7 +828,7 @@ test setSize { const alloc = testing.allocator; const testFont = font.embedded.regular; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = init(); @@ -851,7 +851,7 @@ test hasCodepoint { const alloc = testing.allocator; const testFont = font.embedded.regular; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = init(); @@ -875,7 +875,7 @@ test "hasCodepoint emoji default graphical" { const alloc = testing.allocator; const testEmoji = font.embedded.emoji; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = init(); @@ -898,7 +898,7 @@ test "metrics" { const alloc = testing.allocator; const testFont = font.embedded.inconsolata; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var c = init(); diff --git a/src/font/DeferredFace.zig b/src/font/DeferredFace.zig index 3ee104386..d61f5492f 100644 --- a/src/font/DeferredFace.zig +++ b/src/font/DeferredFace.zig @@ -407,7 +407,7 @@ test "fontconfig" { const alloc = testing.allocator; // Load freetype - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); // Get a deferred face from fontconfig @@ -437,7 +437,7 @@ test "coretext" { const alloc = testing.allocator; // Load freetype - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); // Get a deferred face from fontconfig diff --git a/src/font/SharedGrid.zig b/src/font/SharedGrid.zig index 65c7ecd87..72e97fad8 100644 --- a/src/font/SharedGrid.zig +++ b/src/font/SharedGrid.zig @@ -338,7 +338,7 @@ test getIndex { const alloc = testing.allocator; // const testEmoji = @import("test.zig").fontEmoji; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var grid = try testGrid(.normal, alloc, lib); diff --git a/src/font/SharedGridSet.zig b/src/font/SharedGridSet.zig index ca535eaf8..8ad30629e 100644 --- a/src/font/SharedGridSet.zig +++ b/src/font/SharedGridSet.zig @@ -50,7 +50,7 @@ pub const InitError = Library.InitError; /// Initialize a new SharedGridSet. pub fn init(alloc: Allocator) InitError!SharedGridSet { - var font_lib = try Library.init(); + var font_lib = try Library.init(alloc); errdefer font_lib.deinit(); return .{ diff --git a/src/font/face/coretext.zig b/src/font/face/coretext.zig index 3749b4824..639eae43c 100644 --- a/src/font/face/coretext.zig +++ b/src/font/face/coretext.zig @@ -46,7 +46,11 @@ pub const Face = struct { }; /// Initialize a CoreText-based font from a TTF/TTC in memory. - pub fn init(lib: font.Library, source: [:0]const u8, opts: font.face.Options) !Face { + pub fn init( + lib: font.Library, + source: [:0]const u8, + opts: font.face.Options, + ) !Face { _ = lib; const data = try macos.foundation.Data.createWithBytesNoCopy(source); @@ -914,7 +918,7 @@ test "in-memory" { var atlas = try font.Atlas.init(alloc, 512, .grayscale); defer atlas.deinit(alloc); - var lib = try font.Library.init(); + var lib = try font.Library.init(alloc); defer lib.deinit(); var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } }); @@ -941,7 +945,7 @@ test "variable" { var atlas = try font.Atlas.init(alloc, 512, .grayscale); defer atlas.deinit(alloc); - var lib = try font.Library.init(); + var lib = try font.Library.init(alloc); defer lib.deinit(); var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } }); @@ -968,7 +972,7 @@ test "variable set variation" { var atlas = try font.Atlas.init(alloc, 512, .grayscale); defer atlas.deinit(alloc); - var lib = try font.Library.init(); + var lib = try font.Library.init(alloc); defer lib.deinit(); var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } }); @@ -996,7 +1000,7 @@ test "svg font table" { const alloc = testing.allocator; const testFont = font.embedded.julia_mono; - var lib = try font.Library.init(); + var lib = try font.Library.init(alloc); defer lib.deinit(); var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } }); @@ -1010,9 +1014,10 @@ test "svg font table" { test "glyphIndex colored vs text" { const testing = std.testing; + const alloc = testing.allocator; const testFont = font.embedded.julia_mono; - var lib = try font.Library.init(); + var lib = try font.Library.init(alloc); defer lib.deinit(); var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } }); diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig index c2eab4599..bf86b88de 100644 --- a/src/font/face/freetype.zig +++ b/src/font/face/freetype.zig @@ -29,12 +29,20 @@ pub const Face = struct { assert(font.face.FreetypeLoadFlags != void); } - /// Our freetype library - lib: freetype.Library, + /// Our Library + lib: Library, /// Our font face. face: freetype.Face, + /// This mutex MUST be held while doing anything with the + /// glyph slot on the freetype face, because this struct + /// may be shared across multiple surfaces. + /// + /// This means that anywhere where `self.face.loadGlyph` + /// is called, this mutex must be held. + ft_mutex: *std.Thread.Mutex, + /// Harfbuzz font corresponding to this face. hb_font: harfbuzz.Font, @@ -59,30 +67,52 @@ pub const Face = struct { }; /// Initialize a new font face with the given source in-memory. - pub fn initFile(lib: Library, path: [:0]const u8, index: i32, opts: font.face.Options) !Face { + pub fn initFile( + lib: Library, + path: [:0]const u8, + index: i32, + opts: font.face.Options, + ) !Face { + lib.mutex.lock(); + defer lib.mutex.unlock(); const face = try lib.lib.initFace(path, index); errdefer face.deinit(); return try initFace(lib, face, opts); } /// Initialize a new font face with the given source in-memory. - pub fn init(lib: Library, source: [:0]const u8, opts: font.face.Options) !Face { + pub fn init( + lib: Library, + source: [:0]const u8, + opts: font.face.Options, + ) !Face { + lib.mutex.lock(); + defer lib.mutex.unlock(); const face = try lib.lib.initMemoryFace(source, 0); errdefer face.deinit(); return try initFace(lib, face, opts); } - fn initFace(lib: Library, face: freetype.Face, opts: font.face.Options) !Face { + fn initFace( + lib: Library, + face: freetype.Face, + opts: font.face.Options, + ) !Face { try face.selectCharmap(.unicode); try setSize_(face, opts.size); var hb_font = try harfbuzz.freetype.createFont(face.handle); errdefer hb_font.destroy(); + const ft_mutex = try lib.alloc.create(std.Thread.Mutex); + errdefer lib.alloc.destroy(ft_mutex); + ft_mutex.* = .{}; + var result: Face = .{ - .lib = lib.lib, + .lib = lib, .face = face, .hb_font = hb_font, + .ft_mutex = ft_mutex, .load_flags = opts.freetype_load_flags, }; result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result); @@ -114,7 +144,13 @@ pub const Face = struct { } pub fn deinit(self: *Face) void { - self.face.deinit(); + self.lib.alloc.destroy(self.ft_mutex); + { + self.lib.mutex.lock(); + defer self.lib.mutex.unlock(); + + self.face.deinit(); + } self.hb_font.destroy(); self.* = undefined; } @@ -147,11 +183,7 @@ pub const Face = struct { self.face.ref(); errdefer self.face.deinit(); - var f = try initFace( - .{ .lib = self.lib }, - self.face, - opts, - ); + var f = try initFace(self.lib, self.face, opts); errdefer f.deinit(); f.synthetic = self.synthetic; f.synthetic.bold = true; @@ -166,11 +198,7 @@ pub const Face = struct { self.face.ref(); errdefer self.face.deinit(); - var f = try initFace( - .{ .lib = self.lib }, - self.face, - opts, - ); + var f = try initFace(self.lib, self.face, opts); errdefer f.deinit(); f.synthetic = self.synthetic; f.synthetic.italic = true; @@ -228,7 +256,7 @@ pub const Face = struct { // first thing we have to do is get all the vars and put them into // an array. const mm = try self.face.getMMVar(); - defer self.lib.doneMMVar(mm); + defer self.lib.lib.doneMMVar(mm); // To avoid allocations, we cap the number of variation axes we can // support. This is arbitrary but Firefox caps this at 16 so I @@ -270,6 +298,9 @@ pub const Face = struct { /// Returns true if the given glyph ID is colorized. pub fn isColorGlyph(self: *const Face, glyph_id: u32) bool { + self.ft_mutex.lock(); + defer self.ft_mutex.unlock(); + // Load the glyph and see what pixel mode it renders with. // All modes other than BGRA are non-color. // If the glyph fails to load, just return false. @@ -296,6 +327,9 @@ pub const Face = struct { glyph_index: u32, opts: font.face.RenderOptions, ) !Glyph { + self.ft_mutex.lock(); + defer self.ft_mutex.unlock(); + const metrics = opts.grid_metrics; // If we have synthetic italic, then we apply a transformation matrix. @@ -741,6 +775,9 @@ pub const Face = struct { // If we fail to load any visible ASCII we just use max_advance from // the metrics provided by FreeType. const cell_width: f64 = cell_width: { + self.ft_mutex.lock(); + defer self.ft_mutex.unlock(); + var max: f64 = 0.0; var c: u8 = ' '; while (c < 127) : (c += 1) { @@ -780,6 +817,8 @@ pub const Face = struct { break :heights .{ cap: { + self.ft_mutex.lock(); + defer self.ft_mutex.unlock(); if (face.getCharIndex('H')) |glyph_index| { if (face.loadGlyph(glyph_index, .{ .render = true, @@ -791,6 +830,8 @@ pub const Face = struct { break :cap null; }, ex: { + self.ft_mutex.lock(); + defer self.ft_mutex.unlock(); if (face.getCharIndex('x')) |glyph_index| { if (face.loadGlyph(glyph_index, .{ .render = true, @@ -832,7 +873,7 @@ test { const testFont = font.embedded.inconsolata; const alloc = testing.allocator; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var atlas = try font.Atlas.init(alloc, 512, .grayscale); @@ -881,7 +922,7 @@ test "color emoji" { const alloc = testing.allocator; const testFont = font.embedded.emoji; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var atlas = try font.Atlas.init(alloc, 512, .rgba); @@ -936,7 +977,7 @@ test "mono to rgba" { const alloc = testing.allocator; const testFont = font.embedded.emoji; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var atlas = try font.Atlas.init(alloc, 512, .rgba); @@ -958,7 +999,7 @@ test "svg font table" { const alloc = testing.allocator; const testFont = font.embedded.julia_mono; - var lib = try font.Library.init(); + var lib = try font.Library.init(alloc); defer lib.deinit(); var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12, .xdpi = 72, .ydpi = 72 } }); @@ -995,7 +1036,7 @@ test "bitmap glyph" { const alloc = testing.allocator; const testFont = font.embedded.terminus_ttf; - var lib = try Library.init(); + var lib = try Library.init(alloc); defer lib.deinit(); var atlas = try font.Atlas.init(alloc, 512, .grayscale); diff --git a/src/font/library.zig b/src/font/library.zig index b00bbfce0..43aa101b7 100644 --- a/src/font/library.zig +++ b/src/font/library.zig @@ -1,5 +1,7 @@ //! A library represents the shared state that the underlying font //! library implementation(s) require per-process. +const std = @import("std"); +const Allocator = std.mem.Allocator; const builtin = @import("builtin"); const options = @import("main.zig").options; const freetype = @import("freetype"); @@ -24,13 +26,26 @@ pub const Library = switch (options.backend) { pub const FreetypeLibrary = struct { lib: freetype.Library, - pub const InitError = freetype.Error; + alloc: Allocator, - pub fn init() InitError!Library { - return Library{ .lib = try freetype.Library.init() }; + /// Mutex to be held any time the library is + /// being used to create or destroy a face. + mutex: *std.Thread.Mutex, + + pub const InitError = freetype.Error || Allocator.Error; + + pub fn init(alloc: Allocator) InitError!Library { + const lib = try freetype.Library.init(); + errdefer lib.deinit(); + + const mutex = try alloc.create(std.Thread.Mutex); + mutex.* = .{}; + + return Library{ .lib = lib, .alloc = alloc, .mutex = mutex }; } pub fn deinit(self: *Library) void { + self.alloc.destroy(self.mutex); self.lib.deinit(); } }; @@ -38,7 +53,8 @@ pub const FreetypeLibrary = struct { pub const NoopLibrary = struct { pub const InitError = error{}; - pub fn init() InitError!Library { + pub fn init(alloc: Allocator) InitError!Library { + _ = alloc; return Library{}; } diff --git a/src/font/opentype/svg.zig b/src/font/opentype/svg.zig index 01d172d17..ff8eeed49 100644 --- a/src/font/opentype/svg.zig +++ b/src/font/opentype/svg.zig @@ -99,7 +99,7 @@ test "SVG" { const alloc = testing.allocator; const testFont = font.embedded.julia_mono; - var lib = try font.Library.init(); + var lib = try font.Library.init(alloc); defer lib.deinit(); var face = try font.Face.init(lib, testFont, .{ .size = .{ .points = 12 } }); diff --git a/src/font/shaper/coretext.zig b/src/font/shaper/coretext.zig index ec64fe6eb..f2ac5b85d 100644 --- a/src/font/shaper/coretext.zig +++ b/src/font/shaper/coretext.zig @@ -1761,7 +1761,7 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper { .nerd_font => font.embedded.nerd_font, }; - var lib = try Library.init(); + var lib = try Library.init(alloc); errdefer lib.deinit(); var c = Collection.init(); diff --git a/src/font/shaper/harfbuzz.zig b/src/font/shaper/harfbuzz.zig index b284dc140..eb8130f79 100644 --- a/src/font/shaper/harfbuzz.zig +++ b/src/font/shaper/harfbuzz.zig @@ -1220,7 +1220,7 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper { .arabic => font.embedded.arabic, }; - var lib = try Library.init(); + var lib = try Library.init(alloc); errdefer lib.deinit(); var c = Collection.init(); From 5319d3836619ebbf2d7beefc17197f5c42fc1553 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Thu, 1 May 2025 18:37:24 -0600 Subject: [PATCH 10/49] fix(tests): correctly deinit font faces --- src/font/Collection.zig | 21 ++++++++++++--------- src/font/DeferredFace.zig | 6 ++++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/font/Collection.zig b/src/font/Collection.zig index 66e13c109..59f89d402 100644 --- a/src/font/Collection.zig +++ b/src/font/Collection.zig @@ -714,15 +714,18 @@ test "add full" { ) }); } - try testing.expectError(error.CollectionFull, c.add( - alloc, - .regular, - .{ .loaded = try Face.init( - lib, - testFont, - .{ .size = .{ .points = 12 } }, - ) }, - )); + var face = try Face.init( + lib, + testFont, + .{ .size = .{ .points = 12 } }, + ); + // We have to deinit it manually since the + // collection doesn't do it if adding fails. + defer face.deinit(); + try testing.expectError( + error.CollectionFull, + c.add(alloc, .regular, .{ .loaded = face }), + ); } test "add deferred without loading options" { diff --git a/src/font/DeferredFace.zig b/src/font/DeferredFace.zig index d61f5492f..8794ccea9 100644 --- a/src/font/DeferredFace.zig +++ b/src/font/DeferredFace.zig @@ -425,7 +425,8 @@ test "fontconfig" { try testing.expect(n.len > 0); // Load it and verify it works - const face = try def.load(lib, .{ .size = .{ .points = 12 } }); + var face = try def.load(lib, .{ .size = .{ .points = 12 } }); + defer face.deinit(); try testing.expect(face.glyphIndex(' ') != null); } @@ -456,6 +457,7 @@ test "coretext" { try testing.expect(n.len > 0); // Load it and verify it works - const face = try def.load(lib, .{ .size = .{ .points = 12 } }); + var face = try def.load(lib, .{ .size = .{ .points = 12 } }); + defer face.deinit(); try testing.expect(face.glyphIndex(' ') != null); } From 10dcf1dfe9f311ede45edc76256ea5acdb980dc8 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sat, 3 May 2025 14:35:32 -0500 Subject: [PATCH 11/49] fix most of the feedback from 7012 --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 35ed2dc33..e958acd55 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -16,22 +16,26 @@ body: - The feature or configuration option you encounter the issue with. - The expected behavior. - The actual behavior (and how it deviates from the expected behavior, if it is not immediately obvious). - - Relevant Ghostty logs or other stacktraces. - - Relevant screenshots, screen recordings, or other supporting media (as needed). + - Screenshots, screen recordings, or other supporting media (as needed). - If this is a regression of an existing issue that was closed or resolved, please include the previous item reference (Discussion, Issue, PR, commit) in your description. >[!TIP] > **Not sure what information to include?** > Here are some recommendations: - > - **Input issues:** include your keyboard layout, a screenshot of the terminal inspector's logged keystrokes (Linux: ctrl+shift+i; MacOS: cmd+alt+i), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version. + > - **Input issues:** include your keyboard layout, a screenshot of the terminal inspector's logged keystrokes from the Terminal Inspector's "Keyboard" tab (Linux: ctrl+shift+i; MacOS: cmd+alt+i), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version. > - **Font issues:** include the problematic character(s), the output of `ghostty +show-face` for these character(s), and if they work in other applications. > - **VT issues (including image rendering issues):** attach an [asciinema](https://docs.asciinema.org/getting-started/) cast file, shell script, or text file for reproduction. > - **Renderer issues:** (Linux) include your OpenGL version, graphics card, driver version. - + > - **Crashes:** (macOS) include the [Sentry UUID](https://github.com/ghostty-org/ghostty?tab=readme-ov-file#crash-reports); (Linux) try to reproduce using a debug build and provide the stack trace. placeholder: | Example: When using SSH to connect to my remote Linux machine from my local macOS device in Ghostty, I try to run `clear`, and the screen does not clear. Instead, I see the following error message printed to the terminal: `Error opening terminal: xterm-ghostty.` validations: required: true + - type: textarea + attributes: + label: Ghostty Logs + description: | + Provide any captured Ghostty logs or stacktraces during your issue reproduction in this field. For macOS users, logs can be found via the command provided in [#7194](https://github.com/ghostty-org/ghostty/discussions/7194) - type: textarea attributes: label: Reproduction Steps @@ -93,9 +97,9 @@ body: required: false - type: textarea attributes: - label: Ghostty Configuration + label: Ghostty Configuration (Minimal) description: | - Please provide the minimum configuration needed to reproduce this issue. If you cannot determine this, paste the output of `ghostty +show-config` here. + Please provide the **minimum** configuration needed to reproduce this issue. If you cannot determine this, paste the contents of your Ghostty configuration file here. placeholder: | font-family = CommitMono Nerd Font font-family-bold = CommitMono Nerd Font @@ -112,9 +116,9 @@ body: attributes: label: Additional Relevant Configuration description: | - If your issue involves other programs, tools, or applications in addition to Ghostty (e.g. Neovim, tmux, Zellij, etc.), please provide the minimum configuration needed for all relevant programs to reproduce the issue here. If you use custom CSS or shaders for Ghostty, also include them here, if applicable to your issue. + If your issue involves other programs, tools, or applications in addition to Ghostty (e.g. Neovim, tmux, Zellij, etc.), please provide the minimum configuration and versions needed for all relevant programs to reproduce the issue here. If you use custom CSS or shaders for Ghostty, also include them here, if applicable to your issue. placeholder: | - `tmux.conf` + `tmux.conf` (tmux 3.5a) --- set -g default-terminal "tmux-256color" set-option -sa terminal-overrides ",xterm*:Tc" From 60e1a73c041e934818c205234ec67525517d6ebd Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sat, 3 May 2025 14:45:53 -0500 Subject: [PATCH 12/49] update log textarea render to display inputs in codeblock --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index e958acd55..93aa87b48 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -36,6 +36,7 @@ body: label: Ghostty Logs description: | Provide any captured Ghostty logs or stacktraces during your issue reproduction in this field. For macOS users, logs can be found via the command provided in [#7194](https://github.com/ghostty-org/ghostty/discussions/7194) + render: text - type: textarea attributes: label: Reproduction Steps From abf5f1859868e70fda4116b94638c2fa1979e803 Mon Sep 17 00:00:00 2001 From: mitchellh <1299+mitchellh@users.noreply.github.com> Date: Sun, 4 May 2025 00:14:51 +0000 Subject: [PATCH 13/49] deps: Update iTerm2 color schemes --- build.zig.zon | 4 ++-- build.zig.zon.json | 6 +++--- build.zig.zon.nix | 6 +++--- build.zig.zon.txt | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 180746436..7c5ff8ffc 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -103,8 +103,8 @@ // Other .apple_sdk = .{ .path = "./pkg/apple-sdk" }, .iterm2_themes = .{ - .url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz", - .hash = "N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym", + .url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz", + .hash = "N-V-__8AAHOASAQuLADcCSHLHEJiKFVLZiCD9Aq2rh5GT01A", .lazy = true, }, }, diff --git a/build.zig.zon.json b/build.zig.zon.json index 640167615..513ee0dcd 100644 --- a/build.zig.zon.json +++ b/build.zig.zon.json @@ -54,10 +54,10 @@ "url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz", "hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA=" }, - "N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym": { + "N-V-__8AAHOASAQuLADcCSHLHEJiKFVLZiCD9Aq2rh5GT01A": { "name": "iterm2_themes", - "url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz", - "hash": "sha256-Vy5muiJ3hJXcOvmFHLhqc+Dvdh74GG6+u/L+EsavDb0=" + "url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz", + "hash": "sha256-xpDitXpZrdU/EcgLyG4G0cEiT4r42viy+DJALmy2sQE=" }, "N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD": { "name": "libpng", diff --git a/build.zig.zon.nix b/build.zig.zon.nix index becb2ae87..46cf07cc9 100644 --- a/build.zig.zon.nix +++ b/build.zig.zon.nix @@ -170,11 +170,11 @@ in }; } { - name = "N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym"; + name = "N-V-__8AAHOASAQuLADcCSHLHEJiKFVLZiCD9Aq2rh5GT01A"; path = fetchZigArtifact { name = "iterm2_themes"; - url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz"; - hash = "sha256-Vy5muiJ3hJXcOvmFHLhqc+Dvdh74GG6+u/L+EsavDb0="; + url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz"; + hash = "sha256-xpDitXpZrdU/EcgLyG4G0cEiT4r42viy+DJALmy2sQE="; }; } { diff --git a/build.zig.zon.txt b/build.zig.zon.txt index a8cffe2b2..5f06418a7 100644 --- a/build.zig.zon.txt +++ b/build.zig.zon.txt @@ -27,7 +27,7 @@ https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5. https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz https://github.com/glfw/glfw/archive/e7ea71be039836da3a98cea55ae5569cb5eb885c.tar.gz https://github.com/jcollie/ghostty-gobject/releases/download/0.14.0-2025-03-18-21-1/ghostty-gobject-0.14.0-2025-03-18-21-1.tar.zst -https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz +https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.tar.gz https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz From 4d27fc18ebba617188424852655be7250418891a Mon Sep 17 00:00:00 2001 From: taylrfnt <43214679+taylrfnt@users.noreply.github.com> Date: Sat, 3 May 2025 20:50:04 -0500 Subject: [PATCH 14/49] use log stream command instead of link to discussion Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 93aa87b48..7a6b17ff3 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -35,7 +35,7 @@ body: attributes: label: Ghostty Logs description: | - Provide any captured Ghostty logs or stacktraces during your issue reproduction in this field. For macOS users, logs can be found via the command provided in [#7194](https://github.com/ghostty-org/ghostty/discussions/7194) + Provide any captured Ghostty logs or stacktraces during your issue reproduction in this field. For macOS users, logs can be viewed with `sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'`. render: text - type: textarea attributes: From 71f52fd19820730d09b820f582fffd2453b2c88a Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sat, 3 May 2025 20:53:00 -0500 Subject: [PATCH 15/49] re-order ghostty logs field --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 7a6b17ff3..b762674e4 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -31,12 +31,6 @@ body: Example: When using SSH to connect to my remote Linux machine from my local macOS device in Ghostty, I try to run `clear`, and the screen does not clear. Instead, I see the following error message printed to the terminal: `Error opening terminal: xterm-ghostty.` validations: required: true - - type: textarea - attributes: - label: Ghostty Logs - description: | - Provide any captured Ghostty logs or stacktraces during your issue reproduction in this field. For macOS users, logs can be viewed with `sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'`. - render: text - type: textarea attributes: label: Reproduction Steps @@ -49,6 +43,12 @@ body: 4. Observe `xterm-ghostty` error message above. validations: required: true + - type: textarea + attributes: + label: Ghostty Logs + description: | + Provide any captured Ghostty logs or stacktraces during your issue reproduction in this field. For macOS users, logs can be viewed with `sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'`. + render: text - type: textarea attributes: label: Ghostty Version From 41e3c8830f18cde64403de5a9b254129310c1b99 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sat, 3 May 2025 20:55:47 -0500 Subject: [PATCH 16/49] change VT to terminal emulation Co-authored-by: Leah Amelia Chen --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index b762674e4..81b2c6264 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -24,7 +24,7 @@ body: > Here are some recommendations: > - **Input issues:** include your keyboard layout, a screenshot of the terminal inspector's logged keystrokes from the Terminal Inspector's "Keyboard" tab (Linux: ctrl+shift+i; MacOS: cmd+alt+i), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version. > - **Font issues:** include the problematic character(s), the output of `ghostty +show-face` for these character(s), and if they work in other applications. - > - **VT issues (including image rendering issues):** attach an [asciinema](https://docs.asciinema.org/getting-started/) cast file, shell script, or text file for reproduction. + > - **Terminal emulation issues (including image rendering issues):** attach an [asciinema](https://docs.asciinema.org/getting-started/) cast file, shell script, or text file for reproduction. > - **Renderer issues:** (Linux) include your OpenGL version, graphics card, driver version. > - **Crashes:** (macOS) include the [Sentry UUID](https://github.com/ghostty-org/ghostty?tab=readme-ov-file#crash-reports); (Linux) try to reproduce using a debug build and provide the stack trace. placeholder: | From 51b69253222e82106d04bceb1d964aa1428e0bd9 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sat, 3 May 2025 21:00:53 -0500 Subject: [PATCH 17/49] add Linux log hint --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 81b2c6264..fa48c7dce 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -47,7 +47,7 @@ body: attributes: label: Ghostty Logs description: | - Provide any captured Ghostty logs or stacktraces during your issue reproduction in this field. For macOS users, logs can be viewed with `sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'`. + Provide any captured Ghostty logs or stacktraces during your issue reproduction in this field. On Linux, logs can be found by running `ghostty` from the command-line; on macOS, logs can be viewed with `sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'` from another terminal emulator. render: text - type: textarea attributes: From 9c2f8d8ad38e43155573149d8e9786cfa2a8230d Mon Sep 17 00:00:00 2001 From: taylrfnt <43214679+taylrfnt@users.noreply.github.com> Date: Sat, 3 May 2025 21:02:27 -0500 Subject: [PATCH 18/49] cleanup the input issue hint Co-authored-by: trag1c --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index fa48c7dce..fc032aa1a 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -22,7 +22,7 @@ body: >[!TIP] > **Not sure what information to include?** > Here are some recommendations: - > - **Input issues:** include your keyboard layout, a screenshot of the terminal inspector's logged keystrokes from the Terminal Inspector's "Keyboard" tab (Linux: ctrl+shift+i; MacOS: cmd+alt+i), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version. + > - **Input issues:** include your keyboard layout, a screenshot of logged keystrokes from the Terminal Inspector's "Keyboard" tab (Linux: ctrl+shift+i; MacOS: cmd+alt+i), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version. > - **Font issues:** include the problematic character(s), the output of `ghostty +show-face` for these character(s), and if they work in other applications. > - **Terminal emulation issues (including image rendering issues):** attach an [asciinema](https://docs.asciinema.org/getting-started/) cast file, shell script, or text file for reproduction. > - **Renderer issues:** (Linux) include your OpenGL version, graphics card, driver version. From 6ffb6207e7f784a201da889debecd5c91641c650 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sat, 3 May 2025 21:22:49 -0500 Subject: [PATCH 19/49] split out expected & actual behavior fields --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 22 ++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index fc032aa1a..ffe5a7243 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -14,8 +14,6 @@ body: description: | Provide a detailed description of the issue. Include relevant information, such as: - The feature or configuration option you encounter the issue with. - - The expected behavior. - - The actual behavior (and how it deviates from the expected behavior, if it is not immediately obvious). - Screenshots, screen recordings, or other supporting media (as needed). - If this is a regression of an existing issue that was closed or resolved, please include the previous item reference (Discussion, Issue, PR, commit) in your description. @@ -31,6 +29,26 @@ body: Example: When using SSH to connect to my remote Linux machine from my local macOS device in Ghostty, I try to run `clear`, and the screen does not clear. Instead, I see the following error message printed to the terminal: `Error opening terminal: xterm-ghostty.` validations: required: true + - type: textarea + attributes: + label: Expected Behavior + description: | + Provide a summary of how you expect Ghostty to behave in this situation. Include any relevant documentation links. + placeholder: | + Example: When I run `clear`, I expect the screen to be cleared and the prompt to be redrawn at the top of the window. + render: text + validations: + required: true + - type: textarea + attributes: + label: Actual Behavior + description: | + Provide a summary of how Ghostty actually behaves in this situation. If it is not immediately obvious how the actual behavior differs from the expected behavior described above, please be sure to mention the deviation specifically. + placeholder: | + Example: When I run `clear`, the screen is not cleared, and an error is printed: `Error opening terminal: xterm-ghostty.` + render: text + validations: + required: true - type: textarea attributes: label: Reproduction Steps From 0bf168c834508b824914054793c1d08eb73e98a9 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sat, 3 May 2025 21:27:48 -0500 Subject: [PATCH 20/49] terminal inspector no longer proper noun --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index ffe5a7243..f1442b9a4 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -20,7 +20,7 @@ body: >[!TIP] > **Not sure what information to include?** > Here are some recommendations: - > - **Input issues:** include your keyboard layout, a screenshot of logged keystrokes from the Terminal Inspector's "Keyboard" tab (Linux: ctrl+shift+i; MacOS: cmd+alt+i), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version. + > - **Input issues:** include your keyboard layout, a screenshot of logged keystrokes from the terminal inspector's "Keyboard" tab (Linux: ctrl+shift+i; MacOS: cmd+alt+i), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version. > - **Font issues:** include the problematic character(s), the output of `ghostty +show-face` for these character(s), and if they work in other applications. > - **Terminal emulation issues (including image rendering issues):** attach an [asciinema](https://docs.asciinema.org/getting-started/) cast file, shell script, or text file for reproduction. > - **Renderer issues:** (Linux) include your OpenGL version, graphics card, driver version. From 1f9a4e6794a93c740722f71ecbb14ce227cdaebd Mon Sep 17 00:00:00 2001 From: taylrfnt <43214679+taylrfnt@users.noreply.github.com> Date: Sat, 3 May 2025 21:29:59 -0500 Subject: [PATCH 21/49] more direct naming of minimal configuration Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index f1442b9a4..8537c19d5 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -116,7 +116,7 @@ body: required: false - type: textarea attributes: - label: Ghostty Configuration (Minimal) + label: Minimal Ghostty Configuration description: | Please provide the **minimum** configuration needed to reproduce this issue. If you cannot determine this, paste the contents of your Ghostty configuration file here. placeholder: | From cc95475ae996af3ceb5864106b1cce39bf5000b9 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sat, 3 May 2025 21:48:59 -0500 Subject: [PATCH 22/49] update placeholder text remove the 'example:' prefixes and make them sound less like it's AI-generated --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 8537c19d5..f6bc0b058 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -26,7 +26,7 @@ body: > - **Renderer issues:** (Linux) include your OpenGL version, graphics card, driver version. > - **Crashes:** (macOS) include the [Sentry UUID](https://github.com/ghostty-org/ghostty?tab=readme-ov-file#crash-reports); (Linux) try to reproduce using a debug build and provide the stack trace. placeholder: | - Example: When using SSH to connect to my remote Linux machine from my local macOS device in Ghostty, I try to run `clear`, and the screen does not clear. Instead, I see the following error message printed to the terminal: `Error opening terminal: xterm-ghostty.` + When using SSH to connect to my remote Linux machine from my local macOS device in Ghostty, I try to run `clear`, and the screen does not clear. Instead, I see the following error message printed to the terminal: `Error opening terminal: xterm-ghostty.` validations: required: true - type: textarea @@ -35,7 +35,7 @@ body: description: | Provide a summary of how you expect Ghostty to behave in this situation. Include any relevant documentation links. placeholder: | - Example: When I run `clear`, I expect the screen to be cleared and the prompt to be redrawn at the top of the window. + The screen to be cleared and the prompt is redrawn at the top of the window. render: text validations: required: true @@ -45,7 +45,7 @@ body: description: | Provide a summary of how Ghostty actually behaves in this situation. If it is not immediately obvious how the actual behavior differs from the expected behavior described above, please be sure to mention the deviation specifically. placeholder: | - Example: When I run `clear`, the screen is not cleared, and an error is printed: `Error opening terminal: xterm-ghostty.` + The screen is not cleared, and an error is printed: `Error opening terminal: xterm-ghostty`. render: text validations: required: true From 050375cbb63df93d5dbefa2565675a7a8d0590e7 Mon Sep 17 00:00:00 2001 From: taylrfnt <43214679+taylrfnt@users.noreply.github.com> Date: Sat, 3 May 2025 21:56:08 -0500 Subject: [PATCH 23/49] make minimum configuration more explicit Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index f6bc0b058..8cdb34737 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -118,7 +118,7 @@ body: attributes: label: Minimal Ghostty Configuration description: | - Please provide the **minimum** configuration needed to reproduce this issue. If you cannot determine this, paste the contents of your Ghostty configuration file here. + Please provide the **minimum** configuration needed to reproduce this issue. If you can still reproduce the issue with one of the lines removed, do not include that line. If and **only** if you are not able to determine this, paste the contents of your Ghostty configuration file here. placeholder: | font-family = CommitMono Nerd Font font-family-bold = CommitMono Nerd Font From 233ef4f7822d4110f179c31d36f2d0b8e834972b Mon Sep 17 00:00:00 2001 From: taylrfnt <43214679+taylrfnt@users.noreply.github.com> Date: Sun, 4 May 2025 11:58:50 -0500 Subject: [PATCH 24/49] more updates to expected behavior placeholder Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 8cdb34737..aabe19c27 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -35,7 +35,7 @@ body: description: | Provide a summary of how you expect Ghostty to behave in this situation. Include any relevant documentation links. placeholder: | - The screen to be cleared and the prompt is redrawn at the top of the window. + The screen is cleared and the prompt is redrawn at the top of the window. render: text validations: required: true From 46d3de26fca9377f01c6a979da57580d0e85371f Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sun, 4 May 2025 11:59:57 -0500 Subject: [PATCH 25/49] remove renderers prone to jailbreak --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index aabe19c27..7f402183c 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -36,7 +36,6 @@ body: Provide a summary of how you expect Ghostty to behave in this situation. Include any relevant documentation links. placeholder: | The screen is cleared and the prompt is redrawn at the top of the window. - render: text validations: required: true - type: textarea @@ -46,7 +45,6 @@ body: Provide a summary of how Ghostty actually behaves in this situation. If it is not immediately obvious how the actual behavior differs from the expected behavior described above, please be sure to mention the deviation specifically. placeholder: | The screen is not cleared, and an error is printed: `Error opening terminal: xterm-ghostty`. - render: text validations: required: true - type: textarea @@ -143,7 +141,6 @@ body: set-option -sa terminal-overrides ",xterm*:Tc" set -g base-index 1 setw -g pane-base-index 1 - render: text validations: required: false - type: markdown From 431116c9d8702f2e1149ad4b5b22e6130091cc1f Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sun, 4 May 2025 12:01:22 -0500 Subject: [PATCH 26/49] do not ask users for a summary --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 7f402183c..8b304b4b6 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -33,7 +33,7 @@ body: attributes: label: Expected Behavior description: | - Provide a summary of how you expect Ghostty to behave in this situation. Include any relevant documentation links. + Describe how you expect Ghostty to behave in this situation. Include any relevant documentation links. placeholder: | The screen is cleared and the prompt is redrawn at the top of the window. validations: @@ -42,7 +42,7 @@ body: attributes: label: Actual Behavior description: | - Provide a summary of how Ghostty actually behaves in this situation. If it is not immediately obvious how the actual behavior differs from the expected behavior described above, please be sure to mention the deviation specifically. + Describe how Ghostty actually behaves in this situation. If it is not immediately obvious how the actual behavior differs from the expected behavior described above, please be sure to mention the deviation specifically. placeholder: | The screen is not cleared, and an error is printed: `Error opening terminal: xterm-ghostty`. validations: From 8dfa6beb15c1c87dba9ac450a01ee25b02a3e0f4 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sun, 4 May 2025 12:04:09 -0500 Subject: [PATCH 27/49] add acknowledgement for previewing format before submitting Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 8b304b4b6..26992962c 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -157,3 +157,5 @@ body: required: true - label: I have searched the Ghostty repository (both open and closed Discussions and Issues) and confirm this is not a duplicate of an existing issue or discussion. required: true + - label: I have checked the “Preview” tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (```) on separate lines. + required: true From 11db0ed8ae759936923475541976aac510056fc6 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Sun, 4 May 2025 12:17:29 -0500 Subject: [PATCH 28/49] re-apply formatting & overwrite, not append --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 26992962c..5e49000c9 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -157,5 +157,5 @@ body: required: true - label: I have searched the Ghostty repository (both open and closed Discussions and Issues) and confirm this is not a duplicate of an existing issue or discussion. required: true - - label: I have checked the “Preview” tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (```) on separate lines. + - label: I have checked the “Preview” tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (```) on separate lines. required: true From 37974dba06549ab714cbf36694e3439d9dbc9cf9 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Fri, 2 May 2025 07:42:51 -0400 Subject: [PATCH 29/49] bash: explicitly request a login shell Prior to #7044, on macOS, our shell integrated command line would be executed under `exec -l`, which caused bash to be started as a login shell. Now that we're using direct command execution, add `--login` to our bash command's arguments on macOS to get that same behavior. --- src/termio/shell_integration.zig | 64 +++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/src/termio/shell_integration.zig b/src/termio/shell_integration.zig index 2cf809694..fb62327d3 100644 --- a/src/termio/shell_integration.zig +++ b/src/termio/shell_integration.zig @@ -239,7 +239,7 @@ fn setupBash( resource_dir: []const u8, env: *EnvMap, ) !?config.Command { - var args = try std.ArrayList([:0]const u8).initCapacity(alloc, 2); + var args = try std.ArrayList([:0]const u8).initCapacity(alloc, 3); defer args.deinit(); // Iterator that yields each argument in the original command line. @@ -247,12 +247,17 @@ fn setupBash( var iter = try command.argIterator(alloc); defer iter.deinit(); - // Start accumulating arguments with the executable and `--posix` mode flag. + // Start accumulating arguments with the executable and initial flags. if (iter.next()) |exe| { try args.append(try alloc.dupeZ(u8, exe)); } else return null; try args.append("--posix"); + // On macOS, we request a login shell to match that platform's norms. + if (comptime builtin.target.os.tag.isDarwin()) { + try args.append("--login"); + } + // Stores the list of intercepted command line flags that will be passed // to our shell integration script: --norc --noprofile // We always include at least "1" so the script can differentiate between @@ -342,9 +347,12 @@ test "bash" { const command = try setupBash(alloc, .{ .shell = "bash" }, ".", &env); - try testing.expectEqual(2, command.?.direct.len); + try testing.expect(command.?.direct.len >= 2); try testing.expectEqualStrings("bash", command.?.direct[0]); try testing.expectEqualStrings("--posix", command.?.direct[1]); + if (comptime builtin.target.os.tag.isDarwin()) { + try testing.expectEqualStrings("--login", command.?.direct[2]); + } try testing.expectEqualStrings("./shell-integration/bash/ghostty.bash", env.get("ENV").?); try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_INJECT").?); } @@ -387,9 +395,12 @@ test "bash: inject flags" { const command = try setupBash(alloc, .{ .shell = "bash --norc" }, ".", &env); - try testing.expectEqual(2, command.?.direct.len); + try testing.expect(command.?.direct.len >= 2); try testing.expectEqualStrings("bash", command.?.direct[0]); try testing.expectEqualStrings("--posix", command.?.direct[1]); + if (comptime builtin.target.os.tag.isDarwin()) { + try testing.expectEqualStrings("--login", command.?.direct[2]); + } try testing.expectEqualStrings("1 --norc", env.get("GHOSTTY_BASH_INJECT").?); } @@ -400,9 +411,12 @@ test "bash: inject flags" { const command = try setupBash(alloc, .{ .shell = "bash --noprofile" }, ".", &env); - try testing.expectEqual(2, command.?.direct.len); + try testing.expect(command.?.direct.len >= 2); try testing.expectEqualStrings("bash", command.?.direct[0]); try testing.expectEqualStrings("--posix", command.?.direct[1]); + if (comptime builtin.target.os.tag.isDarwin()) { + try testing.expectEqualStrings("--login", command.?.direct[2]); + } try testing.expectEqualStrings("1 --noprofile", env.get("GHOSTTY_BASH_INJECT").?); } } @@ -419,18 +433,24 @@ test "bash: rcfile" { // bash --rcfile { const command = try setupBash(alloc, .{ .shell = "bash --rcfile profile.sh" }, ".", &env); - try testing.expectEqual(2, command.?.direct.len); + try testing.expect(command.?.direct.len >= 2); try testing.expectEqualStrings("bash", command.?.direct[0]); try testing.expectEqualStrings("--posix", command.?.direct[1]); + if (comptime builtin.target.os.tag.isDarwin()) { + try testing.expectEqualStrings("--login", command.?.direct[2]); + } try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?); } // bash --init-file { const command = try setupBash(alloc, .{ .shell = "bash --init-file profile.sh" }, ".", &env); - try testing.expectEqual(2, command.?.direct.len); + try testing.expect(command.?.direct.len >= 2); try testing.expectEqualStrings("bash", command.?.direct[0]); try testing.expectEqualStrings("--posix", command.?.direct[1]); + if (comptime builtin.target.os.tag.isDarwin()) { + try testing.expectEqualStrings("--login", command.?.direct[2]); + } try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?); } } @@ -476,25 +496,35 @@ test "bash: additional arguments" { // "-" argument separator { const command = try setupBash(alloc, .{ .shell = "bash - --arg file1 file2" }, ".", &env); - try testing.expectEqual(6, command.?.direct.len); + try testing.expect(command.?.direct.len >= 6); try testing.expectEqualStrings("bash", command.?.direct[0]); try testing.expectEqualStrings("--posix", command.?.direct[1]); - try testing.expectEqualStrings("-", command.?.direct[2]); - try testing.expectEqualStrings("--arg", command.?.direct[3]); - try testing.expectEqualStrings("file1", command.?.direct[4]); - try testing.expectEqualStrings("file2", command.?.direct[5]); + if (comptime builtin.target.os.tag.isDarwin()) { + try testing.expectEqualStrings("--login", command.?.direct[2]); + } + + const offset = if (comptime builtin.target.os.tag.isDarwin()) 3 else 2; + try testing.expectEqualStrings("-", command.?.direct[offset + 0]); + try testing.expectEqualStrings("--arg", command.?.direct[offset + 1]); + try testing.expectEqualStrings("file1", command.?.direct[offset + 2]); + try testing.expectEqualStrings("file2", command.?.direct[offset + 3]); } // "--" argument separator { const command = try setupBash(alloc, .{ .shell = "bash -- --arg file1 file2" }, ".", &env); - try testing.expectEqual(6, command.?.direct.len); + try testing.expect(command.?.direct.len >= 6); try testing.expectEqualStrings("bash", command.?.direct[0]); try testing.expectEqualStrings("--posix", command.?.direct[1]); - try testing.expectEqualStrings("--", command.?.direct[2]); - try testing.expectEqualStrings("--arg", command.?.direct[3]); - try testing.expectEqualStrings("file1", command.?.direct[4]); - try testing.expectEqualStrings("file2", command.?.direct[5]); + if (comptime builtin.target.os.tag.isDarwin()) { + try testing.expectEqualStrings("--login", command.?.direct[2]); + } + + const offset = if (comptime builtin.target.os.tag.isDarwin()) 3 else 2; + try testing.expectEqualStrings("--", command.?.direct[offset + 0]); + try testing.expectEqualStrings("--arg", command.?.direct[offset + 1]); + try testing.expectEqualStrings("file1", command.?.direct[offset + 2]); + try testing.expectEqualStrings("file2", command.?.direct[offset + 3]); } } From f84367880b02346c56f79c16cc1b4e93b2e5437f Mon Sep 17 00:00:00 2001 From: taylrfnt <43214679+taylrfnt@users.noreply.github.com> Date: Mon, 5 May 2025 19:23:40 -0500 Subject: [PATCH 30/49] Properly enclose code block backticks Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 5e49000c9..1448b7385 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -157,5 +157,5 @@ body: required: true - label: I have searched the Ghostty repository (both open and closed Discussions and Issues) and confirm this is not a duplicate of an existing issue or discussion. required: true - - label: I have checked the “Preview” tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (```) on separate lines. + - label: I have checked the “Preview” tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (` ``` `) on separate lines. required: true From ad16f984cf1f70e189a39f507efe51da7fefca36 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Mon, 5 May 2025 19:26:46 -0500 Subject: [PATCH 31/49] remove smart quotes in favor of ascii Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 1448b7385..0ed334592 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -157,5 +157,5 @@ body: required: true - label: I have searched the Ghostty repository (both open and closed Discussions and Issues) and confirm this is not a duplicate of an existing issue or discussion. required: true - - label: I have checked the “Preview” tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (` ``` `) on separate lines. + - label: I have checked the "Preview" tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (` ``` `) on separate lines. required: true From f9c1b6b7cf477a49b771c69bb6af890c148754af Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Mon, 5 May 2025 19:28:31 -0500 Subject: [PATCH 32/49] fixup markdown in additional config field Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 0ed334592..c71a9c496 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -135,7 +135,7 @@ body: description: | If your issue involves other programs, tools, or applications in addition to Ghostty (e.g. Neovim, tmux, Zellij, etc.), please provide the minimum configuration and versions needed for all relevant programs to reproduce the issue here. If you use custom CSS or shaders for Ghostty, also include them here, if applicable to your issue. placeholder: | - `tmux.conf` (tmux 3.5a) + #### `tmux.conf` (tmux 3.5a) --- set -g default-terminal "tmux-256color" set-option -sa terminal-overrides ",xterm*:Tc" From d0b9242f496db5b60979f0f57b2b83d4171484a4 Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Mon, 5 May 2025 21:34:33 -0500 Subject: [PATCH 33/49] replace dashes with code block backticks for additional config Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index c71a9c496..6f042ba86 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -136,11 +136,12 @@ body: If your issue involves other programs, tools, or applications in addition to Ghostty (e.g. Neovim, tmux, Zellij, etc.), please provide the minimum configuration and versions needed for all relevant programs to reproduce the issue here. If you use custom CSS or shaders for Ghostty, also include them here, if applicable to your issue. placeholder: | #### `tmux.conf` (tmux 3.5a) - --- + ``` set -g default-terminal "tmux-256color" set-option -sa terminal-overrides ",xterm*:Tc" set -g base-index 1 setw -g pane-base-index 1 + ``` validations: required: false - type: markdown From ad0d426bba0a0003e01fd367dc1da42616e413a9 Mon Sep 17 00:00:00 2001 From: taylrfnt <43214679+taylrfnt@users.noreply.github.com> Date: Mon, 5 May 2025 21:36:38 -0500 Subject: [PATCH 34/49] consistent spacing with tip & important highlights Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 6f042ba86..605a34e6c 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -17,7 +17,7 @@ body: - Screenshots, screen recordings, or other supporting media (as needed). - If this is a regression of an existing issue that was closed or resolved, please include the previous item reference (Discussion, Issue, PR, commit) in your description. - >[!TIP] + > [!TIP] > **Not sure what information to include?** > Here are some recommendations: > - **Input issues:** include your keyboard layout, a screenshot of logged keystrokes from the terminal inspector's "Keyboard" tab (Linux: ctrl+shift+i; MacOS: cmd+alt+i), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version. From ac11ebbb4ad61c59dc69e462ef19fbc9752d2efd Mon Sep 17 00:00:00 2001 From: taylrfnt Date: Mon, 5 May 2025 21:37:49 -0500 Subject: [PATCH 35/49] update applied label to proper name Co-authored-by: Kat <65649991+00-kat@users.noreply.github.com> --- .github/DISCUSSION_TEMPLATE/issue-triage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/DISCUSSION_TEMPLATE/issue-triage.yml b/.github/DISCUSSION_TEMPLATE/issue-triage.yml index 605a34e6c..270bb86f5 100644 --- a/.github/DISCUSSION_TEMPLATE/issue-triage.yml +++ b/.github/DISCUSSION_TEMPLATE/issue-triage.yml @@ -1,4 +1,4 @@ -labels: ["needs confirmation"] +labels: ["needs-confirmation"] body: - type: markdown attributes: From 1bf686d324d7b5c507b3898f188a2fe2f77b589e Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Tue, 6 May 2025 08:44:52 -0500 Subject: [PATCH 36/49] gtk: fix comment about adwaita version --- src/input/Binding.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 10e16f1fe..434ed9f0d 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -345,7 +345,7 @@ pub const Action = union(enum) { move_tab: isize, /// Toggle the tab overview. - /// This only works with libadwaita enabled currently. + /// This only works with libadwaita version 1.4.0 or newer. toggle_tab_overview, /// Change the title of the current focused surface via a prompt. From 3c405a591ae895280e5e617bd267fdfa8d3bfdce Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 6 May 2025 07:19:34 -0700 Subject: [PATCH 37/49] update flatpak cache --- flatpak/zig-packages.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flatpak/zig-packages.json b/flatpak/zig-packages.json index 0c36a600a..bc3b6cd0c 100644 --- a/flatpak/zig-packages.json +++ b/flatpak/zig-packages.json @@ -67,9 +67,9 @@ }, { "type": "archive", - "url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz", - "dest": "vendor/p/N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym", - "sha256": "572e66ba22778495dc3af9851cb86a73e0ef761ef8186ebebbf2fe12c6af0dbd" + "url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz", + "dest": "vendor/p/N-V-__8AAHOASAQuLADcCSHLHEJiKFVLZiCD9Aq2rh5GT01A", + "sha256": "c690e2b57a59add53f11c80bc86e06d1c1224f8af8daf8b2f832402e6cb6b101" }, { "type": "archive", From 9221d392dee482f568112de7b3ab79571a04b763 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 6 May 2025 07:20:42 -0700 Subject: [PATCH 38/49] ci: add flatpak JSON for iterm2 theme updates I forgot to add the path in the GitHub action. --- .github/workflows/update-colorschemes.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update-colorschemes.yml b/.github/workflows/update-colorschemes.yml index e8e3fe99a..fed6d2db7 100644 --- a/.github/workflows/update-colorschemes.yml +++ b/.github/workflows/update-colorschemes.yml @@ -71,6 +71,7 @@ jobs: build.zig.zon.nix build.zig.zon.txt build.zig.zon.json + flatpak/zig-packages.json body: | Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }} labels: dependencies From 9b789172464a67037d89b82e9b296ae4da1ff3cf Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 6 May 2025 10:32:39 -0700 Subject: [PATCH 39/49] macOS: handle scenario cgWindowId is nil Fixes #7114 Supercedes #7271 This fixes a crash that could occur with non-native fullscreen and `fullscreen = true` set at once. The "windowNumber" can be `<= 0` if the window "doesn't have a window device." I don't fully know all the scenarios this is true but it is true when the window is not visible, at least. --- macos/Sources/Helpers/Fullscreen.swift | 25 ++++++++++++------- .../Sources/Helpers/NSWindow+Extension.swift | 8 ++++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/macos/Sources/Helpers/Fullscreen.swift b/macos/Sources/Helpers/Fullscreen.swift index b6fb08271..5233af62d 100644 --- a/macos/Sources/Helpers/Fullscreen.swift +++ b/macos/Sources/Helpers/Fullscreen.swift @@ -355,16 +355,23 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { self.styleMask = window.styleMask self.dock = window.screen?.hasDock ?? false - // We hide the menu only if this window is not on any fullscreen - // spaces. We do this because fullscreen spaces already hide the - // menu and if we insert/remove this presentation option we get - // issues (see #7075) - let activeSpace = CGSSpace.active() - let spaces = CGSSpace.list(for: window.cgWindowId) - if spaces.contains(activeSpace) { - self.menu = activeSpace.type != .fullscreen + if let cgWindowId = window.cgWindowId { + // We hide the menu only if this window is not on any fullscreen + // spaces. We do this because fullscreen spaces already hide the + // menu and if we insert/remove this presentation option we get + // issues (see #7075) + let activeSpace = CGSSpace.active() + let spaces = CGSSpace.list(for: cgWindowId) + if spaces.contains(activeSpace) { + self.menu = activeSpace.type != .fullscreen + } else { + self.menu = spaces.allSatisfy { $0.type != .fullscreen } + } } else { - self.menu = spaces.allSatisfy { $0.type != .fullscreen } + // Window doesn't have a window device, its not visible or something. + // In this case, we assume we can hide the menu. We may want to do + // something more sophisticated but this works for now. + self.menu = true } } } diff --git a/macos/Sources/Helpers/NSWindow+Extension.swift b/macos/Sources/Helpers/NSWindow+Extension.swift index c7523bdb7..06a9fa4e0 100644 --- a/macos/Sources/Helpers/NSWindow+Extension.swift +++ b/macos/Sources/Helpers/NSWindow+Extension.swift @@ -2,7 +2,11 @@ import AppKit extension NSWindow { /// Get the CGWindowID type for the window (used for low level CoreGraphics APIs). - var cgWindowId: CGWindowID { - CGWindowID(windowNumber) + var cgWindowId: CGWindowID? { + // "If the window doesn’t have a window device, the value of this + // property is equal to or less than 0." - Docs. In practice I've + // found this is true if a window is not visible. + guard windowNumber > 0 else { return nil } + return CGWindowID(windowNumber) } } From e2a0f439c6332884577eabb2491a343afde22c42 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 6 May 2025 12:57:53 -0700 Subject: [PATCH 40/49] macOS: save/restore firstResponder on non-native fullscreen Fixes #6999 It appears that at some point one of the operations causes focus to move away for non-native fullscreen. We previously relied on the delegate method to restore this but a better approach appears to handle this directly in the fullscreen implementations. This fixes the linked issue. I still think long term all the `Ghostty.moveFocus` stuff is a code smell and we should be auditing all that code to see if we can eliminate it. But this is a step in the right direction, and removes one of those uses. --- .../Terminal/BaseTerminalController.swift | 8 -------- .../Features/Terminal/TerminalController.swift | 4 +--- macos/Sources/Helpers/Fullscreen.swift | 18 ++++++++++++++++++ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/macos/Sources/Features/Terminal/BaseTerminalController.swift b/macos/Sources/Features/Terminal/BaseTerminalController.swift index b502e56e0..36dc5f93c 100644 --- a/macos/Sources/Features/Terminal/BaseTerminalController.swift +++ b/macos/Sources/Features/Terminal/BaseTerminalController.swift @@ -361,14 +361,6 @@ class BaseTerminalController: NSWindowController, } } - func fullscreenDidChange() { - // For some reason focus can get lost when we change fullscreen. Regardless of - // mode above we just move it back. - if let focusedSurface { - Ghostty.moveFocus(to: focusedSurface) - } - } - // MARK: Clipboard Confirmation @objc private func onConfirmClipboardRequest(notification: SwiftUI.Notification) { diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index f384b97ed..cf2dd3348 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -121,9 +121,7 @@ class TerminalController: BaseTerminalController { } - override func fullscreenDidChange() { - super.fullscreenDidChange() - + func fullscreenDidChange() { // When our fullscreen state changes, we resync our appearance because some // properties change when fullscreen or not. guard let focusedSurface else { return } diff --git a/macos/Sources/Helpers/Fullscreen.swift b/macos/Sources/Helpers/Fullscreen.swift index 5233af62d..6094bf844 100644 --- a/macos/Sources/Helpers/Fullscreen.swift +++ b/macos/Sources/Helpers/Fullscreen.swift @@ -171,6 +171,13 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { guard let savedState = SavedState(window) else { return } self.savedState = savedState + // Get our current first responder on this window. For non-native fullscreen + // we have to restore this because for some reason the operations below + // lose it (see: https://github.com/ghostty-org/ghostty/issues/6999). + // I don't know the root cause here so if we can figure that out there may + // be a nicer way than this. + let firstResponder = window.firstResponder + // We hide the dock if the window is on a screen with the dock. // We must hide the dock FIRST then hide the menu: // If you specify autoHideMenuBar, it must be accompanied by either hideDock or autoHideDock. @@ -207,6 +214,10 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { // https://github.com/ghostty-org/ghostty/issues/1996 DispatchQueue.main.async { self.window.setFrame(self.fullscreenFrame(screen), display: true) + if let firstResponder { + self.window.makeFirstResponder(firstResponder) + } + self.delegate?.fullscreenDidChange() } } @@ -220,6 +231,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { let center = NotificationCenter.default center.removeObserver(self, name: NSWindow.didChangeScreenNotification, object: window) + // See enter where we do the same thing to understand why. + let firstResponder = window.firstResponder + // Unhide our elements if savedState.dock { unhideDock() @@ -258,6 +272,10 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { } } + if let firstResponder { + window.makeFirstResponder(firstResponder) + } + // Unset our saved state, we're restored! self.savedState = nil From b6f120a7492a6cce071b336570fa7ba1b746dd88 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 26 Mar 2025 01:30:15 -0500 Subject: [PATCH 41/49] termio, flatpak: support spawning terminals in cwd Implements path access testing for Flatpak via test spawning. This is required since Flatpak reserves certain paths from being accessible regardless of permissions. Ref: https://docs.flatpak.org/en/latest/sandbox-permissions.html#reserved-paths --- src/termio/Exec.zig | 60 +++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index abe49a47b..23c626879 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -745,7 +745,7 @@ const Subprocess = struct { }); arena: std.heap.ArenaAllocator, - cwd: ?[]const u8, + cwd: ?[:0]const u8, env: ?EnvMap, args: []const [:0]const u8, grid_size: renderer.GridSize, @@ -985,8 +985,8 @@ const Subprocess = struct { // We have to copy the cwd because there is no guarantee that // pointers in full_config remain valid. - const cwd: ?[]u8 = if (cfg.working_directory) |cwd| - try alloc.dupe(u8, cwd) + const cwd: ?[:0]u8 = if (cfg.working_directory) |cwd| + try alloc.dupeZ(u8, cwd) else null; @@ -1048,6 +1048,47 @@ const Subprocess = struct { log.debug("starting command command={s}", .{self.args}); + // If we can't access the cwd, then don't set any cwd and inherit. + // This is important because our cwd can be set by the shell (OSC 7) + // and we don't want to break new windows. + const cwd: ?[:0]const u8 = if (self.cwd) |proposed| cwd: { + if ((comptime build_config.flatpak) and internal_os.isFlatpak()) { + // Flatpak sandboxing prevents access to certain reserved paths + // regardless of configured permissions. Perform a test spawn + // to get around this problem + // + // https://docs.flatpak.org/en/latest/sandbox-permissions.html#reserved-paths + log.info("flatpak detected, will use host command to verify cwd access", .{}); + const dev_null = try std.fs.cwd().openFile("/dev/null", .{ .mode = .read_write }); + defer dev_null.close(); + var cmd: internal_os.FlatpakHostCommand = .{ + .argv = &[_][]const u8{ + "/bin/sh", + "-c", + ":", + }, + .cwd = proposed, + .stdin = dev_null.handle, + .stdout = dev_null.handle, + .stderr = dev_null.handle, + }; + _ = cmd.spawn(alloc) catch |err| { + log.warn("cannot spawn command at cwd, ignoring: {}", .{err}); + break :cwd null; + }; + _ = try cmd.wait(); + + break :cwd proposed; + } + + if (std.fs.cwd().access(proposed, .{})) { + break :cwd proposed; + } else |err| { + log.warn("cannot access cwd, ignoring: {}", .{err}); + break :cwd null; + } + } else null; + // In flatpak, we use the HostCommand to execute our shell. if (internal_os.isFlatpak()) flatpak: { if (comptime !build_config.flatpak) { @@ -1058,6 +1099,7 @@ const Subprocess = struct { // Flatpak command must have a stable pointer. self.flatpak_command = .{ .argv = self.args, + .cwd = cwd, .env = if (self.env) |*env| env else null, .stdin = pty.slave, .stdout = pty.slave, @@ -1083,18 +1125,6 @@ const Subprocess = struct { }; } - // If we can't access the cwd, then don't set any cwd and inherit. - // This is important because our cwd can be set by the shell (OSC 7) - // and we don't want to break new windows. - const cwd: ?[]const u8 = if (self.cwd) |proposed| cwd: { - if (std.fs.cwd().access(proposed, .{})) { - break :cwd proposed; - } else |err| { - log.warn("cannot access cwd, ignoring: {}", .{err}); - break :cwd null; - } - } else null; - // Build our subcommand var cmd: Command = .{ .path = self.args[0], From 2caa8a3fe117121bd91ec86ccf1b9ec64e3f5688 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 6 May 2025 14:54:59 -0700 Subject: [PATCH 42/49] macOS: move window title handling fully to AppKit Fixes #7236 Supersedes #7249 This removes all of our `focusedValue`-based tracking of the surface title and moves it completely to the window controller. The window controller now sets up event listeners (via Combine) when the focused surface changes and updates the window title accordingly. There is some complicated logic here to handle when we lose focus to something other than a surface. In this case, we want our title to be the last focused surface so long as it exists. --- .../Terminal/BaseTerminalController.swift | 23 +++++++++++++++++++ .../Features/Terminal/TerminalView.swift | 15 ------------ .../Ghostty/Ghostty.TerminalSplit.swift | 3 --- macos/Sources/Ghostty/InspectorView.swift | 1 - macos/Sources/Ghostty/SurfaceView.swift | 12 ---------- 5 files changed, 23 insertions(+), 31 deletions(-) diff --git a/macos/Sources/Features/Terminal/BaseTerminalController.swift b/macos/Sources/Features/Terminal/BaseTerminalController.swift index 98e3b87f9..62384586a 100644 --- a/macos/Sources/Features/Terminal/BaseTerminalController.swift +++ b/macos/Sources/Features/Terminal/BaseTerminalController.swift @@ -1,5 +1,6 @@ import Cocoa import SwiftUI +import Combine import GhosttyKit /// A base class for windows that can contain Ghostty windows. This base class implements @@ -71,6 +72,9 @@ class BaseTerminalController: NSWindowController, /// The configuration derived from the Ghostty config so we don't need to rely on references. private var derivedConfig: DerivedConfig + /// The cancellables related to our focused surface. + private var focusedSurfaceCancellables: Set = [] + struct SavedFrame { let window: NSRect let screen: NSRect @@ -286,7 +290,26 @@ class BaseTerminalController: NSWindowController, func surfaceTreeDidChange() {} func focusedSurfaceDidChange(to: Ghostty.SurfaceView?) { + let lastFocusedSurface = focusedSurface focusedSurface = to + + // Important to cancel any prior subscriptions + focusedSurfaceCancellables = [] + + // Setup our title listener. If we have a focused surface we always use that. + // Otherwise, we try to use our last focused surface. In either case, we only + // want to care if the surface is in the tree so we don't listen to titles of + // closed surfaces. + if let titleSurface = focusedSurface ?? lastFocusedSurface, + surfaceTree?.contains(view: titleSurface) ?? false { + // If we have a surface, we want to listen for title changes. + titleSurface.$title + .sink { [weak self] in self?.titleDidChange(to: $0) } + .store(in: &focusedSurfaceCancellables) + } else { + // There is no surface to listen to titles for. + titleDidChange(to: "👻") + } } func titleDidChange(to: String) { diff --git a/macos/Sources/Features/Terminal/TerminalView.swift b/macos/Sources/Features/Terminal/TerminalView.swift index 1178c75a5..7caceb071 100644 --- a/macos/Sources/Features/Terminal/TerminalView.swift +++ b/macos/Sources/Features/Terminal/TerminalView.swift @@ -8,9 +8,6 @@ protocol TerminalViewDelegate: AnyObject { /// Called when the currently focused surface changed. This can be nil. func focusedSurfaceDidChange(to: Ghostty.SurfaceView?) - /// The title of the terminal should change. - func titleDidChange(to: String) - /// The URL of the pwd should change. func pwdDidChange(to: URL?) @@ -59,19 +56,10 @@ struct TerminalView: View { // Various state values sent back up from the currently focused terminals. @FocusedValue(\.ghosttySurfaceView) private var focusedSurface - @FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle @FocusedValue(\.ghosttySurfacePwd) private var surfacePwd @FocusedValue(\.ghosttySurfaceZoomed) private var zoomedSplit @FocusedValue(\.ghosttySurfaceCellSize) private var cellSize - // The title for our window - private var title: String { - if let surfaceTitle, !surfaceTitle.isEmpty { - return surfaceTitle - } - return "👻" - } - // The pwd of the focused surface as a URL private var pwdURL: URL? { guard let surfacePwd, surfacePwd != "" else { return nil } @@ -105,9 +93,6 @@ struct TerminalView: View { self.delegate?.focusedSurfaceDidChange(to: newValue) } } - .onChange(of: title) { newValue in - self.delegate?.titleDidChange(to: newValue) - } .onChange(of: pwdURL) { newValue in self.delegate?.pwdDidChange(to: newValue) } diff --git a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift index 127c925e1..3e942d774 100644 --- a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift +++ b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift @@ -45,8 +45,6 @@ extension Ghostty { /// this one. @Binding var zoomedSurface: SurfaceView? - @FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle: String? - var body: some View { let center = NotificationCenter.default let pubZoom = center.publisher(for: Notification.didToggleSplitZoom) @@ -77,7 +75,6 @@ extension Ghostty { .onReceive(pubZoom) { onZoom(notification: $0) } } } - .navigationTitle(surfaceTitle ?? "Ghostty") .id(node) // Needed for change detection on node } else { // On these events we want to reset the split state and call it. diff --git a/macos/Sources/Ghostty/InspectorView.swift b/macos/Sources/Ghostty/InspectorView.swift index b6147647e..a6e80bd47 100644 --- a/macos/Sources/Ghostty/InspectorView.swift +++ b/macos/Sources/Ghostty/InspectorView.swift @@ -31,7 +31,6 @@ extension Ghostty { }, right: { InspectorViewRepresentable(surfaceView: surfaceView) .focused($inspectorFocus) - .focusedValue(\.ghosttySurfaceTitle, surfaceView.title) .focusedValue(\.ghosttySurfaceView, surfaceView) }) } diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index 3b9c10067..1e9a4cfef 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -6,14 +6,12 @@ extension Ghostty { /// Render a terminal for the active app in the environment. struct Terminal: View { @EnvironmentObject private var ghostty: Ghostty.App - @FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle: String? var body: some View { if let app = self.ghostty.app { SurfaceForApp(app) { surfaceView in SurfaceWrapper(surfaceView: surfaceView) } - .navigationTitle(surfaceTitle ?? "Ghostty") } } } @@ -83,7 +81,6 @@ extension Ghostty { Surface(view: surfaceView, size: geo.size) .focused($surfaceFocus) - .focusedValue(\.ghosttySurfaceTitle, title) .focusedValue(\.ghosttySurfacePwd, surfaceView.pwd) .focusedValue(\.ghosttySurfaceView, surfaceView) .focusedValue(\.ghosttySurfaceCellSize, surfaceView.cellSize) @@ -496,15 +493,6 @@ extension FocusedValues { typealias Value = Ghostty.SurfaceView } - var ghosttySurfaceTitle: String? { - get { self[FocusedGhosttySurfaceTitle.self] } - set { self[FocusedGhosttySurfaceTitle.self] = newValue } - } - - struct FocusedGhosttySurfaceTitle: FocusedValueKey { - typealias Value = String - } - var ghosttySurfacePwd: String? { get { self[FocusedGhosttySurfacePwd.self] } set { self[FocusedGhosttySurfacePwd.self] = newValue } From 071531c5c5233f44e32619a6d6486a6bebfe18b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sat, 3 May 2025 09:07:12 -0700 Subject: [PATCH 43/49] Add "Scroll to Selection" command --- src/Surface.zig | 8 ++++++++ src/input/Binding.zig | 2 ++ src/input/command.zig | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/src/Surface.zig b/src/Surface.zig index 6e62f6639..0d4c9d984 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -4119,6 +4119,14 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool }, .unlocked); }, + .scroll_to_selection => { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const sel = self.io.terminal.screen.selection orelse return false; + const tl = sel.topLeft(&self.io.terminal.screen); + self.io.terminal.screen.scroll(.{ .pin = tl }); + }, + .scroll_page_up => { const rows: isize = @intCast(self.size.grid().rows); self.io.queueMessage(.{ diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 434ed9f0d..6bb50fc5d 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -279,6 +279,7 @@ pub const Action = union(enum) { /// Scroll the screen varying amounts. scroll_to_top, scroll_to_bottom, + scroll_to_selection, scroll_page_up, scroll_page_down, scroll_page_fractional: f32, @@ -789,6 +790,7 @@ pub const Action = union(enum) { .select_all, .scroll_to_top, .scroll_to_bottom, + .scroll_to_selection, .scroll_page_up, .scroll_page_down, .scroll_page_fractional, diff --git a/src/input/command.zig b/src/input/command.zig index 701d537a1..1f685269b 100644 --- a/src/input/command.zig +++ b/src/input/command.zig @@ -170,6 +170,12 @@ fn actionCommands(action: Action.Key) []const Command { .description = "Scroll to the bottom of the screen.", }}, + .scroll_to_selection => comptime &.{.{ + .action = .scroll_to_selection, + .title = "Scroll to Selection", + .description = "Scroll to the selected text.", + }}, + .scroll_page_up => comptime &.{.{ .action = .scroll_page_up, .title = "Scroll Page Up", From e8c845b758d08572ce8f4d7e5312a6abe2f7ffb9 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Wed, 7 May 2025 08:41:09 -0500 Subject: [PATCH 44/49] core: fixup callconv(.C) -> callconv(.c) https://ziglang.org/download/0.14.0/release-notes.html#Calling-Convention-Enhancements-and-setAlignStack-Replaced --- pkg/cimgui/main.zig | 22 ++++----- pkg/glfw/Joystick.zig | 2 +- pkg/glfw/Monitor.zig | 2 +- pkg/glfw/Window.zig | 32 ++++++------- pkg/glfw/errors.zig | 2 +- pkg/glfw/opengl.zig | 2 +- pkg/glfw/vulkan.zig | 4 +- pkg/harfbuzz/blob.zig | 4 +- pkg/macos/foundation/array.zig | 4 +- pkg/macos/video/display_link.zig | 2 +- pkg/opengl/glad.zig | 4 +- pkg/sentry/transport.zig | 4 +- src/apprt/embedded.zig | 12 ++--- src/apprt/gtk/App.zig | 2 +- src/apprt/gtk/ClipboardConfirmationWindow.zig | 6 +-- src/apprt/gtk/CloseDialog.zig | 4 +- src/apprt/gtk/ConfigErrorsDialog.zig | 2 +- src/apprt/gtk/ImguiWidget.zig | 28 +++++------ src/apprt/gtk/ResizeOverlay.zig | 4 +- src/apprt/gtk/Surface.zig | 48 +++++++++---------- src/apprt/gtk/Tab.zig | 2 +- src/apprt/gtk/TabView.zig | 8 ++-- src/apprt/gtk/URLWidget.zig | 4 +- src/apprt/gtk/Window.zig | 36 +++++++------- src/apprt/gtk/inspector.zig | 2 +- src/apprt/gtk/menu.zig | 2 +- src/apprt/gtk/winproto/wayland.zig | 2 +- src/crash/sentry.zig | 4 +- src/os/flatpak.zig | 2 +- src/renderer/Metal.zig | 2 +- src/renderer/shadertoy.zig | 2 +- 31 files changed, 128 insertions(+), 128 deletions(-) diff --git a/pkg/cimgui/main.zig b/pkg/cimgui/main.zig index e6e54c357..b890a49ee 100644 --- a/pkg/cimgui/main.zig +++ b/pkg/cimgui/main.zig @@ -1,20 +1,20 @@ pub const c = @import("c.zig").c; // OpenGL -pub extern fn ImGui_ImplOpenGL3_Init(?[*:0]const u8) callconv(.C) bool; -pub extern fn ImGui_ImplOpenGL3_Shutdown() callconv(.C) void; -pub extern fn ImGui_ImplOpenGL3_NewFrame() callconv(.C) void; -pub extern fn ImGui_ImplOpenGL3_RenderDrawData(*c.ImDrawData) callconv(.C) void; +pub extern fn ImGui_ImplOpenGL3_Init(?[*:0]const u8) callconv(.c) bool; +pub extern fn ImGui_ImplOpenGL3_Shutdown() callconv(.c) void; +pub extern fn ImGui_ImplOpenGL3_NewFrame() callconv(.c) void; +pub extern fn ImGui_ImplOpenGL3_RenderDrawData(*c.ImDrawData) callconv(.c) void; // Metal -pub extern fn ImGui_ImplMetal_Init(*anyopaque) callconv(.C) bool; -pub extern fn ImGui_ImplMetal_Shutdown() callconv(.C) void; -pub extern fn ImGui_ImplMetal_NewFrame(*anyopaque) callconv(.C) void; -pub extern fn ImGui_ImplMetal_RenderDrawData(*c.ImDrawData, *anyopaque, *anyopaque) callconv(.C) void; +pub extern fn ImGui_ImplMetal_Init(*anyopaque) callconv(.c) bool; +pub extern fn ImGui_ImplMetal_Shutdown() callconv(.c) void; +pub extern fn ImGui_ImplMetal_NewFrame(*anyopaque) callconv(.c) void; +pub extern fn ImGui_ImplMetal_RenderDrawData(*c.ImDrawData, *anyopaque, *anyopaque) callconv(.c) void; // OSX -pub extern fn ImGui_ImplOSX_Init(*anyopaque) callconv(.C) bool; -pub extern fn ImGui_ImplOSX_Shutdown() callconv(.C) void; -pub extern fn ImGui_ImplOSX_NewFrame(*anyopaque) callconv(.C) void; +pub extern fn ImGui_ImplOSX_Init(*anyopaque) callconv(.c) bool; +pub extern fn ImGui_ImplOSX_Shutdown() callconv(.c) void; +pub extern fn ImGui_ImplOSX_NewFrame(*anyopaque) callconv(.c) void; test {} diff --git a/pkg/glfw/Joystick.zig b/pkg/glfw/Joystick.zig index dd55c731d..a8152513e 100644 --- a/pkg/glfw/Joystick.zig +++ b/pkg/glfw/Joystick.zig @@ -333,7 +333,7 @@ pub inline fn setCallback(comptime callback: ?fn (joystick: Joystick, event: Eve if (callback) |user_callback| { const CWrapper = struct { - pub fn joystickCallbackWrapper(jid: c_int, event: c_int) callconv(.C) void { + pub fn joystickCallbackWrapper(jid: c_int, event: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ Joystick{ .jid = @as(Joystick.Id, @enumFromInt(jid)) }, @as(Event, @enumFromInt(event)), diff --git a/pkg/glfw/Monitor.zig b/pkg/glfw/Monitor.zig index 868872e19..4accb23cd 100644 --- a/pkg/glfw/Monitor.zig +++ b/pkg/glfw/Monitor.zig @@ -389,7 +389,7 @@ pub inline fn setCallback(comptime callback: ?fn (monitor: Monitor, event: Event if (callback) |user_callback| { const CWrapper = struct { - pub fn monitorCallbackWrapper(monitor: ?*c.GLFWmonitor, event: c_int) callconv(.C) void { + pub fn monitorCallbackWrapper(monitor: ?*c.GLFWmonitor, event: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ Monitor{ .handle = monitor.? }, @as(Event, @enumFromInt(event)), diff --git a/pkg/glfw/Window.zig b/pkg/glfw/Window.zig index 29dcac23e..804184f0e 100644 --- a/pkg/glfw/Window.zig +++ b/pkg/glfw/Window.zig @@ -1230,7 +1230,7 @@ pub inline fn setPosCallback(self: Window, comptime callback: ?fn (window: Windo if (callback) |user_callback| { const CWrapper = struct { - pub fn posCallbackWrapper(handle: ?*c.GLFWwindow, xpos: c_int, ypos: c_int) callconv(.C) void { + pub fn posCallbackWrapper(handle: ?*c.GLFWwindow, xpos: c_int, ypos: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), @as(i32, @intCast(xpos)), @@ -1263,7 +1263,7 @@ pub inline fn setSizeCallback(self: Window, comptime callback: ?fn (window: Wind if (callback) |user_callback| { const CWrapper = struct { - pub fn sizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { + pub fn sizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), @as(i32, @intCast(width)), @@ -1304,7 +1304,7 @@ pub inline fn setCloseCallback(self: Window, comptime callback: ?fn (window: Win if (callback) |user_callback| { const CWrapper = struct { - pub fn closeCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.C) void { + pub fn closeCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), }); @@ -1341,7 +1341,7 @@ pub inline fn setRefreshCallback(self: Window, comptime callback: ?fn (window: W if (callback) |user_callback| { const CWrapper = struct { - pub fn refreshCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.C) void { + pub fn refreshCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), }); @@ -1379,7 +1379,7 @@ pub inline fn setFocusCallback(self: Window, comptime callback: ?fn (window: Win if (callback) |user_callback| { const CWrapper = struct { - pub fn focusCallbackWrapper(handle: ?*c.GLFWwindow, focused: c_int) callconv(.C) void { + pub fn focusCallbackWrapper(handle: ?*c.GLFWwindow, focused: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), focused == c.GLFW_TRUE, @@ -1413,7 +1413,7 @@ pub inline fn setIconifyCallback(self: Window, comptime callback: ?fn (window: W if (callback) |user_callback| { const CWrapper = struct { - pub fn iconifyCallbackWrapper(handle: ?*c.GLFWwindow, iconified: c_int) callconv(.C) void { + pub fn iconifyCallbackWrapper(handle: ?*c.GLFWwindow, iconified: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), iconified == c.GLFW_TRUE, @@ -1448,7 +1448,7 @@ pub inline fn setMaximizeCallback(self: Window, comptime callback: ?fn (window: if (callback) |user_callback| { const CWrapper = struct { - pub fn maximizeCallbackWrapper(handle: ?*c.GLFWwindow, maximized: c_int) callconv(.C) void { + pub fn maximizeCallbackWrapper(handle: ?*c.GLFWwindow, maximized: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), maximized == c.GLFW_TRUE, @@ -1483,7 +1483,7 @@ pub inline fn setFramebufferSizeCallback(self: Window, comptime callback: ?fn (w if (callback) |user_callback| { const CWrapper = struct { - pub fn framebufferSizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { + pub fn framebufferSizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), @as(u32, @intCast(width)), @@ -1519,7 +1519,7 @@ pub inline fn setContentScaleCallback(self: Window, comptime callback: ?fn (wind if (callback) |user_callback| { const CWrapper = struct { - pub fn windowScaleCallbackWrapper(handle: ?*c.GLFWwindow, xscale: f32, yscale: f32) callconv(.C) void { + pub fn windowScaleCallbackWrapper(handle: ?*c.GLFWwindow, xscale: f32, yscale: f32) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), xscale, @@ -1871,7 +1871,7 @@ pub inline fn setKeyCallback(self: Window, comptime callback: ?fn (window: Windo if (callback) |user_callback| { const CWrapper = struct { - pub fn keyCallbackWrapper(handle: ?*c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, mods: c_int) callconv(.C) void { + pub fn keyCallbackWrapper(handle: ?*c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, mods: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), @as(Key, @enumFromInt(key)), @@ -1917,7 +1917,7 @@ pub inline fn setCharCallback(self: Window, comptime callback: ?fn (window: Wind if (callback) |user_callback| { const CWrapper = struct { - pub fn charCallbackWrapper(handle: ?*c.GLFWwindow, codepoint: c_uint) callconv(.C) void { + pub fn charCallbackWrapper(handle: ?*c.GLFWwindow, codepoint: c_uint) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), @as(u21, @intCast(codepoint)), @@ -1958,7 +1958,7 @@ pub inline fn setMouseButtonCallback(self: Window, comptime callback: ?fn (windo if (callback) |user_callback| { const CWrapper = struct { - pub fn mouseButtonCallbackWrapper(handle: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.C) void { + pub fn mouseButtonCallbackWrapper(handle: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), @as(MouseButton, @enumFromInt(button)), @@ -1996,7 +1996,7 @@ pub inline fn setCursorPosCallback(self: Window, comptime callback: ?fn (window: if (callback) |user_callback| { const CWrapper = struct { - pub fn cursorPosCallbackWrapper(handle: ?*c.GLFWwindow, xpos: f64, ypos: f64) callconv(.C) void { + pub fn cursorPosCallbackWrapper(handle: ?*c.GLFWwindow, xpos: f64, ypos: f64) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), xpos, @@ -2030,7 +2030,7 @@ pub inline fn setCursorEnterCallback(self: Window, comptime callback: ?fn (windo if (callback) |user_callback| { const CWrapper = struct { - pub fn cursorEnterCallbackWrapper(handle: ?*c.GLFWwindow, entered: c_int) callconv(.C) void { + pub fn cursorEnterCallbackWrapper(handle: ?*c.GLFWwindow, entered: c_int) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), entered == c.GLFW_TRUE, @@ -2067,7 +2067,7 @@ pub inline fn setScrollCallback(self: Window, comptime callback: ?fn (window: Wi if (callback) |user_callback| { const CWrapper = struct { - pub fn scrollCallbackWrapper(handle: ?*c.GLFWwindow, xoffset: f64, yoffset: f64) callconv(.C) void { + pub fn scrollCallbackWrapper(handle: ?*c.GLFWwindow, xoffset: f64, yoffset: f64) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), xoffset, @@ -2110,7 +2110,7 @@ pub inline fn setDropCallback(self: Window, comptime callback: ?fn (window: Wind if (callback) |user_callback| { const CWrapper = struct { - pub fn dropCallbackWrapper(handle: ?*c.GLFWwindow, path_count: c_int, paths: [*c][*c]const u8) callconv(.C) void { + pub fn dropCallbackWrapper(handle: ?*c.GLFWwindow, path_count: c_int, paths: [*c][*c]const u8) callconv(.c) void { @call(.always_inline, user_callback, .{ from(handle.?), @as([*][*:0]const u8, @ptrCast(paths))[0..@as(u32, @intCast(path_count))], diff --git a/pkg/glfw/errors.zig b/pkg/glfw/errors.zig index ce98ec5cd..b9721fd05 100644 --- a/pkg/glfw/errors.zig +++ b/pkg/glfw/errors.zig @@ -300,7 +300,7 @@ pub inline fn mustGetErrorString() [:0]const u8 { pub fn setErrorCallback(comptime callback: ?fn (error_code: ErrorCode, description: [:0]const u8) void) void { if (callback) |user_callback| { const CWrapper = struct { - pub fn errorCallbackWrapper(err_int: c_int, c_description: [*c]const u8) callconv(.C) void { + pub fn errorCallbackWrapper(err_int: c_int, c_description: [*c]const u8) callconv(.c) void { convertError(err_int) catch |error_code| { user_callback(error_code, mem.sliceTo(c_description, 0)); }; diff --git a/pkg/glfw/opengl.zig b/pkg/glfw/opengl.zig index de99582c2..04bc3a65c 100644 --- a/pkg/glfw/opengl.zig +++ b/pkg/glfw/opengl.zig @@ -161,7 +161,7 @@ pub const GLProc = *const fn () callconv(if (builtin.os.tag == .windows and buil /// @thread_safety This function may be called from any thread. /// /// see also: context_glext, glfwExtensionSupported -pub fn getProcAddress(proc_name: [*:0]const u8) callconv(.C) ?GLProc { +pub fn getProcAddress(proc_name: [*:0]const u8) callconv(.c) ?GLProc { internal_debug.assertInitialized(); if (c.glfwGetProcAddress(proc_name)) |proc_address| return @ptrCast(proc_address); return null; diff --git a/pkg/glfw/vulkan.zig b/pkg/glfw/vulkan.zig index 6c6021d02..1b84145d5 100644 --- a/pkg/glfw/vulkan.zig +++ b/pkg/glfw/vulkan.zig @@ -33,7 +33,7 @@ pub fn initVulkanLoader(loader_function: ?VKGetInstanceProcAddr) void { c.glfwInitVulkanLoader(loader_function orelse null); } -pub const VKGetInstanceProcAddr = *const fn (vk_instance: c.VkInstance, name: [*c]const u8) callconv(.C) ?VKProc; +pub const VKGetInstanceProcAddr = *const fn (vk_instance: c.VkInstance, name: [*c]const u8) callconv(.c) ?VKProc; /// Returns whether the Vulkan loader and an ICD have been found. /// @@ -127,7 +127,7 @@ pub const VKProc = *const fn () callconv(if (builtin.os.tag == .windows and buil /// @pointer_lifetime The returned function pointer is valid until the library is terminated. /// /// @thread_safety This function may be called from any thread. -pub fn getInstanceProcAddress(vk_instance: ?*anyopaque, proc_name: [*:0]const u8) callconv(.C) ?VKProc { +pub fn getInstanceProcAddress(vk_instance: ?*anyopaque, proc_name: [*:0]const u8) callconv(.c) ?VKProc { internal_debug.assertInitialized(); if (c.glfwGetInstanceProcAddress(if (vk_instance) |v| @as(c.VkInstance, @ptrCast(v)) else null, proc_name)) |proc_address| return proc_address; return null; diff --git a/pkg/harfbuzz/blob.zig b/pkg/harfbuzz/blob.zig index d25df6974..9472e4c75 100644 --- a/pkg/harfbuzz/blob.zig +++ b/pkg/harfbuzz/blob.zig @@ -77,11 +77,11 @@ pub const Blob = struct { comptime T: type, key: ?*anyopaque, ptr: ?*T, - comptime destroycb: ?*const fn (?*T) callconv(.C) void, + comptime destroycb: ?*const fn (?*T) callconv(.c) void, replace: bool, ) bool { const Callback = struct { - pub fn callback(data: ?*anyopaque) callconv(.C) void { + pub fn callback(data: ?*anyopaque) callconv(.c) void { @call(.{ .modifier = .always_inline }, destroycb, .{ @as(?*T, @ptrCast(@alignCast(data))), }); diff --git a/pkg/macos/foundation/array.zig b/pkg/macos/foundation/array.zig index 37fa2b985..d3a977539 100644 --- a/pkg/macos/foundation/array.zig +++ b/pkg/macos/foundation/array.zig @@ -84,7 +84,7 @@ pub const MutableArray = opaque { a: *const Elem, b: *const Elem, context: ?*Context, - ) callconv(.C) ComparisonResult, + ) callconv(.c) ComparisonResult, ) void { CFArraySortValues( self, @@ -155,7 +155,7 @@ test "array sorting" { void, null, struct { - fn compare(a: *const u8, b: *const u8, _: ?*void) callconv(.C) ComparisonResult { + fn compare(a: *const u8, b: *const u8, _: ?*void) callconv(.c) ComparisonResult { if (a.* > b.*) return .greater; if (a.* == b.*) return .equal; return .less; diff --git a/pkg/macos/video/display_link.zig b/pkg/macos/video/display_link.zig index ca0c80d0b..4bbf58a0c 100644 --- a/pkg/macos/video/display_link.zig +++ b/pkg/macos/video/display_link.zig @@ -66,7 +66,7 @@ pub const DisplayLink = opaque { flagsIn: c.CVOptionFlags, flagsOut: *c.CVOptionFlags, inner_userinfo: ?*anyopaque, - ) callconv(.C) c.CVReturn { + ) callconv(.c) c.CVReturn { _ = inNow; _ = inOutputTime; _ = flagsIn; diff --git a/pkg/opengl/glad.zig b/pkg/opengl/glad.zig index 79a2e4d6b..663e75e12 100644 --- a/pkg/opengl/glad.zig +++ b/pkg/opengl/glad.zig @@ -13,8 +13,8 @@ pub threadlocal var context: Context = undefined; /// The getProcAddress param is an anytype so that we can accept multiple /// forms of the function depending on what we're interfacing with. pub fn load(getProcAddress: anytype) !c_int { - const GlProc = *const fn () callconv(.C) void; - const GlfwFn = *const fn ([*:0]const u8) callconv(.C) ?GlProc; + const GlProc = *const fn () callconv(.c) void; + const GlfwFn = *const fn ([*:0]const u8) callconv(.c) ?GlProc; const res = switch (@TypeOf(getProcAddress)) { // glfw diff --git a/pkg/sentry/transport.zig b/pkg/sentry/transport.zig index 835b87cd3..747187211 100644 --- a/pkg/sentry/transport.zig +++ b/pkg/sentry/transport.zig @@ -5,8 +5,8 @@ const Envelope = @import("envelope.zig").Envelope; /// sentry_transport_t pub const Transport = opaque { - pub const SendFunc = *const fn (envelope: *Envelope, state: ?*anyopaque) callconv(.C) void; - pub const FreeFunc = *const fn (state: ?*anyopaque) callconv(.C) void; + pub const SendFunc = *const fn (envelope: *Envelope, state: ?*anyopaque) callconv(.c) void; + pub const FreeFunc = *const fn (state: ?*anyopaque) callconv(.c) void; pub fn init(f: SendFunc) *Transport { return @ptrCast(c.sentry_transport_new(@ptrCast(f)).?); diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 22ae6e488..c953300cd 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -43,15 +43,15 @@ pub const App = struct { /// Callback called to wakeup the event loop. This should trigger /// a full tick of the app loop. - wakeup: *const fn (AppUD) callconv(.C) void, + wakeup: *const fn (AppUD) callconv(.c) void, /// Callback called to handle an action. - action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.C) bool, + action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.c) bool, /// Read the clipboard value. The return value must be preserved /// by the host until the next call. If there is no valid clipboard /// value then this should return null. - read_clipboard: *const fn (SurfaceUD, c_int, *apprt.ClipboardRequest) callconv(.C) void, + read_clipboard: *const fn (SurfaceUD, c_int, *apprt.ClipboardRequest) callconv(.c) void, /// This may be called after a read clipboard call to request /// confirmation that the clipboard value is safe to read. The embedder @@ -61,13 +61,13 @@ pub const App = struct { [*:0]const u8, *apprt.ClipboardRequest, apprt.ClipboardRequestType, - ) callconv(.C) void, + ) callconv(.c) void, /// Write the clipboard value. - write_clipboard: *const fn (SurfaceUD, [*:0]const u8, c_int, bool) callconv(.C) void, + write_clipboard: *const fn (SurfaceUD, [*:0]const u8, c_int, bool) callconv(.c) void, /// Close the current surface given by this function. - close_surface: ?*const fn (SurfaceUD, bool) callconv(.C) void = null, + close_surface: ?*const fn (SurfaceUD, bool) callconv(.c) void = null, }; /// This is the key event sent for ghostty_surface_key and diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 9ca1629ba..c9a973611 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -1527,7 +1527,7 @@ fn adwNotifyDark( style_manager: *adw.StyleManager, _: *gobject.ParamSpec, self: *App, -) callconv(.C) void { +) callconv(.c) void { const color_scheme: apprt.ColorScheme = if (style_manager.getDark() == 0) .light else diff --git a/src/apprt/gtk/ClipboardConfirmationWindow.zig b/src/apprt/gtk/ClipboardConfirmationWindow.zig index 583a58a2c..f10fc79ac 100644 --- a/src/apprt/gtk/ClipboardConfirmationWindow.zig +++ b/src/apprt/gtk/ClipboardConfirmationWindow.zig @@ -152,7 +152,7 @@ fn init( } } -fn gtkResponse(_: *DialogType, response: [*:0]u8, self: *ClipboardConfirmation) callconv(.C) void { +fn gtkResponse(_: *DialogType, response: [*:0]u8, self: *ClipboardConfirmation) callconv(.c) void { if (std.mem.orderZ(u8, response, "ok") == .eq) { self.core_surface.completeClipboardRequest( self.pending_req, @@ -165,7 +165,7 @@ fn gtkResponse(_: *DialogType, response: [*:0]u8, self: *ClipboardConfirmation) self.destroy(); } -fn gtkRevealButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.C) void { +fn gtkRevealButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.c) void { self.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(true)); self.text_view.as(gtk.Widget).removeCssClass("blurred"); @@ -173,7 +173,7 @@ fn gtkRevealButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv self.reveal_button.as(gtk.Widget).setVisible(@intFromBool(false)); } -fn gtkHideButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.C) void { +fn gtkHideButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.c) void { self.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(false)); self.text_view.as(gtk.Widget).addCssClass("blurred"); diff --git a/src/apprt/gtk/CloseDialog.zig b/src/apprt/gtk/CloseDialog.zig index ea683c477..559737cf4 100644 --- a/src/apprt/gtk/CloseDialog.zig +++ b/src/apprt/gtk/CloseDialog.zig @@ -64,7 +64,7 @@ fn responseCallback( _: *DialogType, response: [*:0]const u8, target: *Target, -) callconv(.C) void { +) callconv(.c) void { const alloc = target.allocator(); defer alloc.destroy(target); @@ -141,7 +141,7 @@ pub const Target = union(enum) { } }; -fn findActiveWindow(data: ?*const anyopaque, _: ?*const anyopaque) callconv(.C) c_int { +fn findActiveWindow(data: ?*const anyopaque, _: ?*const anyopaque) callconv(.c) c_int { const window: *gtk.Window = @ptrCast(@alignCast(@constCast(data orelse return -1))); // Confusingly, `isActive` returns 1 when active, diff --git a/src/apprt/gtk/ConfigErrorsDialog.zig b/src/apprt/gtk/ConfigErrorsDialog.zig index 7a245a1a0..a1a2a61af 100644 --- a/src/apprt/gtk/ConfigErrorsDialog.zig +++ b/src/apprt/gtk/ConfigErrorsDialog.zig @@ -67,7 +67,7 @@ pub fn maybePresent(app: *App, window: ?*Window) void { } } -fn onResponse(_: *DialogType, response: [*:0]const u8, app: *App) callconv(.C) void { +fn onResponse(_: *DialogType, response: [*:0]const u8, app: *App) callconv(.c) void { if (std.mem.orderZ(u8, response, "reload") == .eq) { app.reloadConfig(.app, .{}) catch |err| { log.warn("error reloading config error={}", .{err}); diff --git a/src/apprt/gtk/ImguiWidget.zig b/src/apprt/gtk/ImguiWidget.zig index f1f0c8f6b..338fd7982 100644 --- a/src/apprt/gtk/ImguiWidget.zig +++ b/src/apprt/gtk/ImguiWidget.zig @@ -221,12 +221,12 @@ fn translateMouseButton(button: c_uint) ?c_int { }; } -fn gtkDestroy(_: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void { +fn gtkDestroy(_: *gtk.GLArea, self: *ImguiWidget) callconv(.c) void { log.debug("imgui widget destroy", .{}); self.deinit(); } -fn gtkRealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void { +fn gtkRealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.c) void { log.debug("gl surface realized", .{}); // We need to make the context current so we can call GL functions. @@ -242,7 +242,7 @@ fn gtkRealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void { _ = cimgui.ImGui_ImplOpenGL3_Init(null); } -fn gtkUnrealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void { +fn gtkUnrealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.c) void { _ = area; log.debug("gl surface unrealized", .{}); @@ -250,7 +250,7 @@ fn gtkUnrealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void { cimgui.ImGui_ImplOpenGL3_Shutdown(); } -fn gtkResize(area: *gtk.GLArea, width: c_int, height: c_int, self: *ImguiWidget) callconv(.C) void { +fn gtkResize(area: *gtk.GLArea, width: c_int, height: c_int, self: *ImguiWidget) callconv(.c) void { cimgui.c.igSetCurrentContext(self.ig_ctx); const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO(); const scale_factor = area.as(gtk.Widget).getScaleFactor(); @@ -273,7 +273,7 @@ fn gtkResize(area: *gtk.GLArea, width: c_int, height: c_int, self: *ImguiWidget) active_style.* = style.*; } -fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *ImguiWidget) callconv(.C) c_int { +fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *ImguiWidget) callconv(.c) c_int { cimgui.c.igSetCurrentContext(self.ig_ctx); // Setup our frame. We render twice because some ImGui behaviors @@ -307,7 +307,7 @@ fn gtkMouseMotion( x: f64, y: f64, self: *ImguiWidget, -) callconv(.C) void { +) callconv(.c) void { cimgui.c.igSetCurrentContext(self.ig_ctx); const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO(); const scale_factor: f64 = @floatFromInt(self.gl_area.as(gtk.Widget).getScaleFactor()); @@ -325,7 +325,7 @@ fn gtkMouseDown( _: f64, _: f64, self: *ImguiWidget, -) callconv(.C) void { +) callconv(.c) void { self.queueRender(); cimgui.c.igSetCurrentContext(self.ig_ctx); @@ -343,7 +343,7 @@ fn gtkMouseUp( _: f64, _: f64, self: *ImguiWidget, -) callconv(.C) void { +) callconv(.c) void { self.queueRender(); cimgui.c.igSetCurrentContext(self.ig_ctx); @@ -359,7 +359,7 @@ fn gtkMouseScroll( x: f64, y: f64, self: *ImguiWidget, -) callconv(.C) c_int { +) callconv(.c) c_int { self.queueRender(); cimgui.c.igSetCurrentContext(self.ig_ctx); @@ -373,7 +373,7 @@ fn gtkMouseScroll( return @intFromBool(true); } -fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C) void { +fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.c) void { self.queueRender(); cimgui.c.igSetCurrentContext(self.ig_ctx); @@ -381,7 +381,7 @@ fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C) cimgui.c.ImGuiIO_AddFocusEvent(io, true); } -fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C) void { +fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.c) void { self.queueRender(); cimgui.c.igSetCurrentContext(self.ig_ctx); @@ -393,7 +393,7 @@ fn gtkInputCommit( _: *gtk.IMMulticontext, bytes: [*:0]u8, self: *ImguiWidget, -) callconv(.C) void { +) callconv(.c) void { self.queueRender(); cimgui.c.igSetCurrentContext(self.ig_ctx); @@ -407,7 +407,7 @@ fn gtkKeyPressed( keycode: c_uint, gtk_mods: gdk.ModifierType, self: *ImguiWidget, -) callconv(.C) c_int { +) callconv(.c) c_int { return @intFromBool(self.keyEvent( .press, ec_key, @@ -423,7 +423,7 @@ fn gtkKeyReleased( keycode: c_uint, gtk_mods: gdk.ModifierType, self: *ImguiWidget, -) callconv(.C) void { +) callconv(.c) void { _ = self.keyEvent( .release, ec_key, diff --git a/src/apprt/gtk/ResizeOverlay.zig b/src/apprt/gtk/ResizeOverlay.zig index 47f2aea1a..767cf097d 100644 --- a/src/apprt/gtk/ResizeOverlay.zig +++ b/src/apprt/gtk/ResizeOverlay.zig @@ -104,7 +104,7 @@ pub fn maybeShow(self: *ResizeOverlay) void { /// Actually update the overlay widget. This should only be called from a GTK /// idle handler. -fn gtkUpdate(ud: ?*anyopaque) callconv(.C) c_int { +fn gtkUpdate(ud: ?*anyopaque) callconv(.c) c_int { const self: *ResizeOverlay = @ptrCast(@alignCast(ud orelse return 0)); // No matter what our idler is complete with this callback @@ -198,7 +198,7 @@ fn setPosition(label: *gtk.Label, config: *DerivedConfig) void { /// If this fires, it means that the delay period has expired and the resize /// overlay widget should be hidden. -fn gtkTimerExpired(ud: ?*anyopaque) callconv(.C) c_int { +fn gtkTimerExpired(ud: ?*anyopaque) callconv(.c) c_int { const self: *ResizeOverlay = @ptrCast(@alignCast(ud orelse return 0)); self.timer = null; if (self.label) |label| hide(label); diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 4ad2eeb13..7ff96480e 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1025,7 +1025,7 @@ pub fn setTitle(self: *Surface, slice: [:0]const u8, source: SetTitleSource) !vo self.update_title_timer = glib.timeoutAdd(75, updateTitleTimerExpired, self); } -fn updateTitleTimerExpired(ud: ?*anyopaque) callconv(.C) c_int { +fn updateTitleTimerExpired(ud: ?*anyopaque) callconv(.c) c_int { const self: *Surface = @ptrCast(@alignCast(ud.?)); self.updateTitleLabels(); @@ -1265,7 +1265,7 @@ fn gtkClipboardRead( source: ?*gobject.Object, res: *gio.AsyncResult, ud: ?*anyopaque, -) callconv(.C) void { +) callconv(.c) void { const clipboard = gobject.ext.cast(gdk.Clipboard, source orelse return) orelse return; const req: *ClipboardRequest = @ptrCast(@alignCast(ud orelse return)); const self = req.self; @@ -1349,7 +1349,7 @@ pub fn showDesktopNotification( app.sendNotification(body.ptr, notification); } -fn gtkRealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.C) void { +fn gtkRealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.c) void { log.debug("gl surface realized", .{}); // We need to make the context current so we can call GL functions. @@ -1377,7 +1377,7 @@ fn gtkRealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.C) void { /// This is called when the underlying OpenGL resources must be released. /// This is usually due to the OpenGL area changing GDK surfaces. -fn gtkUnrealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.C) void { +fn gtkUnrealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.c) void { log.debug("gl surface unrealized", .{}); // See gtkRealize for why we do this here. @@ -1405,7 +1405,7 @@ fn gtkUnrealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.C) void { } /// render signal -fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *Surface) callconv(.C) c_int { +fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *Surface) callconv(.c) c_int { self.render() catch |err| { log.err("surface failed to render: {}", .{err}); return 0; @@ -1415,7 +1415,7 @@ fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *Surface) callconv(.C) c_i } /// resize signal -fn gtkResize(gl_area: *gtk.GLArea, width: c_int, height: c_int, self: *Surface) callconv(.C) void { +fn gtkResize(gl_area: *gtk.GLArea, width: c_int, height: c_int, self: *Surface) callconv(.c) void { // Some debug output to help understand what GTK is telling us. { const scale_factor = scale: { @@ -1471,7 +1471,7 @@ fn gtkResize(gl_area: *gtk.GLArea, width: c_int, height: c_int, self: *Surface) } /// "destroy" signal for surface -fn gtkDestroy(_: *gtk.GLArea, self: *Surface) callconv(.C) void { +fn gtkDestroy(_: *gtk.GLArea, self: *Surface) callconv(.c) void { log.debug("gl destroy", .{}); const alloc = self.app.core_app.alloc; @@ -1505,7 +1505,7 @@ fn gtkMouseDown( x: f64, y: f64, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { const event = gesture.as(gtk.EventController).getCurrentEvent() orelse return; const gtk_mods = event.getModifierState(); @@ -1538,7 +1538,7 @@ fn gtkMouseUp( _: f64, _: f64, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { const event = gesture.as(gtk.EventController).getCurrentEvent() orelse return; const gtk_mods = event.getModifierState(); @@ -1557,7 +1557,7 @@ fn gtkMouseMotion( x: f64, y: f64, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { const event = ec.as(gtk.EventController).getCurrentEvent() orelse return; const scaled = self.scaledCoordinates(x, y); @@ -1603,7 +1603,7 @@ fn gtkMouseMotion( fn gtkMouseLeave( ec_motion: *gtk.EventControllerMotion, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { const event = ec_motion.as(gtk.EventController).getCurrentEvent() orelse return; // Get our modifiers @@ -1618,14 +1618,14 @@ fn gtkMouseLeave( fn gtkMouseScrollPrecisionBegin( _: *gtk.EventControllerScroll, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { self.precision_scroll = true; } fn gtkMouseScrollPrecisionEnd( _: *gtk.EventControllerScroll, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { self.precision_scroll = false; } @@ -1634,7 +1634,7 @@ fn gtkMouseScroll( x: f64, y: f64, self: *Surface, -) callconv(.C) c_int { +) callconv(.c) c_int { const scaled = self.scaledCoordinates(x, y); // GTK doesn't support any of the scroll mods. @@ -1664,7 +1664,7 @@ fn gtkKeyPressed( keycode: c_uint, gtk_mods: gdk.ModifierType, self: *Surface, -) callconv(.C) c_int { +) callconv(.c) c_int { return @intFromBool(self.keyEvent( .press, ec_key, @@ -1680,7 +1680,7 @@ fn gtkKeyReleased( keycode: c_uint, state: gdk.ModifierType, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { _ = self.keyEvent( .release, ec_key, @@ -1971,7 +1971,7 @@ pub fn keyEvent( fn gtkInputPreeditStart( _: *gtk.IMMulticontext, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { // log.warn("GTKIM: preedit start", .{}); // Start our composing state for the input method and reset our @@ -1983,7 +1983,7 @@ fn gtkInputPreeditStart( fn gtkInputPreeditChanged( ctx: *gtk.IMMulticontext, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { // Any preedit change should mark that we're composing. Its possible this // is false using fcitx5-hangul and typing "dkssud" ("안녕"). The // second "s" results in a "commit" for "안" which sets composing to false, @@ -2009,7 +2009,7 @@ fn gtkInputPreeditChanged( fn gtkInputPreeditEnd( _: *gtk.IMMulticontext, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { // log.warn("GTKIM: preedit end", .{}); // End our composing state for GTK, allowing us to commit the text. @@ -2025,7 +2025,7 @@ fn gtkInputCommit( _: *gtk.IMMulticontext, bytes: [*:0]u8, self: *Surface, -) callconv(.C) void { +) callconv(.c) void { const str = std.mem.sliceTo(bytes, 0); // log.debug("GTKIM: input commit composing={} keyevent={} str={s}", .{ @@ -2100,7 +2100,7 @@ fn gtkInputCommit( }; } -fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *Surface) callconv(.C) void { +fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *Surface) callconv(.c) void { if (!self.realized) return; // Notify our IM context @@ -2125,7 +2125,7 @@ fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *Surface) callconv(.C) void }; } -fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *Surface) callconv(.C) void { +fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *Surface) callconv(.c) void { if (!self.realized) return; // Notify our IM context @@ -2243,7 +2243,7 @@ fn gtkDrop( _: f64, _: f64, self: *Surface, -) callconv(.C) c_int { +) callconv(.c) c_int { const alloc = self.app.core_app.alloc; if (g_value_holds(value, gdk.FileList.getGObjectType())) { @@ -2395,7 +2395,7 @@ fn g_value_holds(value_: ?*gobject.Value, g_type: gobject.Type) bool { return false; } -fn gtkPromptTitleResponse(source_object: ?*gobject.Object, result: *gio.AsyncResult, ud: ?*anyopaque) callconv(.C) void { +fn gtkPromptTitleResponse(source_object: ?*gobject.Object, result: *gio.AsyncResult, ud: ?*anyopaque) callconv(.c) void { if (!adw_version.supportsDialogs()) return; const dialog = gobject.ext.cast(adw.AlertDialog, source_object.?).?; const self: *Surface = @ptrCast(@alignCast(ud)); diff --git a/src/apprt/gtk/Tab.zig b/src/apprt/gtk/Tab.zig index 57a9644d9..c32fa19fc 100644 --- a/src/apprt/gtk/Tab.zig +++ b/src/apprt/gtk/Tab.zig @@ -161,7 +161,7 @@ pub fn closeWithConfirmation(tab: *Tab) void { } } -fn gtkDestroy(_: *gtk.Box, self: *Tab) callconv(.C) void { +fn gtkDestroy(_: *gtk.Box, self: *Tab) callconv(.c) void { log.debug("tab box destroy", .{}); const alloc = self.window.app.core_app.alloc; diff --git a/src/apprt/gtk/TabView.zig b/src/apprt/gtk/TabView.zig index ddd0951d2..29a069a6d 100644 --- a/src/apprt/gtk/TabView.zig +++ b/src/apprt/gtk/TabView.zig @@ -227,7 +227,7 @@ pub fn createWindow(window: *Window) !*Window { return new_window; } -fn adwPageAttached(_: *adw.TabView, page: *adw.TabPage, _: c_int, self: *TabView) callconv(.C) void { +fn adwPageAttached(_: *adw.TabView, page: *adw.TabPage, _: c_int, self: *TabView) callconv(.c) void { const child = page.getChild().as(gobject.Object); const tab: *Tab = @ptrCast(@alignCast(child.getData(Tab.GHOSTTY_TAB) orelse return)); tab.window = self.window; @@ -239,7 +239,7 @@ fn adwClosePage( _: *adw.TabView, page: *adw.TabPage, self: *TabView, -) callconv(.C) c_int { +) callconv(.c) c_int { const child = page.getChild().as(gobject.Object); const tab: *Tab = @ptrCast(@alignCast(child.getData(Tab.GHOSTTY_TAB) orelse return 0)); self.tab_view.closePageFinish(page, @intFromBool(self.forcing_close)); @@ -251,7 +251,7 @@ fn adwClosePage( fn adwTabViewCreateWindow( _: *adw.TabView, self: *TabView, -) callconv(.C) ?*adw.TabView { +) callconv(.c) ?*adw.TabView { const window = createWindow(self.window) catch |err| { log.warn("error creating new window error={}", .{err}); return null; @@ -259,7 +259,7 @@ fn adwTabViewCreateWindow( return window.notebook.tab_view; } -fn adwSelectPage(_: *adw.TabView, _: *gobject.ParamSpec, self: *TabView) callconv(.C) void { +fn adwSelectPage(_: *adw.TabView, _: *gobject.ParamSpec, self: *TabView) callconv(.c) void { const page = self.tab_view.getSelectedPage() orelse return; // If the tab was previously marked as needing attention diff --git a/src/apprt/gtk/URLWidget.zig b/src/apprt/gtk/URLWidget.zig index d1628aa6e..e59827aaf 100644 --- a/src/apprt/gtk/URLWidget.zig +++ b/src/apprt/gtk/URLWidget.zig @@ -101,7 +101,7 @@ fn gtkLeftEnter( _: f64, _: f64, right: *gtk.Label, -) callconv(.C) void { +) callconv(.c) void { right.as(gtk.Widget).removeCssClass("hidden"); } @@ -110,6 +110,6 @@ fn gtkLeftEnter( fn gtkLeftLeave( _: *gtk.EventControllerMotion, right: *gtk.Label, -) callconv(.C) void { +) callconv(.c) void { right.as(gtk.Widget).addCssClass("hidden"); } diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index e130cd1be..d82087ff0 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -792,7 +792,7 @@ fn gtkWindowNotifyIsActive( _: *adw.ApplicationWindow, _: *gobject.ParamSpec, self: *Window, -) callconv(.C) void { +) callconv(.c) void { if (!self.isQuickTerminal()) return; // Hide when we're unfocused @@ -883,7 +883,7 @@ fn adwTabOverviewOpen( fn adwTabOverviewFocusTimer( ud: ?*anyopaque, -) callconv(.C) c_int { +) callconv(.c) c_int { if (!adw_version.supportsTabOverview()) unreachable; const self: *Window = @ptrCast(@alignCast(ud orelse return 0)); self.adw_tab_overview_focus_timer = null; @@ -970,7 +970,7 @@ fn gtkActionAbout( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { const name = "Ghostty"; const icon = "com.mitchellh.ghostty"; const website = "https://ghostty.org"; @@ -1014,7 +1014,7 @@ fn gtkActionClose( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.closeWithConfirmation(); } @@ -1022,7 +1022,7 @@ fn gtkActionNewWindow( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .new_window = {} }); } @@ -1030,7 +1030,7 @@ fn gtkActionNewTab( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .new_tab = {} }); } @@ -1038,7 +1038,7 @@ fn gtkActionCloseTab( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .close_tab = {} }); } @@ -1046,7 +1046,7 @@ fn gtkActionSplitRight( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .new_split = .right }); } @@ -1054,7 +1054,7 @@ fn gtkActionSplitDown( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .new_split = .down }); } @@ -1062,7 +1062,7 @@ fn gtkActionSplitLeft( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .new_split = .left }); } @@ -1070,7 +1070,7 @@ fn gtkActionSplitUp( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .new_split = .up }); } @@ -1078,7 +1078,7 @@ fn gtkActionToggleInspector( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .inspector = .toggle }); } @@ -1086,7 +1086,7 @@ fn gtkActionCopy( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .copy_to_clipboard = {} }); } @@ -1094,7 +1094,7 @@ fn gtkActionPaste( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .paste_from_clipboard = {} }); } @@ -1102,7 +1102,7 @@ fn gtkActionReset( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .reset = {} }); } @@ -1110,7 +1110,7 @@ fn gtkActionClear( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .clear_screen = {} }); } @@ -1118,7 +1118,7 @@ fn gtkActionPromptTitle( _: *gio.SimpleAction, _: ?*glib.Variant, self: *Window, -) callconv(.C) void { +) callconv(.c) void { self.performBindingAction(.{ .prompt_surface_title = {} }); } @@ -1133,7 +1133,7 @@ fn gtkTitlebarMenuActivate( btn: *gtk.MenuButton, _: *gobject.ParamSpec, self: *Window, -) callconv(.C) void { +) callconv(.c) void { // debian 12 is stuck on GTK 4.8 if (!gtk_version.atLeast(4, 10, 0)) return; const active = btn.getActive() != 0; diff --git a/src/apprt/gtk/inspector.zig b/src/apprt/gtk/inspector.zig index aa4f6e435..e3e61e258 100644 --- a/src/apprt/gtk/inspector.zig +++ b/src/apprt/gtk/inspector.zig @@ -177,7 +177,7 @@ const Window = struct { } /// "destroy" signal for the window - fn gtkDestroy(_: *gtk.ApplicationWindow, self: *Window) callconv(.C) void { + fn gtkDestroy(_: *gtk.ApplicationWindow, self: *Window) callconv(.c) void { log.debug("window destroy", .{}); self.deinit(); } diff --git a/src/apprt/gtk/menu.zig b/src/apprt/gtk/menu.zig index f63a0eb5f..d9d0083d0 100644 --- a/src/apprt/gtk/menu.zig +++ b/src/apprt/gtk/menu.zig @@ -130,7 +130,7 @@ pub fn Menu( } /// Refocus tab that lost focus because of the popover menu - fn gtkRefocusTerm(_: *gtk.PopoverMenu, self: *Self) callconv(.C) void { + fn gtkRefocusTerm(_: *gtk.PopoverMenu, self: *Self) callconv(.c) void { const window: *Window = switch (T) { Window => self.parent, Surface => self.parent.container.window() orelse return, diff --git a/src/apprt/gtk/winproto/wayland.zig b/src/apprt/gtk/winproto/wayland.zig index 6737e98e2..5f5feca6e 100644 --- a/src/apprt/gtk/winproto/wayland.zig +++ b/src/apprt/gtk/winproto/wayland.zig @@ -410,7 +410,7 @@ pub const Window = struct { _: *gdk.Surface, monitor: *gdk.Monitor, apprt_window: *ApprtWindow, - ) callconv(.C) void { + ) callconv(.c) void { const window = apprt_window.window.as(gtk.Window); const size = apprt_window.config.quick_terminal_size; const position = apprt_window.config.quick_terminal_position; diff --git a/src/crash/sentry.zig b/src/crash/sentry.zig index e9c49048c..c29184020 100644 --- a/src/crash/sentry.zig +++ b/src/crash/sentry.zig @@ -166,7 +166,7 @@ fn beforeSend( event_val: sentry.c.sentry_value_t, _: ?*anyopaque, _: ?*anyopaque, -) callconv(.C) sentry.c.sentry_value_t { +) callconv(.c) sentry.c.sentry_value_t { // The native SDK at the time of writing doesn't support thread-local // scopes. The full SDK has one global scope. So we use the beforeSend // handler to set thread-specific data such as window size, grid size, @@ -237,7 +237,7 @@ fn beforeSend( } pub const Transport = struct { - pub fn send(envelope: *sentry.Envelope, ud: ?*anyopaque) callconv(.C) void { + pub fn send(envelope: *sentry.Envelope, ud: ?*anyopaque) callconv(.c) void { _ = ud; defer envelope.deinit(); diff --git a/src/os/flatpak.zig b/src/os/flatpak.zig index 61a217929..7b92a8ba9 100644 --- a/src/os/flatpak.zig +++ b/src/os/flatpak.zig @@ -444,7 +444,7 @@ pub const FlatpakHostCommand = struct { _: [*c]const u8, params: ?*c.GVariant, ud: ?*anyopaque, - ) callconv(.C) void { + ) callconv(.c) void { const self = @as(*FlatpakHostCommand, @ptrCast(@alignCast(ud))); const state = state: { self.state_mutex.lock(); diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index e6f77216f..ddc94b1ec 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -1524,7 +1524,7 @@ const CompletionBlock = objc.Block(struct { self: *Metal }, .{ fn bufferCompleted( block: *const CompletionBlock.Context, buffer_id: objc.c.id, -) callconv(.C) void { +) callconv(.c) void { const self = block.self; const buffer = objc.Object.fromId(buffer_id); diff --git a/src/renderer/shadertoy.zig b/src/renderer/shadertoy.zig index 8c9b68447..45d86cbfe 100644 --- a/src/renderer/shadertoy.zig +++ b/src/renderer/shadertoy.zig @@ -250,7 +250,7 @@ fn spvCross( // It would be better to get this out into an output parameter to // show users but for now we can just log it. c.spvc_context_set_error_callback(ctx, @ptrCast(&(struct { - fn callback(_: ?*anyopaque, msg_ptr: [*c]const u8) callconv(.C) void { + fn callback(_: ?*anyopaque, msg_ptr: [*c]const u8) callconv(.c) void { const msg = std.mem.sliceTo(msg_ptr, 0); std.log.warn("spirv-cross error message={s}", .{msg}); } From 362c5cb05ffc548398ee5beb6e1a5ecb24e6f665 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 7 May 2025 08:17:15 -0700 Subject: [PATCH 45/49] Allow struct/union/enum binding types to have default values This allows for `keybind = super+d=new_split` to now work (defaults to "auto"). This will also let us convert void types to union/enum/struct types in the future without breaking existing bindings. --- src/input/Binding.zig | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 6bb50fc5d..6583e1462 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -568,6 +568,8 @@ pub const Action = union(enum) { left, up, auto, // splits along the larger direction + + pub const default: SplitDirection = .auto; }; pub const SplitFocusDirection = enum { @@ -729,7 +731,28 @@ pub const Action = union(enum) { Action.CursorKey => return Error.InvalidAction, else => { - const idx = colonIdx orelse return Error.InvalidFormat; + // Get the parameter after the colon. The parameter + // can be optional for action types that can have a + // "default" decl. + const idx = colonIdx orelse { + switch (@typeInfo(field.type)) { + .@"struct", + .@"union", + .@"enum", + => if (@hasDecl(field.type, "default")) { + return @unionInit( + Action, + field.name, + @field(field.type, "default"), + ); + }, + + else => {}, + } + + return Error.InvalidFormat; + }; + const param = input[idx + 1 ..]; return @unionInit( Action, @@ -2015,6 +2038,17 @@ test "parse: action with enum" { } } +test "parse: action with enum with default" { + const testing = std.testing; + + // parameter + { + const binding = try parseSingle("a=new_split"); + try testing.expect(binding.action == .new_split); + try testing.expectEqual(Action.SplitDirection.auto, binding.action.new_split); + } +} + test "parse: action with int" { const testing = std.testing; From 00a2d544204b169c5a1a6e221a265153d6687326 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Wed, 7 May 2025 10:36:31 -0500 Subject: [PATCH 46/49] gtk: only allow one config error dialog at a time This fixes a problem introduced by #7241 that would cause multiple error dialogs to be shown. --- src/apprt/gtk/App.zig | 3 ++ src/apprt/gtk/ConfigErrorsDialog.zig | 58 ++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index c9a973611..06cc41b9d 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -74,6 +74,9 @@ cursor_none: ?*gdk.Cursor, /// The clipboard confirmation window, if it is currently open. clipboard_confirmation_window: ?*ClipboardConfirmationWindow = null, +/// The config errors dialog, if it is currently open. +config_errors_dialog: ?ConfigErrorsDialog = null, + /// The window containing the quick terminal. /// Null when never initialized. quick_terminal: ?*Window = null, diff --git a/src/apprt/gtk/ConfigErrorsDialog.zig b/src/apprt/gtk/ConfigErrorsDialog.zig index a1a2a61af..c2de2f1dc 100644 --- a/src/apprt/gtk/ConfigErrorsDialog.zig +++ b/src/apprt/gtk/ConfigErrorsDialog.zig @@ -29,15 +29,39 @@ error_message: *gtk.TextBuffer, pub fn maybePresent(app: *App, window: ?*Window) void { if (app.config._diagnostics.empty()) return; - var builder = switch (DialogType) { - adw.AlertDialog => Builder.init("config-errors-dialog", 1, 5), - adw.MessageDialog => Builder.init("config-errors-dialog", 1, 2), - else => unreachable, - }; - defer builder.deinit(); + const config_errors_dialog = config_errors_dialog: { + if (app.config_errors_dialog) |config_errors_dialog| break :config_errors_dialog config_errors_dialog; - const dialog = builder.getObject(DialogType, "config_errors_dialog").?; - const error_message = builder.getObject(gtk.TextBuffer, "error_message").?; + var builder = switch (DialogType) { + adw.AlertDialog => Builder.init("config-errors-dialog", 1, 5), + adw.MessageDialog => Builder.init("config-errors-dialog", 1, 2), + else => unreachable, + }; + // defer builder.deinit(); + + const dialog = builder.getObject(DialogType, "config_errors_dialog").?; + const error_message = builder.getObject(gtk.TextBuffer, "error_message").?; + + _ = DialogType.signals.response.connect(dialog, *App, onResponse, app, .{}); + + app.config_errors_dialog = .{ + .builder = builder, + .dialog = dialog, + .error_message = error_message, + }; + + break :config_errors_dialog app.config_errors_dialog.?; + }; + + { + var start = std.mem.zeroes(gtk.TextIter); + config_errors_dialog.error_message.getStartIter(&start); + + var end = std.mem.zeroes(gtk.TextIter); + config_errors_dialog.error_message.getEndIter(&end); + + config_errors_dialog.error_message.delete(&start, &end); + } var msg_buf: [4095:0]u8 = undefined; var fbs = std.io.fixedBufferStream(&msg_buf); @@ -52,22 +76,24 @@ pub fn maybePresent(app: *App, window: ?*Window) void { continue; }; - error_message.insertAtCursor(&msg_buf, @intCast(fbs.pos)); - error_message.insertAtCursor("\n", 1); + config_errors_dialog.error_message.insertAtCursor(&msg_buf, @intCast(fbs.pos)); + config_errors_dialog.error_message.insertAtCursor("\n", 1); } - _ = DialogType.signals.response.connect(dialog, *App, onResponse, app, .{}); - - const parent = if (window) |w| w.window.as(gtk.Widget) else null; - switch (DialogType) { - adw.AlertDialog => dialog.as(adw.Dialog).present(parent), - adw.MessageDialog => dialog.as(gtk.Window).present(), + adw.AlertDialog => { + const parent = if (window) |w| w.window.as(gtk.Widget) else null; + config_errors_dialog.dialog.as(adw.Dialog).present(parent); + }, + adw.MessageDialog => config_errors_dialog.dialog.as(gtk.Window).present(), else => unreachable, } } fn onResponse(_: *DialogType, response: [*:0]const u8, app: *App) callconv(.c) void { + if (app.config_errors_dialog) |config_errors_dialog| config_errors_dialog.builder.deinit(); + app.config_errors_dialog = null; + if (std.mem.orderZ(u8, response, "reload") == .eq) { app.reloadConfig(.app, .{}) catch |err| { log.warn("error reloading config error={}", .{err}); From 5d81a31a49cb3971c3e886d4a400b5db80476e23 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Wed, 7 May 2025 11:12:07 -0500 Subject: [PATCH 47/49] gtk: remove dead code --- src/apprt/gtk/ConfigErrorsDialog.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/apprt/gtk/ConfigErrorsDialog.zig b/src/apprt/gtk/ConfigErrorsDialog.zig index c2de2f1dc..ccc5599ad 100644 --- a/src/apprt/gtk/ConfigErrorsDialog.zig +++ b/src/apprt/gtk/ConfigErrorsDialog.zig @@ -37,7 +37,6 @@ pub fn maybePresent(app: *App, window: ?*Window) void { adw.MessageDialog => Builder.init("config-errors-dialog", 1, 2), else => unreachable, }; - // defer builder.deinit(); const dialog = builder.getObject(DialogType, "config_errors_dialog").?; const error_message = builder.getObject(gtk.TextBuffer, "error_message").?; From 69a744b52153237dd31ad377d95cc2b645d41a85 Mon Sep 17 00:00:00 2001 From: tangowithfoxtrot <5676771+tangowithfoxtrot@users.noreply.github.com> Date: Wed, 7 May 2025 11:46:36 -0700 Subject: [PATCH 48/49] docs: fix minor grammatical error --- src/config/Config.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/Config.zig b/src/config/Config.zig index 95dcf3420..7850fd068 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -2004,7 +2004,7 @@ keybind: Keybinds = .{}, /// macOS doesn't have a distinct "alt" key and instead has the "option" /// key which behaves slightly differently. On macOS by default, the -/// option key plus a character will sometimes produces a Unicode character. +/// option key plus a character will sometimes produce a Unicode character. /// For example, on US standard layouts option-b produces "∫". This may be /// undesirable if you want to use "option" as an "alt" key for keybindings /// in terminal programs or shells. From 9c70f8aee17ab68e4b9bd5a3bd5449dd4b2981ee Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Thu, 16 Jan 2025 16:08:35 -0600 Subject: [PATCH 49/49] core: add context menu key --- include/ghostty.h | 3 +++ src/input/key.zig | 4 +++- src/input/keycodes.zig | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/ghostty.h b/include/ghostty.h index 18c547910..9409fa7c6 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -240,6 +240,9 @@ typedef enum { GHOSTTY_KEY_KP_DELETE, GHOSTTY_KEY_KP_BEGIN, + // special keys + GHOSTTY_KEY_CONTEXT_MENU, + // modifiers GHOSTTY_KEY_LEFT_SHIFT, GHOSTTY_KEY_LEFT_CONTROL, diff --git a/src/input/key.zig b/src/input/key.zig index ec65170f2..c0f80e294 100644 --- a/src/input/key.zig +++ b/src/input/key.zig @@ -401,7 +401,8 @@ pub const Key = enum(c_int) { kp_delete, kp_begin, - // TODO: media keys + // special keys + context_menu, // modifiers left_shift, @@ -579,6 +580,7 @@ pub const Key = enum(c_int) { .backspace => cimgui.c.ImGuiKey_Backspace, .print_screen => cimgui.c.ImGuiKey_PrintScreen, .pause => cimgui.c.ImGuiKey_Pause, + .context_menu => cimgui.c.ImGuiKey_Menu, .f1 => cimgui.c.ImGuiKey_F1, .f2 => cimgui.c.ImGuiKey_F2, diff --git a/src/input/keycodes.zig b/src/input/keycodes.zig index 67ce46daf..e9adbc156 100644 --- a/src/input/keycodes.zig +++ b/src/input/keycodes.zig @@ -153,6 +153,7 @@ const code_to_key = code_to_key: { .{ "Numpad0", .kp_0 }, .{ "NumpadDecimal", .kp_decimal }, .{ "NumpadEqual", .kp_equal }, + .{ "ContextMenu", .context_menu }, .{ "ControlLeft", .left_control }, .{ "ShiftLeft", .left_shift }, .{ "AltLeft", .left_alt },