Merge branch 'ghostty-org:main' into feature-cursor-height

This commit is contained in:
I Duran
2024-11-05 12:30:22 +03:00
committed by GitHub
11 changed files with 114 additions and 33 deletions

View File

@ -63,7 +63,7 @@ placed at `$XDG_CONFIG_HOME/ghostty/config`, which defaults to
The file format is documented below as an example:
```
```ini
# The syntax is "key = value". The whitespace around the equals doesn't matter.
background = 282c34
foreground= ffffff
@ -375,7 +375,7 @@ test cases.
We believe Ghostty is one of the most compliant terminal emulators available.
Terminal behavior is partially a dejour standard
Terminal behavior is partially a de jure standard
(i.e. [ECMA-48](https://ecma-international.org/publications-and-standards/standards/ecma-48/))
but mostly a de facto standard as defined by popular terminal emulators
worldwide. Ghostty takes the approach that our behavior is defined by

View File

@ -499,6 +499,22 @@ pub fn build(b: *std.Build) !void {
});
}
// Neovim plugin
// This is just a copy-paste of the Vim plugin, but using a Neovim subdir.
// By default, Neovim doesn't look inside share/vim/vimfiles. Some distros
// configure it to do that however. Fedora, does not as a counterexample.
{
const wf = b.addWriteFiles();
_ = wf.add("syntax/ghostty.vim", config_vim.syntax);
_ = wf.add("ftdetect/ghostty.vim", config_vim.ftdetect);
_ = wf.add("ftplugin/ghostty.vim", config_vim.ftplugin);
b.installDirectory(.{
.source_dir = wf.getDirectory(),
.install_dir = .prefix,
.install_subdir = "share/nvim/site",
});
}
// Documentation
if (emit_docs) {
try buildDocumentation(b, config);
@ -1227,14 +1243,7 @@ fn addDeps(
.optimize = optimize,
});
// This is a bit of a hack that should probably be fixed upstream
// in zig-objc, but we need to add the apple SDK paths to the
// zig-objc module so that it can find the objc runtime headers.
const module = objc_dep.module("objc");
module.resolved_target = step.root_module.resolved_target;
try @import("apple_sdk").addPaths(b, module);
step.root_module.addImport("objc", module);
step.root_module.addImport("objc", objc_dep.module("objc"));
step.root_module.addImport("macos", macos_dep.module("macos"));
step.linkLibrary(macos_dep.artifact("macos"));
try static_libs.append(macos_dep.artifact("macos").getEmittedBin());

View File

