mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Merge branch 'ghostty-org:main' into feature-cursor-height
This commit is contained in:
@ -63,7 +63,7 @@ placed at `$XDG_CONFIG_HOME/ghostty/config`, which defaults to
|
|||||||
|
|
||||||
The file format is documented below as an example:
|
The file format is documented below as an example:
|
||||||
|
|
||||||
```
|
```ini
|
||||||
# The syntax is "key = value". The whitespace around the equals doesn't matter.
|
# The syntax is "key = value". The whitespace around the equals doesn't matter.
|
||||||
background = 282c34
|
background = 282c34
|
||||||
foreground= ffffff
|
foreground= ffffff
|
||||||
@ -375,9 +375,9 @@ test cases.
|
|||||||
|
|
||||||
We believe Ghostty is one of the most compliant terminal emulators available.
|
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/))
|
(i.e. [ECMA-48](https://ecma-international.org/publications-and-standards/standards/ecma-48/))
|
||||||
but mostly a defacto standard as defined by popular terminal emulators
|
but mostly a de facto standard as defined by popular terminal emulators
|
||||||
worldwide. Ghostty takes the approach that our behavior is defined by
|
worldwide. Ghostty takes the approach that our behavior is defined by
|
||||||
(1) standards, if available, (2) xterm, if the feature exists, (3)
|
(1) standards, if available, (2) xterm, if the feature exists, (3)
|
||||||
other popular terminals, in that order. This defines what the Ghostty project
|
other popular terminals, in that order. This defines what the Ghostty project
|
||||||
|
25
build.zig
25
build.zig
@ -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
|
// Documentation
|
||||||
if (emit_docs) {
|
if (emit_docs) {
|
||||||
try buildDocumentation(b, config);
|
try buildDocumentation(b, config);
|
||||||
@ -1227,14 +1243,7 @@ fn addDeps(
|
|||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
// This is a bit of a hack that should probably be fixed upstream
|
step.root_module.addImport("objc", objc_dep.module("objc"));
|
||||||
// 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("macos", macos_dep.module("macos"));
|
step.root_module.addImport("macos", macos_dep.module("macos"));
|
||||||
step.linkLibrary(macos_dep.artifact("macos"));
|
step.linkLibrary(macos_dep.artifact("macos"));
|
||||||
try static_libs.append(macos_dep.artifact("macos").getEmittedBin());
|
try static_libs.append(macos_dep.artifact("macos").getEmittedBin());
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
.zig_objc = .{
|
.zig_objc = .{
|
||||||
.url = "https://github.com/mitchellh/zig-objc/archive/fe5ac419530cf800294369d996133fe9cd067aec.tar.gz",
|
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
|
||||||
.hash = "122034b3e15d582d8d101a7713e5f13c872b8b8eb6d9cb47515b8e34ee75e122630d",
|
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
|
||||||
},
|
},
|
||||||
.zig_js = .{
|
.zig_js = .{
|
||||||
.url = "https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz",
|
.url = "https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz",
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
||||||
# more details.
|
# more details.
|
||||||
"sha256-5LBZAExb4PJefW+M0Eo+TcoszhBdIFTGBOv6lte5L0Q="
|
"sha256-dNGDbVaPxDbIrDUkDwjzeRHHVcX4KnWKciXiTp1c7lE="
|
||||||
|
@ -929,6 +929,13 @@ fn loadRuntimeCss(
|
|||||||
\\ --headerbar-bg-color: rgb({d},{d},{d});
|
\\ --headerbar-bg-color: rgb({d},{d},{d});
|
||||||
\\ --headerbar-backdrop-color: oklab(from var(--headerbar-bg-color) calc(l * 0.9) a b / alpha);
|
\\ --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.r,
|
||||||
headerbar_foreground.g,
|
headerbar_foreground.g,
|
||||||
|
@ -14,6 +14,7 @@ pub const emoji = @embedFile("res/NotoColorEmoji.ttf");
|
|||||||
pub const emoji_text = @embedFile("res/NotoEmoji-Regular.ttf");
|
pub const emoji_text = @embedFile("res/NotoEmoji-Regular.ttf");
|
||||||
|
|
||||||
/// Fonts with general properties
|
/// Fonts with general properties
|
||||||
|
pub const arabic = @embedFile("res/KawkabMono-Regular.ttf");
|
||||||
pub const variable = @embedFile("res/Lilex-VF.ttf");
|
pub const variable = @embedFile("res/Lilex-VF.ttf");
|
||||||
|
|
||||||
/// Font with nerd fonts embedded.
|
/// Font with nerd fonts embedded.
|
||||||
|
BIN
src/font/res/KawkabMono-Regular.ttf
Normal file
BIN
src/font/res/KawkabMono-Regular.ttf
Normal file
Binary file not shown.
@ -190,6 +190,11 @@ pub const Shaper = struct {
|
|||||||
// Reset the buffer for our current run
|
// Reset the buffer for our current run
|
||||||
self.shaper.hb_buf.reset();
|
self.shaper.hb_buf.reset();
|
||||||
self.shaper.hb_buf.setContentType(.unicode);
|
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 {
|
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" {
|
test "shape emoji width" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
@ -1146,6 +1191,7 @@ const TestShaper = struct {
|
|||||||
const TestFont = enum {
|
const TestFont = enum {
|
||||||
inconsolata,
|
inconsolata,
|
||||||
monaspace_neon,
|
monaspace_neon,
|
||||||
|
arabic,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper to return a fully initialized shaper.
|
/// Helper to return a fully initialized shaper.
|
||||||
@ -1159,6 +1205,7 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper {
|
|||||||
const testFont = switch (font_req) {
|
const testFont = switch (font_req) {
|
||||||
.inconsolata => font.embedded.inconsolata,
|
.inconsolata => font.embedded.inconsolata,
|
||||||
.monaspace_neon => font.embedded.monaspace_neon,
|
.monaspace_neon => font.embedded.monaspace_neon,
|
||||||
|
.arabic => font.embedded.arabic,
|
||||||
};
|
};
|
||||||
|
|
||||||
var lib = try Library.init();
|
var lib = try Library.init();
|
||||||
|
3
src/font/shaper/testdata/arabic.txt
vendored
Normal file
3
src/font/shaper/testdata/arabic.txt
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
غريبه لاني عربي أبا عن جد
|
||||||
|
واتكلم الانجليزية بطلاقة اكثر من ٢٥ سنه
|
||||||
|
ومع هذا اجد العربيه افضل لان فيها الكثير من المفردات الاكثر دقه بالوصف
|
@ -175,9 +175,9 @@ pub const GPUState = struct {
|
|||||||
instance: InstanceBuffer, // MTLBuffer
|
instance: InstanceBuffer, // MTLBuffer
|
||||||
|
|
||||||
pub fn init() !GPUState {
|
pub fn init() !GPUState {
|
||||||
const device = objc.Object.fromId(mtl.MTLCreateSystemDefaultDevice());
|
const device = try chooseDevice();
|
||||||
const queue = device.msgSend(objc.Object, objc.sel("newCommandQueue"), .{});
|
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, &.{
|
var instance = try InstanceBuffer.initFill(device, &.{
|
||||||
0, 1, 3, // Top-left triangle
|
0, 1, 3, // Top-left triangle
|
||||||
@ -200,13 +200,33 @@ pub const GPUState = struct {
|
|||||||
return result;
|
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 that’s 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 {
|
pub fn deinit(self: *GPUState) void {
|
||||||
// Wait for all of our inflight draws to complete so that
|
// Wait for all of our inflight draws to complete so that
|
||||||
// we can cleanly deinit our GPU state.
|
// we can cleanly deinit our GPU state.
|
||||||
for (0..BufferCount) |_| self.frame_sema.wait();
|
for (0..BufferCount) |_| self.frame_sema.wait();
|
||||||
for (&self.frames) |*frame| frame.deinit();
|
for (&self.frames) |*frame| frame.deinit();
|
||||||
self.instance.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
|
/// Get the next frame state to draw to. This will wait on the
|
||||||
@ -269,13 +289,13 @@ pub const FrameState = struct {
|
|||||||
.size = 8,
|
.size = 8,
|
||||||
.format = .grayscale,
|
.format = .grayscale,
|
||||||
});
|
});
|
||||||
errdefer deinitMTLResource(grayscale);
|
errdefer grayscale.release();
|
||||||
const color = try initAtlasTexture(device, &.{
|
const color = try initAtlasTexture(device, &.{
|
||||||
.data = undefined,
|
.data = undefined,
|
||||||
.size = 8,
|
.size = 8,
|
||||||
.format = .rgba,
|
.format = .rgba,
|
||||||
});
|
});
|
||||||
errdefer deinitMTLResource(color);
|
errdefer color.release();
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.uniforms = uniforms,
|
.uniforms = uniforms,
|
||||||
@ -290,8 +310,8 @@ pub const FrameState = struct {
|
|||||||
self.uniforms.deinit();
|
self.uniforms.deinit();
|
||||||
self.cells.deinit();
|
self.cells.deinit();
|
||||||
self.cells_bg.deinit();
|
self.cells_bg.deinit();
|
||||||
deinitMTLResource(self.grayscale);
|
self.grayscale.release();
|
||||||
deinitMTLResource(self.color);
|
self.color.release();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -319,8 +339,8 @@ pub const CustomShaderState = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *CustomShaderState) void {
|
pub fn deinit(self: *CustomShaderState) void {
|
||||||
deinitMTLResource(self.front_texture);
|
self.front_texture.release();
|
||||||
deinitMTLResource(self.back_texture);
|
self.back_texture.release();
|
||||||
self.sampler.deinit();
|
self.sampler.deinit();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -2057,8 +2077,8 @@ pub fn setScreenSize(
|
|||||||
// Only free our previous texture if this isn't our first
|
// Only free our previous texture if this isn't our first
|
||||||
// time setting the custom shader state.
|
// time setting the custom shader state.
|
||||||
if (state.uniforms.resolution[0] > 0) {
|
if (state.uniforms.resolution[0] > 0) {
|
||||||
deinitMTLResource(state.front_texture);
|
state.front_texture.release();
|
||||||
deinitMTLResource(state.back_texture);
|
state.back_texture.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
state.uniforms.resolution = .{
|
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");
|
const width = texture.getProperty(c_ulong, "width");
|
||||||
if (atlas.size > width) {
|
if (atlas.size > width) {
|
||||||
// Free our old texture
|
// Free our old texture
|
||||||
deinitMTLResource(texture.*);
|
texture.*.release();
|
||||||
|
|
||||||
// Reallocate
|
// Reallocate
|
||||||
texture.* = try initAtlasTexture(device, atlas);
|
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);
|
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 {
|
test {
|
||||||
_ = mtl_cell;
|
_ = mtl_cell;
|
||||||
}
|
}
|
||||||
|
@ -175,4 +175,4 @@ pub const MTLSize = extern struct {
|
|||||||
depth: c_ulong,
|
depth: c_ulong,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub extern "c" fn MTLCreateSystemDefaultDevice() ?*anyopaque;
|
pub extern "c" fn MTLCopyAllDevices() ?*anyopaque;
|
||||||
|
Reference in New Issue
Block a user