Merge branch 'main' of github.com:AlexJuca/ghostty into feature/display-memory-size-in-bytes-and-kb

This commit is contained in:
Alexandre Antonio Juca
2025-01-03 23:37:34 +01:00
16 changed files with 177 additions and 68 deletions

View File

@ -1,10 +1,10 @@
.{
.name = "cimgui",
.version = "1.89.9",
.version = "1.90.6", // -docking branch
.paths = .{""},
.dependencies = .{
// This should be kept in sync with the submodule in the cimgui source
// code to be safe that they're compatible.
// code in ./vendor/ to be safe that they're compatible.
.imgui = .{
.url = "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz",
.hash = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402",

View File

@ -13,7 +13,56 @@ pub fn build(b: *std.Build) !void {
) orelse (target.result.os.tag != .windows);
const freetype_enabled = b.option(bool, "enable-freetype", "Build freetype") orelse true;
const module = b.addModule("fontconfig", .{ .root_source_file = b.path("main.zig") });
const module = b.addModule("fontconfig", .{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
const test_exe = b.addTest(.{
.name = "test",
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
if (b.systemIntegrationOption("fontconfig", .{})) {
module.linkSystemLibrary("fontconfig", dynamic_link_opts);
test_exe.linkSystemLibrary2("fontconfig", dynamic_link_opts);
} else {
const lib = try buildLib(b, module, .{
.target = target,
.optimize = optimize,
.libxml2_enabled = libxml2_enabled,
.libxml2_iconv_enabled = libxml2_iconv_enabled,
.freetype_enabled = freetype_enabled,
.dynamic_link_opts = dynamic_link_opts,
});
test_exe.linkLibrary(lib);
}
}
pub fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
const target = options.target;
const optimize = options.optimize;
const libxml2_enabled = options.libxml2_enabled;
const libxml2_iconv_enabled = options.libxml2_iconv_enabled;
const freetype_enabled = options.freetype_enabled;
const upstream = b.dependency("fontconfig", .{});
const lib = b.addStaticLibrary(.{
@ -131,13 +180,7 @@ pub fn build(b: *std.Build) !void {
}
}
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
const dynamic_link_opts = options.dynamic_link_opts;
// Freetype2
_ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help
@ -194,16 +237,7 @@ pub fn build(b: *std.Build) !void {
b.installArtifact(lib);
const test_exe = b.addTest(.{
.name = "test",
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
test_exe.linkLibrary(lib);
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
return lib;
}
const headers = &.{

View File

@ -5,36 +5,59 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const module = b.addModule("oniguruma", .{ .root_source_file = b.path("main.zig") });
const module = b.addModule("oniguruma", .{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
const upstream = b.dependency("oniguruma", .{});
const lib = try buildOniguruma(b, upstream, target, optimize);
module.addIncludePath(upstream.path("src"));
b.installArtifact(lib);
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
var test_exe: ?*std.Build.Step.Compile = null;
if (target.query.isNative()) {
const test_exe = b.addTest(.{
test_exe = b.addTest(.{
.name = "test",
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
test_exe.linkLibrary(lib);
const tests_run = b.addRunArtifact(test_exe);
const tests_run = b.addRunArtifact(test_exe.?);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
// Uncomment this if we're debugging tests
b.installArtifact(test_exe);
b.installArtifact(test_exe.?);
}
if (b.systemIntegrationOption("oniguruma", .{})) {
module.linkSystemLibrary("oniguruma", dynamic_link_opts);
if (test_exe) |exe| {
exe.linkSystemLibrary2("oniguruma", dynamic_link_opts);
}
} else {
const lib = try buildLib(b, module, .{
.target = target,
.optimize = optimize,
});
if (test_exe) |exe| {
exe.linkLibrary(lib);
}
}
}
fn buildOniguruma(
b: *std.Build,
upstream: *std.Build.Dependency,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
) !*std.Build.Step.Compile {
pub fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
const target = options.target;
const optimize = options.optimize;
const upstream = b.dependency("oniguruma", .{});
const lib = b.addStaticLibrary(.{
.name = "oniguruma",
.target = target,
@ -43,6 +66,7 @@ fn buildOniguruma(
const t = target.result;
lib.linkLibC();
lib.addIncludePath(upstream.path("src"));
module.addIncludePath(upstream.path("src"));
if (target.result.isDarwin()) {
const apple_sdk = @import("apple_sdk");
@ -134,5 +158,7 @@ fn buildOniguruma(
.{ .include_extensions = &.{".h"} },
);
b.installArtifact(lib);
return lib;
}

View File

@ -1,6 +1,6 @@
.{
.name = "simdutf",
.version = "4.0.9",
.version = "5.2.8",
.paths = .{""},
.dependencies = .{
.apple_sdk = .{ .path = "../apple-sdk" },

View File

@ -1644,7 +1644,7 @@ fn gtkActionPresentSurface(
// Convert that u64 to pointer to a core surface. A value of zero
// means that there was no target surface for the notification so
// we dont' focus any surface.
// we don't focus any surface.
const ptr_int: u64 = c.g_variant_get_uint64(parameter);
if (ptr_int == 0) return;
const surface: *CoreSurface = @ptrFromInt(ptr_int);

View File

@ -64,6 +64,7 @@ fn init(
c.gtk_window_set_title(gtk_window, titleText(request));
c.gtk_window_set_default_size(gtk_window, 550, 275);
c.gtk_window_set_resizable(gtk_window, 0);
c.gtk_widget_add_css_class(@ptrCast(@alignCast(gtk_window)), "clipboard-confirmation-window");
_ = c.g_signal_connect_data(
window,
"destroy",

View File

@ -55,6 +55,7 @@ fn init(self: *ConfigErrors, app: *App) !void {
c.gtk_window_set_default_size(gtk_window, 600, 275);
c.gtk_window_set_resizable(gtk_window, 0);
c.gtk_window_set_icon_name(gtk_window, build_config.bundle_id);
c.gtk_widget_add_css_class(@ptrCast(@alignCast(gtk_window)), "config-errors-window");
_ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT);
// Set some state

View File

@ -143,6 +143,7 @@ const Window = struct {
c.gtk_window_set_title(gtk_window, "Ghostty: Terminal Inspector");
c.gtk_window_set_default_size(gtk_window, 1000, 600);
c.gtk_window_set_icon_name(gtk_window, build_config.bundle_id);
c.gtk_widget_add_css_class(@ptrCast(@alignCast(gtk_window)), "inspector-window");
// Initialize our imgui widget
try self.imgui_widget.init();

View File

@ -129,7 +129,7 @@ pub fn eventMods(
// On Wayland, we have to use the GDK device because the mods sent
// to this event do not have the modifier key applied if it was
// presssed (i.e. left control).
// pressed (i.e. left control).
break :mods translateMods(c.gdk_device_get_modifier_state(device));
};

View File

@ -2916,7 +2916,7 @@ pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
self.@"config-default-files" = true;
// Keep track of the replay steps up to this point so we
// can replay if we are disgarding the default files.
// can replay if we are discarding the default files.
const replay_len_start = self._replay_steps.items.len;
// Keep track of font families because if they are set from the CLI

View File

@ -341,7 +341,7 @@ pub const Action = union(enum) {
/// the direction given. For example `new_split:up`. Valid values are left, right, up, down and auto.
new_split: SplitDirection,
/// Focus on a split in a given direction. For example `goto_split:top`. Valid values are top, bottom, left, right, previous and next.
/// Focus on a split in a given direction. For example `goto_split:up`. Valid values are left, right, up, down, previous and next.
goto_split: SplitFocusDirection,
/// zoom/unzoom the current split.

View File

@ -282,7 +282,12 @@ fn legacy(
// If we match a control sequence, we output that directly. For
// ctrlSeq we have to use all mods because we want it to only
// match ctrl+<char>.
if (ctrlSeq(self.event.utf8, self.event.unshifted_codepoint, all_mods)) |char| {
if (ctrlSeq(
self.event.key,
self.event.utf8,
self.event.unshifted_codepoint,
all_mods,
)) |char| {
// C0 sequences support alt-as-esc prefixing.
if (binding_mods.alt) {
if (buf.len < 2) return error.OutOfMemory;
@ -538,19 +543,17 @@ fn pcStyleFunctionKey(
/// into a C0 byte. There are many cases for this and you should read
/// the source code to understand them.
fn ctrlSeq(
logical_key: key.Key,
utf8: []const u8,
unshifted_codepoint: u21,
mods: key.Mods,
) ?u8 {
const ctrl_only = comptime (key.Mods{ .ctrl = true }).int();
// If ctrl is not pressed then we never do anything.
if (!mods.ctrl) return null;
// If we don't have exactly one byte in our utf8 sequence, then
// we don't do anything, since all our ctrl keys are based on ASCII.
if (utf8.len != 1) return null;
const char, const unset_mods = unset_mods: {
var char = utf8[0];
var unset_mods = mods;
// Remove alt from our modifiers because it does not impact whether
@ -558,6 +561,34 @@ fn ctrlSeq(
// logic separately.
unset_mods.alt = false;
var char: u8 = char: {
// If we have exactly one UTF8 byte, we assume that is the
// character we want to convert to a C0 byte.
if (utf8.len == 1) break :char utf8[0];
// If we have a logical key that maps to a single byte
// printable character, we use that. History to explain this:
// this was added to support cyrillic keyboard layouts such
// as Russian and Mongolian. These layouts have a `c` key that
// maps to U+0441 (cyrillic small letter "c") but every
// terminal I've tested encodes this as ctrl+c.
if (logical_key.codepoint()) |cp| {
if (std.math.cast(u8, cp)) |byte| {
// For this specific case, we only map to the key if
// we have exactly ctrl pressed. This is because shift
// would modify the key and we don't know how to do that
// properly here (don't have the layout). And we want
// to encode shift as CSIu.
if (unset_mods.int() != ctrl_only) return null;
break :char byte;
}
}
// Otherwise we don't have a character to convert that
// we can reliably map to a C0 byte.
return null;
};
// Remove shift if we have something outside of the US letter
// range. This is so that characters such as `ctrl+shift+-`
// generate the correct ctrl-seq (used by emacs).
@ -596,7 +627,6 @@ fn ctrlSeq(
};
// After unsetting, we only continue if we have ONLY control set.
const ctrl_only = comptime (key.Mods{ .ctrl = true }).int();
if (unset_mods.int() != ctrl_only) return null;
// From Kitty's key encoding logic. I tried to discern the exact
@ -2132,36 +2162,52 @@ test "legacy: hu layout ctrl+ő sends proper codepoint" {
const actual = try enc.legacy(&buf);
try testing.expectEqualStrings("[337;5u", actual[1..]);
}
test "ctrlseq: normal ctrl c" {
const seq = ctrlSeq("c", 'c', .{ .ctrl = true });
const seq = ctrlSeq(.invalid, "c", 'c', .{ .ctrl = true });
try testing.expectEqual(@as(u8, 0x03), seq.?);
}
test "ctrlseq: normal ctrl c, right control" {
const seq = ctrlSeq("c", 'c', .{ .ctrl = true, .sides = .{ .ctrl = .right } });
const seq = ctrlSeq(.invalid, "c", 'c', .{ .ctrl = true, .sides = .{ .ctrl = .right } });
try testing.expectEqual(@as(u8, 0x03), seq.?);
}
test "ctrlseq: alt should be allowed" {
const seq = ctrlSeq("c", 'c', .{ .alt = true, .ctrl = true });
const seq = ctrlSeq(.invalid, "c", 'c', .{ .alt = true, .ctrl = true });
try testing.expectEqual(@as(u8, 0x03), seq.?);
}
test "ctrlseq: no ctrl does nothing" {
try testing.expect(ctrlSeq("c", 'c', .{}) == null);
try testing.expect(ctrlSeq(.invalid, "c", 'c', .{}) == null);
}
test "ctrlseq: shifted non-character" {
const seq = ctrlSeq("_", '-', .{ .ctrl = true, .shift = true });
const seq = ctrlSeq(.invalid, "_", '-', .{ .ctrl = true, .shift = true });
try testing.expectEqual(@as(u8, 0x1F), seq.?);
}
test "ctrlseq: caps ascii letter" {
const seq = ctrlSeq("C", 'c', .{ .ctrl = true, .caps_lock = true });
const seq = ctrlSeq(.invalid, "C", 'c', .{ .ctrl = true, .caps_lock = true });
try testing.expectEqual(@as(u8, 0x03), seq.?);
}
test "ctrlseq: shift does not generate ctrl seq" {
try testing.expect(ctrlSeq("C", 'c', .{ .shift = true }) == null);
try testing.expect(ctrlSeq("C", 'c', .{ .shift = true, .ctrl = true }) == null);
try testing.expect(ctrlSeq(.invalid, "C", 'c', .{ .shift = true }) == null);
try testing.expect(ctrlSeq(.invalid, "C", 'c', .{ .shift = true, .ctrl = true }) == null);
}
test "ctrlseq: russian ctrl c" {
const seq = ctrlSeq(.c, "с", 0x0441, .{ .ctrl = true });
try testing.expectEqual(@as(u8, 0x03), seq.?);
}
test "ctrlseq: russian shifted ctrl c" {
const seq = ctrlSeq(.c, "с", 0x0441, .{ .ctrl = true, .shift = true });
try testing.expect(seq == null);
}
test "ctrlseq: russian alt ctrl c" {
const seq = ctrlSeq(.c, "с", 0x0441, .{ .ctrl = true, .alt = true });
try testing.expectEqual(@as(u8, 0x03), seq.?);
}

View File

@ -295,7 +295,7 @@ pub const Key = enum(c_int) {
eight,
nine,
// puncuation
// punctuation
semicolon,
space,
apostrophe,
@ -411,7 +411,7 @@ pub const Key = enum(c_int) {
/// may be from the number row or the keypad, but it always maps
/// to '.zero'.
///
/// This is what we want, we awnt people to create keybindings that
/// This is what we want, we want people to create keybindings that
/// are independent of the physical key.
pub fn fromASCII(ch: u8) ?Key {
return switch (ch) {

View File

@ -35,7 +35,7 @@ pub const VTEvent = struct {
const Kind = enum { print, execute, csi, esc, osc, dcs, apc };
const Metadata = std.StringHashMap([:0]const u8);
/// Initiaze the event information for the given parser action.
/// Initialize the event information for the given parser action.
pub fn init(
alloc: Allocator,
surface: *Surface,

View File

@ -58,6 +58,15 @@ fn dir(
opts: Options,
internal_opts: InternalOptions,
) ![]u8 {
// If we have a cached home dir, use that.
if (opts.home) |home| {
return try std.fs.path.join(alloc, &[_][]const u8{
home,
internal_opts.default_subdir,
opts.subdir orelse "",
});
}
// First check the env var. On Windows we have to allocate so this tracks
// both whether we have the env var and whether we own it.
// on Windows we treat `LOCALAPPDATA` as a fallback for `XDG_CONFIG_HOME`
@ -93,15 +102,6 @@ fn dir(
return try alloc.dupe(u8, env);
}
// If we have a cached home dir, use that.
if (opts.home) |home| {
return try std.fs.path.join(alloc, &[_][]const u8{
home,
internal_opts.default_subdir,
opts.subdir orelse "",
});
}
// Get our home dir
var buf: [1024]u8 = undefined;
if (try homedir.home(&buf)) |home| {

View File

@ -764,7 +764,7 @@ pub fn updateFrame(
// We used to share terminal state, but we've since learned through
// analysis that it is faster to copy the terminal state than to
// hold the lock wile rebuilding GPU cells.
// hold the lock while rebuilding GPU cells.
var screen_copy = try state.terminal.screen.clone(
self.alloc,
.{ .viewport = .{} },