@ -14,8 +14,8 @@
.lazy = true,
},
.zig_objc = .{
.url = "https://github.com/mitchellh/zig-objc/archive/fe5ac419530cf800294369d996133fe9cd067aec.tar.gz",
.hash = "122034b3e15d582d8d101a7713e5f13c872b8b8eb6d9cb47515b8e34ee75e122630d",
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
},
.zig_js = .{
.url = "https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz",

View File

@ -1,3 +1,3 @@
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
# more details.
"sha256-5LBZAExb4PJefW+M0Eo+TcoszhBdIFTGBOv6lte5L0Q="
"sha256-dNGDbVaPxDbIrDUkDwjzeRHHVcX4KnWKciXiTp1c7lE="

View File

@ -929,6 +929,13 @@ fn loadRuntimeCss(
\\ --headerbar-bg-color: rgb({d},{d},{d});
\\ --headerbar-backdrop-color: oklab(from var(--headerbar-bg-color) calc(l * 0.9) a b / alpha);
\\}}
\\windowhandle {{
\\ background-color: var(--headerbar-bg-color);
\\ color: var(--headerbar-fg-color);
\\}}
\\windowhandle:backdrop {{
\\ background-color: var(--headerbar-backdrop-color);
\\}}
, .{
headerbar_foreground.r,
headerbar_foreground.g,

View File

@ -14,6 +14,7 @@ pub const emoji = @embedFile("res/NotoColorEmoji.ttf");
pub const emoji_text = @embedFile("res/NotoEmoji-Regular.ttf");
/// Fonts with general properties
pub const arabic = @embedFile("res/KawkabMono-Regular.ttf");
pub const variable = @embedFile("res/Lilex-VF.ttf");
/// Font with nerd fonts embedded.

Binary file not shown.

View File

@ -190,6 +190,11 @@ pub const Shaper = struct {
// Reset the buffer for our current run
self.shaper.hb_buf.reset();
self.shaper.hb_buf.setContentType(.unicode);
// We don't support RTL text because RTL in terminals is messy.
// Its something we want to improve. For now, we force LTR because
// our renderers assume a strictly increasing X value.
self.shaper.hb_buf.setDirection(.ltr);
}
pub fn addCodepoint(self: RunIteratorHook, cp: u32, cluster: u32) !void {
@ -453,6 +458,46 @@ test "shape monaspace ligs" {
}
}
// Ghostty doesn't currently support RTL and our renderers assume
// that cells are in strict LTR order. This means that we need to
// force RTL text to be LTR for rendering. This test ensures that
// we are correctly forcing RTL text to be LTR.
test "shape arabic forced LTR" {
const testing = std.testing;
const alloc = testing.allocator;
var testdata = try testShaperWithFont(alloc, .arabic);
defer testdata.deinit();
var screen = try terminal.Screen.init(alloc, 120, 30, 0);
defer screen.deinit();
try screen.testWriteString(@embedFile("testdata/arabic.txt"));
var shaper = &testdata.shaper;
var it = shaper.runIterator(
testdata.grid,
&screen,
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
null,
null,
);
var count: usize = 0;
while (try it.next(alloc)) |run| {
count += 1;
try testing.expectEqual(@as(usize, 25), run.cells);
const cells = try shaper.shape(run);
try testing.expectEqual(@as(usize, 25), cells.len);
var x: u16 = cells[0].x;
for (cells[1..]) |cell| {
try testing.expectEqual(x + 1, cell.x);
x = cell.x;
}
}
try testing.expectEqual(@as(usize, 1), count);
}
test "shape emoji width" {
const testing = std.testing;
const alloc = testing.allocator;
@ -1146,6 +1191,7 @@ const TestShaper = struct {
const TestFont = enum {
inconsolata,
monaspace_neon,
arabic,
};
/// Helper to return a fully initialized shaper.
@ -1159,6 +1205,7 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper {
const testFont = switch (font_req) {
.inconsolata => font.embedded.inconsolata,
.monaspace_neon => font.embedded.monaspace_neon,
.arabic => font.embedded.arabic,
};
var lib = try Library.init();

3
src/font/shaper/testdata/arabic.txt vendored Normal file
View File

@ -0,0 +1,3 @@
غريبه لاني عربي أبا عن جد
واتكلم الانجليزية بطلاقة اكثر من ٢٥ سنه
ومع هذا اجد العربيه افضل لان فيها الكثير من المفردات الاكثر دقه بالوصف

View File

@ -175,9 +175,9 @@ pub const GPUState = struct {
instance: InstanceBuffer, // MTLBuffer
pub fn init() !GPUState {
const device = objc.Object.fromId(mtl.MTLCreateSystemDefaultDevice());
const device = try chooseDevice();
const queue = device.msgSend(objc.Object, objc.sel("newCommandQueue"), .{});
errdefer queue.msgSend(void, objc.sel("release"), .{});
errdefer queue.release();
var instance = try InstanceBuffer.initFill(device, &.{
0, 1, 3, // Top-left triangle
@ -200,13 +200,33 @@ pub const GPUState = struct {
return result;
}
fn chooseDevice() error{NoMetalDevice}!objc.Object {
const devices = objc.Object.fromId(mtl.MTLCopyAllDevices());
defer devices.release();
var chosen_device: ?objc.Object = null;
var iter = devices.iterate();
while (iter.next()) |device| {
// We want a GPU thats connected to a display.
if (device.getProperty(bool, "isHeadless")) continue;
chosen_device = device;
// If the user has an eGPU plugged in, they probably want
// to use it. Otherwise, integrated GPUs are better for
// battery life and thermals.
if (device.getProperty(bool, "isRemovable") or
device.getProperty(bool, "isLowPower")) break;
}
const device = chosen_device orelse return error.NoMetalDevice;
return device.retain();
}
pub fn deinit(self: *GPUState) void {
// Wait for all of our inflight draws to complete so that
// we can cleanly deinit our GPU state.
for (0..BufferCount) |_| self.frame_sema.wait();
for (&self.frames) |*frame| frame.deinit();
self.instance.deinit();
self.queue.msgSend(void, objc.sel("release"), .{});
self.queue.release();
self.device.release();
}
/// Get the next frame state to draw to. This will wait on the
@ -269,13 +289,13 @@ pub const FrameState = struct {
.size = 8,
.format = .grayscale,
});
errdefer deinitMTLResource(grayscale);
errdefer grayscale.release();
const color = try initAtlasTexture(device, &.{
.data = undefined,
.size = 8,
.format = .rgba,
});
errdefer deinitMTLResource(color);
errdefer color.release();
return .{
.uniforms = uniforms,
@ -290,8 +310,8 @@ pub const FrameState = struct {
self.uniforms.deinit();
self.cells.deinit();
self.cells_bg.deinit();
deinitMTLResource(self.grayscale);
deinitMTLResource(self.color);
self.grayscale.release();
self.color.release();
}
};
@ -319,8 +339,8 @@ pub const CustomShaderState = struct {
}
pub fn deinit(self: *CustomShaderState) void {
deinitMTLResource(self.front_texture);
deinitMTLResource(self.back_texture);
self.front_texture.release();
self.back_texture.release();
self.sampler.deinit();
}
};
@ -2057,8 +2077,8 @@ pub fn setScreenSize(
// Only free our previous texture if this isn't our first
// time setting the custom shader state.
if (state.uniforms.resolution[0] > 0) {
deinitMTLResource(state.front_texture);
deinitMTLResource(state.back_texture);
state.front_texture.release();
state.back_texture.release();
}
state.uniforms.resolution = .{
@ -2982,7 +3002,7 @@ fn syncAtlasTexture(device: objc.Object, atlas: *const font.Atlas, texture: *obj
const width = texture.getProperty(c_ulong, "width");
if (atlas.size > width) {
// Free our old texture
deinitMTLResource(texture.*);
texture.*.release();
// Reallocate
texture.* = try initAtlasTexture(device, atlas);
@ -3049,12 +3069,6 @@ fn initAtlasTexture(device: objc.Object, atlas: *const font.Atlas) !objc.Object
return objc.Object.fromId(id);
}
/// Deinitialize a metal resource (buffer, texture, etc.) and free the
/// memory associated with it.
fn deinitMTLResource(obj: objc.Object) void {
obj.msgSend(void, objc.sel("release"), .{});
}
test {
_ = mtl_cell;
}

View File

@ -175,4 +175,4 @@ pub const MTLSize = extern struct {
depth: c_ulong,
};
pub extern "c" fn MTLCreateSystemDefaultDevice() ?*anyopaque;
pub extern "c" fn MTLCopyAllDevices() ?*anyopaque;