mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge branch 'main' of github.com:AlexJuca/ghostty into feature/display-memory-size-in-bytes-and-kb
This commit is contained in:
@ -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",
|
||||
|
@ -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 = &.{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
.{
|
||||
.name = "simdutf",
|
||||
.version = "4.0.9",
|
||||
.version = "5.2.8",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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(>kDestroy), self, null, c.G_CONNECT_DEFAULT);
|
||||
|
||||
// Set some state
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.?);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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| {
|
||||
|
@ -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 = .{} },
|
||||
|
Reference in New Issue
Block a user