diff --git a/.gitignore b/.gitignore index 699ac9a5f21..3567a29b33f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ zig-cache/ .zig-cache/ zig-out/ +zig-pkg/ build-cmake/ CMakeCache.txt CMakeFiles/ diff --git a/build.zig b/build.zig index b89958e5546..eff7f4d8501 100644 --- a/build.zig +++ b/build.zig @@ -25,9 +25,10 @@ pub fn build(b: *std.Build) !void { // use that as the version source of truth. Otherwise we fall back // to what is in the build.zig.zon. const file_version: ?[]const u8 = if (b.build_root.handle.readFileAlloc( - b.allocator, + b.graph.io, "VERSION", - 128, + b.allocator, + .limited(128), )) |content| std.mem.trim( u8, content, @@ -292,7 +293,7 @@ pub fn build(b: *std.Build) !void { // We need to rebuild Ghostty with a baseline CPU target. const valgrind_exe = exe: { var valgrind_config = config; - valgrind_config.target = valgrind_config.baselineTarget(); + valgrind_config.target = valgrind_config.baselineTarget(b); break :exe try buildpkg.GhosttyExe.init( b, &valgrind_config, @@ -337,7 +338,7 @@ pub fn build(b: *std.Build) !void { .filters = test_filters, .root_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), - .target = config.baselineTarget(), + .target = config.baselineTarget(b), .optimize = .Debug, .strip = false, .omit_frame_pointer = false, @@ -352,7 +353,7 @@ pub fn build(b: *std.Build) !void { // Verify our internal libghostty header. const ghostty_h = b.addTranslateC(.{ .root_source_file = b.path("include/ghostty.h"), - .target = config.baselineTarget(), + .target = config.baselineTarget(b), .optimize = .Debug, }); test_exe.root_module.addImport("ghostty.h", ghostty_h.createModule()); diff --git a/build.zig.zon b/build.zig.zon index 9d8d20c3976..f3b81d018c8 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -3,62 +3,63 @@ .version = "1.3.2-dev", .paths = .{""}, .fingerprint = 0x64407a2a0b4147e5, - .minimum_zig_version = "0.15.2", + .minimum_zig_version = "0.16.0", .dependencies = .{ // Zig libs .libxev = .{ // mitchellh/libxev - .url = "https://deps.files.ghostty.org/libxev-34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz", - .hash = "libxev-0.0.0-86vtc4IcEwCqEYxEYoN_3KXmc6A9VLcm22aVImfvecYs", + .url = "https://github.com/mitchellh/libxev/archive/a82a04eabb46b0611c72d6d0e32db2d9dcf2e745.tar.gz", + .hash = "libxev-0.0.0-86vtcwIRFACVrx54GaHsMFFlyC4dTi0tcVh10V7btRUc", .lazy = true, }, .vaxis = .{ // rockorager/libvaxis - .url = "https://deps.files.ghostty.org/vaxis-7dbb9fd3122e4ffad262dd7c151d80d863b68558.tar.gz", - .hash = "vaxis-0.5.1-BWNV_LosCQAGmCCNOLljCIw6j6-yt53tji6n6rwJ2BhS", + // TODO: See https://github.com/rockorager/libvaxis/pull/316 + .url = "https://github.com/rockorager/libvaxis/archive/6dff795b77e930fc9c9a4dc5851faf1d6e703636.tar.gz", + .hash = "vaxis-0.6.0-BWNV_EnQCQArQLgoFAs30nhVr6dIHdso3NNXJ9PKcdTH", .lazy = true, }, .z2d = .{ // vancluever/z2d - .url = "https://deps.files.ghostty.org/z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ.tar.gz", - .hash = "z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ", + .url = "https://github.com/vancluever/z2d/archive/4eab47ca696fb2b62f005125c0aa4ba8d2eba5ee.tar.gz", + .hash = "z2d-0.11.0-j5P_HtLzDwBGyQt49DrT0v4BuVqI_SRs6CXsuj7eBVhR", .lazy = true, }, .zig_objc = .{ // mitchellh/zig-objc - .url = "https://deps.files.ghostty.org/zig_objc-f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz", - .hash = "zig_objc-0.0.0-Ir_Sp5gTAQCvxxR7oVIrPXxXwsfKgVP7_wqoOQrZjFeK", + .url = "https://github.com/mitchellh/zig-objc/archive/c8de82ff80281215ad92900866dab7103a8efa8b.tar.gz", + .hash = "zig_objc-0.0.0-Ir_Sp9gsAQCPAJc0oF5xoWePHWP6Y6tCphDeyNUThJoi", .lazy = true, }, .zig_js = .{ // mitchellh/zig-js - .url = "https://deps.files.ghostty.org/zig_js-04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz", - .hash = "zig_js-0.0.0-rjCAV-6GAADxFug7rDmPH-uM_XcnJ5NmuAMJCAscMjhi", + .url = "https://github.com/mitchellh/zig-js/archive/3c23860e47fdcdc5af805efb7fd0bdac5fd3e9bc.tar.gz", + .hash = "zig_js-0.0.0-rjCAV7-GAADvMTBL7lPMuvDk7xgS9PCMIZWiOUXLZSlj", .lazy = true, }, .uucode = .{ // jacobsandlund/uucode - .url = "https://deps.files.ghostty.org/uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9.tar.gz", - .hash = "uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9", + .url = "git+https://github.com/jacobsandlund/uucode#2826a37a4562284fdacd8fa029d49509cc9bffcd", + .hash = "uucode-0.2.0-ZZjBPlK5VADj7fdoq7G8LIHzD5o6FSkcBXXrRWr4jnrA", }, .zig_wayland = .{ // codeberg ifreund/zig-wayland - .url = "https://deps.files.ghostty.org/zig_wayland-1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz", - .hash = "wayland-0.5.0-dev-lQa1khrMAQDJDwYFKpdH3HizherB7sHo5dKMECfvxQHe", + .url = "https://codeberg.org/ifreund/zig-wayland/archive/4a150a04f76f7329e80280661355c04328369d1f.tar.gz", + .hash = "wayland-0.6.0-dev-lQa1krD8AQBlMqwuhAMJjPQKXvpRByZBxxqMVAZ7yzbG", .lazy = true, }, .zf = .{ // natecraddock/zf - .url = "https://deps.files.ghostty.org/zf-3c52637b7e937c5ae61fd679717da3e276765b23.tar.gz", - .hash = "zf-0.10.3-OIRy8RuJAACKA3Lohoumrt85nRbHwbpMcUaLES8vxDnh", + .url = "https://github.com/natecraddock/zf/archive/91c8283b4804d9dbb4eb10b37e9884fb5593245a.tar.gz", + .hash = "zf-0.11.0-OIRy8X-RAAAwaRXHMYpj2uvBnuGTZWEE_3V7acqHQNtW", .lazy = true, }, .gobject = .{ // https://github.com/ghostty-org/zig-gobject based on zig_gobject // Temporary until we generate them at build time automatically. - .url = "https://deps.files.ghostty.org/gobject-2025-11-08-23-1.tar.zst", - .hash = "gobject-0.3.0-Skun7ANLnwDvEfIpVmohcppXgOvg_I6YOJFmPIsKfXk-", + .url = "https://github.com/ghostty-org/zig-gobject/releases/download/0.8.0-2026-04-23-26-1/ghostty-gobject-0.8.0-2026-04-23-26-1.tar.zst", + .hash = "gobject-0.3.1-Skun7E1KnwBGMX5nslHYG1yWHaSevywxQO8oM7tTOgIp", .lazy = true, }, diff --git a/build.zig.zon.json b/build.zig.zon.json index 462ef7c5b01..bf812f3fb2a 100644 --- a/build.zig.zon.json +++ b/build.zig.zon.json @@ -29,10 +29,10 @@ "url": "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz", "hash": "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U=" }, - "gobject-0.3.0-Skun7ANLnwDvEfIpVmohcppXgOvg_I6YOJFmPIsKfXk-": { + "gobject-0.3.1-Skun7HhMnwBOgnunKXpCmltm6076SYIDU3KGVgNdobJC": { "name": "gobject", - "url": "https://deps.files.ghostty.org/gobject-2025-11-08-23-1.tar.zst", - "hash": "sha256-2b1DBvAIHY5LhItq3+q9L6tJgi7itnnrSAHc7fXWDEg=" + "url": "https://github.com/ghostty-org/zig-gobject/releases/download/0.8.0-2026-04-13-24-1/ghostty-gobject-0.8.0-2026-04-13-24-1.tar.zst", + "hash": "sha256-5sTA+APmlMDiF+LZA7kS0z5naNmlKaD1MQTu1ckqeik=" }, "N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr": { "name": "gtk4_layer_shell", @@ -69,10 +69,10 @@ "url": "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz", "hash": "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo=" }, - "libxev-0.0.0-86vtc4IcEwCqEYxEYoN_3KXmc6A9VLcm22aVImfvecYs": { + "libxev-0.0.0-86vtcwIRFACVrx54GaHsMFFlyC4dTi0tcVh10V7btRUc": { "name": "libxev", - "url": "https://deps.files.ghostty.org/libxev-34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz", - "hash": "sha256-YAPqa5bkpRihKPkyMn15oRvTCZaxO3O66ymRY3lIfdc=" + "url": "https://github.com/mitchellh/libxev/archive/a82a04eabb46b0611c72d6d0e32db2d9dcf2e745.tar.gz", + "hash": "sha256-4k9syvKLeM5gVxz4Ja7/WeoAF37ogHybVGIjvK7xhp4=" }, "N-V-__8AAG3RoQEyRC2Vw7Qoro5SYBf62IHn3HjqtNVY6aWK": { "name": "libxml2", @@ -109,20 +109,20 @@ "url": "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz", "hash": "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M=" }, - "uucode-0.1.0-ZZjBPj96QADXyt5sqwBJUnhaDYs_qBeeKijZvlRa0eqM": { + "uucode-0.2.0-ZZjBPlK5VADj7fdoq7G8LIHzD5o6FSkcBXXrRWr4jnrA": { "name": "uucode", - "url": "git+https://github.com/jacobsandlund/uucode#5f05f8f83a75caea201f12cc8ea32a2d82ea9732", - "hash": "sha256-sHPh+TQSdUGus/QTbj7KSJJkTuNTrK4VNmQDjS30Lf8=" + "url": "git+https://github.com/jacobsandlund/uucode#2826a37a4562284fdacd8fa029d49509cc9bffcd", + "hash": "sha256-R5RXW5tWIaDq5JOF2+oWd5YOYOyns6WH7f687WE+b20=" }, - "uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9": { - "name": "uucode", - "url": "https://deps.files.ghostty.org/uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9.tar.gz", - "hash": "sha256-0KvuD0+L1urjwFF3fhbnxC2JZKqqAVWRxOVlcD9GX5U=" + "vaxis-0.6.0-BWNV_MjFCQCs9UDHiRkrgw_ayeiPkzOe4xVbaAqXkUWW": { + "name": "vaxis", + "url": "git+https://github.com/rockorager/libvaxis.git#c1e1f23be38951c425cdf31af455ba23ef178940", + "hash": "sha256-bIXu8lGwGo42QbItC0jOi/eN7u+f4snknBexw7dc0DI=" }, - "vaxis-0.5.1-BWNV_LosCQAGmCCNOLljCIw6j6-yt53tji6n6rwJ2BhS": { + "vaxis-0.6.0-BWNV_EnQCQArQLgoFAs30nhVr6dIHdso3NNXJ9PKcdTH": { "name": "vaxis", - "url": "https://deps.files.ghostty.org/vaxis-7dbb9fd3122e4ffad262dd7c151d80d863b68558.tar.gz", - "hash": "sha256-LnIzK8icW1Qexua9SHaeHz+3V8QAbz0a+UC1T5sIjvY=" + "url": "https://github.com/rockorager/libvaxis/archive/6dff795b77e930fc9c9a4dc5851faf1d6e703636.tar.gz", + "hash": "sha256-TBjiHmJEVjONuvnBs6JZJT/4qQYEWTziTO6jVpNmEcY=" }, "N-V-__8AAKrHGAAs2shYq8UkE6bGcR1QJtLTyOE_lcosMn6t": { "name": "wayland", @@ -144,35 +144,40 @@ "url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz", "hash": "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM=" }, - "z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ": { + "z2d-0.11.0-j5P_HtLzDwBGyQt49DrT0v4BuVqI_SRs6CXsuj7eBVhR": { "name": "z2d", - "url": "https://deps.files.ghostty.org/z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ.tar.gz", - "hash": "sha256-afIdou/V7gk3/lXE0J5Ir8T7L5GgHvFnyMJ1rgRnl/c=" + "url": "https://github.com/vancluever/z2d/archive/4eab47ca696fb2b62f005125c0aa4ba8d2eba5ee.tar.gz", + "hash": "sha256-9tsSB3tfj1v+pI+D4gJHMv6eyoR7gy0sAH3bmkMZaLo=" }, - "zf-0.10.3-OIRy8RuJAACKA3Lohoumrt85nRbHwbpMcUaLES8vxDnh": { + "zf-0.11.0-OIRy8X-RAAAwaRXHMYpj2uvBnuGTZWEE_3V7acqHQNtW": { "name": "zf", - "url": "https://deps.files.ghostty.org/zf-3c52637b7e937c5ae61fd679717da3e276765b23.tar.gz", - "hash": "sha256-OwFdkorwTp4mJyvBXrTbtNmp1GnrbSkKDdrmc7d8RWg=" + "url": "https://github.com/natecraddock/zf/archive/91c8283b4804d9dbb4eb10b37e9884fb5593245a.tar.gz", + "hash": "sha256-zgEwcBWJ5hsFpofkNb0Awid1KuP7x1hWdxuxwseLkPA=" + }, + "zig-0.0.0-Fp4XJIbnIg33b41dbDFzgPZfQEbWITdlrryg8kEqYh5x": { + "name": "zig_autodoc", + "url": "git+https://github.com/ianprime0509/zig?ref=zig-gobject#f2696e399062de9f9ffba0ee1eac5411bcca90e8", + "hash": "sha256-BqmBh1T/16YTRA5qZRlGv4aG2DSpEYwmrCjLujlOmVk=" }, - "zig_js-0.0.0-rjCAV-6GAADxFug7rDmPH-uM_XcnJ5NmuAMJCAscMjhi": { + "zig_js-0.0.0-rjCAV7-GAADvMTBL7lPMuvDk7xgS9PCMIZWiOUXLZSlj": { "name": "zig_js", - "url": "https://deps.files.ghostty.org/zig_js-04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz", - "hash": "sha256-TCAY5WAV05UEuAkDhq2c6Tk/ODgAhdnDI3O/flb8c6M=" + "url": "https://github.com/mitchellh/zig-js/archive/3c23860e47fdcdc5af805efb7fd0bdac5fd3e9bc.tar.gz", + "hash": "sha256-ov9IiiRDAfTllGmlIyqtaGmwGsxQ99ftJkfAKaV9Szs=" }, - "zig_objc-0.0.0-Ir_Sp5gTAQCvxxR7oVIrPXxXwsfKgVP7_wqoOQrZjFeK": { + "zig_objc-0.0.0-Ir_Sp9gsAQCPAJc0oF5xoWePHWP6Y6tCphDeyNUThJoi": { "name": "zig_objc", - "url": "https://deps.files.ghostty.org/zig_objc-f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz", - "hash": "sha256-3YSvc3YlNW/NciyzCQnzsujXAmZ89XlxSqfqvArAjsw=" + "url": "https://github.com/mitchellh/zig-objc/archive/c8de82ff80281215ad92900866dab7103a8efa8b.tar.gz", + "hash": "sha256-gx6uxVS7I+8gIp8ecF2PU+iinIKYQLuND17bBcdtbfA=" }, - "wayland-0.5.0-dev-lQa1khrMAQDJDwYFKpdH3HizherB7sHo5dKMECfvxQHe": { + "wayland-0.6.0-dev-lQa1krD8AQBlMqwuhAMJjPQKXvpRByZBxxqMVAZ7yzbG": { "name": "zig_wayland", - "url": "https://deps.files.ghostty.org/zig_wayland-1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz", - "hash": "sha256-TxRrc17Q1Sf1IOO/cdPpP3LD0PpYOujt06SFH3B5Ek4=" + "url": "https://codeberg.org/ifreund/zig-wayland/archive/4a150a04f76f7329e80280661355c04328369d1f.tar.gz", + "hash": "sha256-VBkZPsaDNEd2EgoyIz/XMhFs9BZWha+JYooWRMSA0ag=" }, - "zigimg-0.1.0-8_eo2vHnEwCIVW34Q14Ec-xUlzIoVg86-7FU2ypPtxms": { + "zigimg-0.1.0-8_eo2oyaFwBZwJpmqPkCfVXWBrHcqbYwmrp1I6bTD3lI": { "name": "zigimg", - "url": "https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz", - "hash": "sha256-LB7Xa6KzVRRUSwwnyWM+y6fDG+kIDjfnoBDJO1obxVM=" + "url": "git+https://github.com/zigimg/zigimg#d695acd97c02e57bb151e8f659d1280f5cd6ca70", + "hash": "sha256-0IYATQldT6eJxRR2T/2CsIYZuzomqjvmdVyjmsjguyE=" }, "N-V-__8AAB0eQwD-0MdOEBmz7intriBReIsIDNlukNVoNu6o": { "name": "zlib", diff --git a/build.zig.zon.nix b/build.zig.zon.nix index e88f08238f1..0147417d121 100644 --- a/build.zig.zon.nix +++ b/build.zig.zon.nix @@ -131,11 +131,11 @@ in }; } { - name = "gobject-0.3.0-Skun7ANLnwDvEfIpVmohcppXgOvg_I6YOJFmPIsKfXk-"; + name = "gobject-0.3.1-Skun7HhMnwBOgnunKXpCmltm6076SYIDU3KGVgNdobJC"; path = fetchZigArtifact { name = "gobject"; - url = "https://deps.files.ghostty.org/gobject-2025-11-08-23-1.tar.zst"; - hash = "sha256-2b1DBvAIHY5LhItq3+q9L6tJgi7itnnrSAHc7fXWDEg="; + url = "https://github.com/ghostty-org/zig-gobject/releases/download/0.8.0-2026-04-13-24-1/ghostty-gobject-0.8.0-2026-04-13-24-1.tar.zst"; + hash = "sha256-5sTA+APmlMDiF+LZA7kS0z5naNmlKaD1MQTu1ckqeik="; }; } { @@ -195,11 +195,11 @@ in }; } { - name = "libxev-0.0.0-86vtc4IcEwCqEYxEYoN_3KXmc6A9VLcm22aVImfvecYs"; + name = "libxev-0.0.0-86vtcwIRFACVrx54GaHsMFFlyC4dTi0tcVh10V7btRUc"; path = fetchZigArtifact { name = "libxev"; - url = "https://deps.files.ghostty.org/libxev-34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz"; - hash = "sha256-YAPqa5bkpRihKPkyMn15oRvTCZaxO3O66ymRY3lIfdc="; + url = "https://github.com/mitchellh/libxev/archive/a82a04eabb46b0611c72d6d0e32db2d9dcf2e745.tar.gz"; + hash = "sha256-4k9syvKLeM5gVxz4Ja7/WeoAF37ogHybVGIjvK7xhp4="; }; } { @@ -259,27 +259,27 @@ in }; } { - name = "uucode-0.1.0-ZZjBPj96QADXyt5sqwBJUnhaDYs_qBeeKijZvlRa0eqM"; + name = "uucode-0.2.0-ZZjBPlK5VADj7fdoq7G8LIHzD5o6FSkcBXXrRWr4jnrA"; path = fetchZigArtifact { name = "uucode"; - url = "git+https://github.com/jacobsandlund/uucode#5f05f8f83a75caea201f12cc8ea32a2d82ea9732"; - hash = "sha256-sHPh+TQSdUGus/QTbj7KSJJkTuNTrK4VNmQDjS30Lf8="; + url = "git+https://github.com/jacobsandlund/uucode#2826a37a4562284fdacd8fa029d49509cc9bffcd"; + hash = "sha256-R5RXW5tWIaDq5JOF2+oWd5YOYOyns6WH7f687WE+b20="; }; } { - name = "uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9"; + name = "vaxis-0.6.0-BWNV_MjFCQCs9UDHiRkrgw_ayeiPkzOe4xVbaAqXkUWW"; path = fetchZigArtifact { - name = "uucode"; - url = "https://deps.files.ghostty.org/uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9.tar.gz"; - hash = "sha256-0KvuD0+L1urjwFF3fhbnxC2JZKqqAVWRxOVlcD9GX5U="; + name = "vaxis"; + url = "git+https://github.com/rockorager/libvaxis.git#c1e1f23be38951c425cdf31af455ba23ef178940"; + hash = "sha256-bIXu8lGwGo42QbItC0jOi/eN7u+f4snknBexw7dc0DI="; }; } { - name = "vaxis-0.5.1-BWNV_LosCQAGmCCNOLljCIw6j6-yt53tji6n6rwJ2BhS"; + name = "vaxis-0.6.0-BWNV_EnQCQArQLgoFAs30nhVr6dIHdso3NNXJ9PKcdTH"; path = fetchZigArtifact { name = "vaxis"; - url = "https://deps.files.ghostty.org/vaxis-7dbb9fd3122e4ffad262dd7c151d80d863b68558.tar.gz"; - hash = "sha256-LnIzK8icW1Qexua9SHaeHz+3V8QAbz0a+UC1T5sIjvY="; + url = "https://github.com/rockorager/libvaxis/archive/6dff795b77e930fc9c9a4dc5851faf1d6e703636.tar.gz"; + hash = "sha256-TBjiHmJEVjONuvnBs6JZJT/4qQYEWTziTO6jVpNmEcY="; }; } { @@ -315,51 +315,59 @@ in }; } { - name = "z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ"; + name = "z2d-0.11.0-j5P_HtLzDwBGyQt49DrT0v4BuVqI_SRs6CXsuj7eBVhR"; path = fetchZigArtifact { name = "z2d"; - url = "https://deps.files.ghostty.org/z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ.tar.gz"; - hash = "sha256-afIdou/V7gk3/lXE0J5Ir8T7L5GgHvFnyMJ1rgRnl/c="; + url = "https://github.com/vancluever/z2d/archive/4eab47ca696fb2b62f005125c0aa4ba8d2eba5ee.tar.gz"; + hash = "sha256-9tsSB3tfj1v+pI+D4gJHMv6eyoR7gy0sAH3bmkMZaLo="; }; } { - name = "zf-0.10.3-OIRy8RuJAACKA3Lohoumrt85nRbHwbpMcUaLES8vxDnh"; + name = "zf-0.11.0-OIRy8X-RAAAwaRXHMYpj2uvBnuGTZWEE_3V7acqHQNtW"; path = fetchZigArtifact { name = "zf"; - url = "https://deps.files.ghostty.org/zf-3c52637b7e937c5ae61fd679717da3e276765b23.tar.gz"; - hash = "sha256-OwFdkorwTp4mJyvBXrTbtNmp1GnrbSkKDdrmc7d8RWg="; + url = "https://github.com/natecraddock/zf/archive/91c8283b4804d9dbb4eb10b37e9884fb5593245a.tar.gz"; + hash = "sha256-zgEwcBWJ5hsFpofkNb0Awid1KuP7x1hWdxuxwseLkPA="; + }; + } + { + name = "zig-0.0.0-Fp4XJIbnIg33b41dbDFzgPZfQEbWITdlrryg8kEqYh5x"; + path = fetchZigArtifact { + name = "zig_autodoc"; + url = "git+https://github.com/ianprime0509/zig?ref=zig-gobject#f2696e399062de9f9ffba0ee1eac5411bcca90e8"; + hash = "sha256-BqmBh1T/16YTRA5qZRlGv4aG2DSpEYwmrCjLujlOmVk="; }; } { - name = "zig_js-0.0.0-rjCAV-6GAADxFug7rDmPH-uM_XcnJ5NmuAMJCAscMjhi"; + name = "zig_js-0.0.0-rjCAV7-GAADvMTBL7lPMuvDk7xgS9PCMIZWiOUXLZSlj"; path = fetchZigArtifact { name = "zig_js"; - url = "https://deps.files.ghostty.org/zig_js-04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz"; - hash = "sha256-TCAY5WAV05UEuAkDhq2c6Tk/ODgAhdnDI3O/flb8c6M="; + url = "https://github.com/mitchellh/zig-js/archive/3c23860e47fdcdc5af805efb7fd0bdac5fd3e9bc.tar.gz"; + hash = "sha256-ov9IiiRDAfTllGmlIyqtaGmwGsxQ99ftJkfAKaV9Szs="; }; } { - name = "zig_objc-0.0.0-Ir_Sp5gTAQCvxxR7oVIrPXxXwsfKgVP7_wqoOQrZjFeK"; + name = "zig_objc-0.0.0-Ir_Sp9gsAQCPAJc0oF5xoWePHWP6Y6tCphDeyNUThJoi"; path = fetchZigArtifact { name = "zig_objc"; - url = "https://deps.files.ghostty.org/zig_objc-f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz"; - hash = "sha256-3YSvc3YlNW/NciyzCQnzsujXAmZ89XlxSqfqvArAjsw="; + url = "https://github.com/mitchellh/zig-objc/archive/c8de82ff80281215ad92900866dab7103a8efa8b.tar.gz"; + hash = "sha256-gx6uxVS7I+8gIp8ecF2PU+iinIKYQLuND17bBcdtbfA="; }; } { - name = "wayland-0.5.0-dev-lQa1khrMAQDJDwYFKpdH3HizherB7sHo5dKMECfvxQHe"; + name = "wayland-0.6.0-dev-lQa1krD8AQBlMqwuhAMJjPQKXvpRByZBxxqMVAZ7yzbG"; path = fetchZigArtifact { name = "zig_wayland"; - url = "https://deps.files.ghostty.org/zig_wayland-1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz"; - hash = "sha256-TxRrc17Q1Sf1IOO/cdPpP3LD0PpYOujt06SFH3B5Ek4="; + url = "https://codeberg.org/ifreund/zig-wayland/archive/4a150a04f76f7329e80280661355c04328369d1f.tar.gz"; + hash = "sha256-VBkZPsaDNEd2EgoyIz/XMhFs9BZWha+JYooWRMSA0ag="; }; } { - name = "zigimg-0.1.0-8_eo2vHnEwCIVW34Q14Ec-xUlzIoVg86-7FU2ypPtxms"; + name = "zigimg-0.1.0-8_eo2oyaFwBZwJpmqPkCfVXWBrHcqbYwmrp1I6bTD3lI"; path = fetchZigArtifact { name = "zigimg"; - url = "https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz"; - hash = "sha256-LB7Xa6KzVRRUSwwnyWM+y6fDG+kIDjfnoBDJO1obxVM="; + url = "git+https://github.com/zigimg/zigimg#d695acd97c02e57bb151e8f659d1280f5cd6ca70"; + hash = "sha256-0IYATQldT6eJxRR2T/2CsIYZuzomqjvmdVyjmsjguyE="; }; } { diff --git a/build.zig.zon.txt b/build.zig.zon.txt index 5bd0ded1186..ab12ac3eb14 100644 --- a/build.zig.zon.txt +++ b/build.zig.zon.txt @@ -1,4 +1,8 @@ -git+https://github.com/jacobsandlund/uucode#5f05f8f83a75caea201f12cc8ea32a2d82ea9732 +git+https://github.com/ianprime0509/zig?ref=zig-gobject#f2696e399062de9f9ffba0ee1eac5411bcca90e8 +git+https://github.com/jacobsandlund/uucode#2826a37a4562284fdacd8fa029d49509cc9bffcd +git+https://github.com/rockorager/libvaxis.git#c1e1f23be38951c425cdf31af455ba23ef178940 +git+https://github.com/zigimg/zigimg#d695acd97c02e57bb151e8f659d1280f5cd6ca70 +https://codeberg.org/ifreund/zig-wayland/archive/4a150a04f76f7329e80280661355c04328369d1f.tar.gz https://deps.files.ghostty.org/DearBindings_v0.17_ImGui_v1.92.5-docking.tar.gz https://deps.files.ghostty.org/JetBrainsMono-2.304.tar.gz https://deps.files.ghostty.org/NerdFontsSymbolsOnly-3.4.0.tar.gz @@ -8,29 +12,26 @@ https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab0 https://deps.files.ghostty.org/gettext-0.24.tar.gz https://deps.files.ghostty.org/ghostty-themes-release-20260323-152405-a2c7b60.tgz https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz -https://deps.files.ghostty.org/gobject-2025-11-08-23-1.tar.zst https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz https://deps.files.ghostty.org/harfbuzz-11.0.0.tar.xz https://deps.files.ghostty.org/highway-66486a10623fa0d72fe91260f96c892e41aceb06.tar.gz https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz -https://deps.files.ghostty.org/libxev-34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz https://deps.files.ghostty.org/libxml2-2.11.5.tar.gz https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz -https://deps.files.ghostty.org/uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9.tar.gz -https://deps.files.ghostty.org/vaxis-7dbb9fd3122e4ffad262dd7c151d80d863b68558.tar.gz https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz -https://deps.files.ghostty.org/z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ.tar.gz -https://deps.files.ghostty.org/zf-3c52637b7e937c5ae61fd679717da3e276765b23.tar.gz -https://deps.files.ghostty.org/zig_js-04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz -https://deps.files.ghostty.org/zig_objc-f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz -https://deps.files.ghostty.org/zig_wayland-1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz -https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz +https://github.com/ghostty-org/zig-gobject/releases/download/0.8.0-2026-04-13-24-1/ghostty-gobject-0.8.0-2026-04-13-24-1.tar.zst +https://github.com/mitchellh/libxev/archive/a82a04eabb46b0611c72d6d0e32db2d9dcf2e745.tar.gz +https://github.com/mitchellh/zig-js/archive/3c23860e47fdcdc5af805efb7fd0bdac5fd3e9bc.tar.gz +https://github.com/mitchellh/zig-objc/archive/c8de82ff80281215ad92900866dab7103a8efa8b.tar.gz +https://github.com/natecraddock/zf/archive/91c8283b4804d9dbb4eb10b37e9884fb5593245a.tar.gz https://github.com/ocornut/imgui/archive/refs/tags/v1.92.5-docking.tar.gz +https://github.com/rockorager/libvaxis/archive/6dff795b77e930fc9c9a4dc5851faf1d6e703636.tar.gz +https://github.com/vancluever/z2d/archive/4eab47ca696fb2b62f005125c0aa4ba8d2eba5ee.tar.gz https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz diff --git a/example/zig-formatter/src/main.zig b/example/zig-formatter/src/main.zig index df21a20468f..952697b67da 100644 --- a/example/zig-formatter/src/main.zig +++ b/example/zig-formatter/src/main.zig @@ -1,28 +1,28 @@ const std = @import("std"); const ghostty_vt = @import("ghostty-vt"); -pub fn main() !void { - var gpa: std.heap.DebugAllocator(.{}) = .init; - defer _ = gpa.deinit(); - const alloc = gpa.allocator(); - +pub fn main(init: std.process.Init) !void { // Create a terminal - var t: ghostty_vt.Terminal = try .init(alloc, .{ .cols = 150, .rows = 80 }); - defer t.deinit(alloc); + var t: ghostty_vt.Terminal = try .init(init.gpa, .{ .cols = 150, .rows = 80 }); + defer t.deinit(init.gpa); // Create a read-only VT stream for parsing terminal sequences var stream = t.vtStream(); defer stream.deinit(); // Read from stdin - const stdin = std.fs.File.stdin(); + var stdin = std.Io.File.stdin(); var buf: [4096]u8 = undefined; + var reader = stdin.reader(init.io, &buf); + while (true) { - const n = try stdin.readAll(&buf); - if (n == 0) break; + reader.interface.fillMore() catch |err| switch (err) { + error.EndOfStream => break, + error.ReadFailed => return reader.err, + }; // Replace \n with \r\n - for (buf[0..n]) |byte| { + for (reader.interface.buffered()) |byte| { if (byte == '\n') stream.next('\r'); stream.next(byte); } @@ -35,7 +35,7 @@ pub fn main() !void { }); // Write to stdout - var stdout_writer = std.fs.File.stdout().writer(&buf); + var stdout_writer = std.Io.File.stdout().writer(init.io, &buf); const stdout = &stdout_writer.interface; try stdout.print("{f}", .{formatter}); try stdout.flush(); diff --git a/example/zig-vt-stream/src/main.zig b/example/zig-vt-stream/src/main.zig index 87d8857ddb1..b87aa3d5750 100644 --- a/example/zig-vt-stream/src/main.zig +++ b/example/zig-vt-stream/src/main.zig @@ -1,13 +1,9 @@ const std = @import("std"); const ghostty_vt = @import("ghostty-vt"); -pub fn main() !void { - var gpa: std.heap.DebugAllocator(.{}) = .init; - defer _ = gpa.deinit(); - const alloc = gpa.allocator(); - - var t: ghostty_vt.Terminal = try .init(alloc, .{ .cols = 80, .rows = 24 }); - defer t.deinit(alloc); +pub fn main(init: std.process.Init) !void { + var t: ghostty_vt.Terminal = try .init(init.gpa, .{ .cols = 80, .rows = 24 }); + defer t.deinit(init.gpa); // Create a read-only VT stream for parsing terminal sequences var stream = t.vtStream(); diff --git a/example/zig-vt/src/main.zig b/example/zig-vt/src/main.zig index f57c7008748..aebc9edb800 100644 --- a/example/zig-vt/src/main.zig +++ b/example/zig-vt/src/main.zig @@ -1,19 +1,13 @@ const std = @import("std"); const ghostty_vt = @import("ghostty-vt"); -pub fn main() !void { - // Use a debug allocator so we get leak checking. You probably want - // to replace this for release builds. - var gpa: std.heap.DebugAllocator(.{}) = .init; - defer _ = gpa.deinit(); - const alloc = gpa.allocator(); - +pub fn main(init: std.process.Init) !void { // Initialize a terminal. - var t: ghostty_vt.Terminal = try .init(alloc, .{ + var t: ghostty_vt.Terminal = try .init(init.gpa, .{ .cols = 6, .rows = 40, }); - defer t.deinit(alloc); + defer t.deinit(init.gpa); // Write some text. It'll wrap because this is too long for our // columns size above (6). diff --git a/flake.nix b/flake.nix index 0a4e5340ae7..7bf76bce8aa 100644 --- a/flake.nix +++ b/flake.nix @@ -75,10 +75,7 @@ in { devShells = forAllPlatforms (pkgs: { default = pkgs.callPackage ./nix/devShell.nix { - zig = - if pkgs.stdenv.hostPlatform.isDarwin - then zig.packages.${pkgs.stdenv.hostPlatform.system}.brew."0.15.2" - else zig.packages.${pkgs.stdenv.hostPlatform.system}."0.15.2"; + zig = zig.packages.${pkgs.stdenv.hostPlatform.system}."0.16.0"; wraptest = pkgs.callPackage ./nix/pkgs/wraptest.nix {}; zon2nix = zon2nix; diff --git a/flatpak/zig-packages.json b/flatpak/zig-packages.json index 23411e69556..71c2d1a025e 100644 --- a/flatpak/zig-packages.json +++ b/flatpak/zig-packages.json @@ -37,9 +37,9 @@ }, { "type": "archive", - "url": "https://deps.files.ghostty.org/gobject-2025-11-08-23-1.tar.zst", - "dest": "vendor/p/gobject-0.3.0-Skun7ANLnwDvEfIpVmohcppXgOvg_I6YOJFmPIsKfXk-", - "sha256": "d9bd4306f0081d8e4b848b6adfeabd2fab49822ee2b679eb4801dcedf5d60c48" + "url": "https://github.com/ghostty-org/zig-gobject/releases/download/0.8.0-2026-04-13-24-1/ghostty-gobject-0.8.0-2026-04-13-24-1.tar.zst", + "dest": "vendor/p/gobject-0.3.1-Skun7HhMnwBOgnunKXpCmltm6076SYIDU3KGVgNdobJC", + "sha256": "e6c4c0f803e694c0e217e2d903b912d33e6768d9a529a0f53104eed5c92a7a29" }, { "type": "archive", @@ -85,9 +85,9 @@ }, { "type": "archive", - "url": "https://deps.files.ghostty.org/libxev-34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz", - "dest": "vendor/p/libxev-0.0.0-86vtc4IcEwCqEYxEYoN_3KXmc6A9VLcm22aVImfvecYs", - "sha256": "6003ea6b96e4a518a128f932327d79a11bd30996b13b73baeb29916379487dd7" + "url": "https://github.com/mitchellh/libxev/archive/a82a04eabb46b0611c72d6d0e32db2d9dcf2e745.tar.gz", + "dest": "vendor/p/libxev-0.0.0-86vtcwIRFACVrx54GaHsMFFlyC4dTi0tcVh10V7btRUc", + "sha256": "e24f6ccaf28b78ce60571cf825aeff59ea00177ee8807c9b546223bcaef1869e" }, { "type": "archive", @@ -134,20 +134,20 @@ { "type": "git", "url": "https://github.com/jacobsandlund/uucode", - "commit": "5f05f8f83a75caea201f12cc8ea32a2d82ea9732", - "dest": "vendor/p/uucode-0.1.0-ZZjBPj96QADXyt5sqwBJUnhaDYs_qBeeKijZvlRa0eqM" + "commit": "2826a37a4562284fdacd8fa029d49509cc9bffcd", + "dest": "vendor/p/uucode-0.2.0-ZZjBPlK5VADj7fdoq7G8LIHzD5o6FSkcBXXrRWr4jnrA" }, { - "type": "archive", - "url": "https://deps.files.ghostty.org/uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9.tar.gz", - "dest": "vendor/p/uucode-0.2.0-ZZjBPqZVVABQepOqZHR7vV_NcaN-wats0IB6o-Exj6m9", - "sha256": "d0abee0f4f8bd6eae3c051777e16e7c42d8964aaaa015591c4e565703f465f95" + "type": "git", + "url": "https://github.com/rockorager/libvaxis.git", + "commit": "c1e1f23be38951c425cdf31af455ba23ef178940", + "dest": "vendor/p/vaxis-0.6.0-BWNV_MjFCQCs9UDHiRkrgw_ayeiPkzOe4xVbaAqXkUWW" }, { "type": "archive", - "url": "https://deps.files.ghostty.org/vaxis-7dbb9fd3122e4ffad262dd7c151d80d863b68558.tar.gz", - "dest": "vendor/p/vaxis-0.5.1-BWNV_LosCQAGmCCNOLljCIw6j6-yt53tji6n6rwJ2BhS", - "sha256": "2e72332bc89c5b541ec6e6bd48769e1f3fb757c4006f3d1af940b54f9b088ef6" + "url": "https://github.com/rockorager/libvaxis/archive/6dff795b77e930fc9c9a4dc5851faf1d6e703636.tar.gz", + "dest": "vendor/p/vaxis-0.6.0-BWNV_EnQCQArQLgoFAs30nhVr6dIHdso3NNXJ9PKcdTH", + "sha256": "4c18e21e624456338dbaf9c1b3a259253ff8a90604593ce24ceea356936611c6" }, { "type": "archive", @@ -175,39 +175,45 @@ }, { "type": "archive", - "url": "https://deps.files.ghostty.org/z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ.tar.gz", - "dest": "vendor/p/z2d-0.10.0-j5P_Hu-6FgBsZNgwphIqh17jDnj8_yPtD8yzjO6PpHRQ", - "sha256": "69f21da2efd5ee0937fe55c4d09e48afc4fb2f91a01ef167c8c275ae046797f7" + "url": "https://github.com/vancluever/z2d/archive/4eab47ca696fb2b62f005125c0aa4ba8d2eba5ee.tar.gz", + "dest": "vendor/p/z2d-0.11.0-j5P_HtLzDwBGyQt49DrT0v4BuVqI_SRs6CXsuj7eBVhR", + "sha256": "f6db12077b5f8f5bfea48f83e2024732fe9eca847b832d2c007ddb9a431968ba" }, { "type": "archive", - "url": "https://deps.files.ghostty.org/zf-3c52637b7e937c5ae61fd679717da3e276765b23.tar.gz", - "dest": "vendor/p/zf-0.10.3-OIRy8RuJAACKA3Lohoumrt85nRbHwbpMcUaLES8vxDnh", - "sha256": "3b015d928af04e9e26272bc15eb4dbb4d9a9d469eb6d290a0ddae673b77c4568" + "url": "https://github.com/natecraddock/zf/archive/91c8283b4804d9dbb4eb10b37e9884fb5593245a.tar.gz", + "dest": "vendor/p/zf-0.11.0-OIRy8X-RAAAwaRXHMYpj2uvBnuGTZWEE_3V7acqHQNtW", + "sha256": "ce0130701589e61b05a687e435bd00c227752ae3fbc75856771bb1c2c78b90f0" }, { - "type": "archive", - "url": "https://deps.files.ghostty.org/zig_js-04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz", - "dest": "vendor/p/zig_js-0.0.0-rjCAV-6GAADxFug7rDmPH-uM_XcnJ5NmuAMJCAscMjhi", - "sha256": "4c2018e56015d39504b8090386ad9ce9393f38380085d9c32373bf7e56fc73a3" + "type": "git", + "url": "https://github.com/ianprime0509/zig", + "commit": "f2696e399062de9f9ffba0ee1eac5411bcca90e8", + "dest": "vendor/p/zig-0.0.0-Fp4XJIbnIg33b41dbDFzgPZfQEbWITdlrryg8kEqYh5x" }, { "type": "archive", - "url": "https://deps.files.ghostty.org/zig_objc-f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz", - "dest": "vendor/p/zig_objc-0.0.0-Ir_Sp5gTAQCvxxR7oVIrPXxXwsfKgVP7_wqoOQrZjFeK", - "sha256": "dd84af737625356fcd722cb30909f3b2e8d702667cf579714aa7eabc0ac08ecc" + "url": "https://github.com/mitchellh/zig-js/archive/3c23860e47fdcdc5af805efb7fd0bdac5fd3e9bc.tar.gz", + "dest": "vendor/p/zig_js-0.0.0-rjCAV7-GAADvMTBL7lPMuvDk7xgS9PCMIZWiOUXLZSlj", + "sha256": "a2ff488a244301f4e59469a5232aad6869b01acc50f7d7ed2647c029a57d4b3b" }, { "type": "archive", - "url": "https://deps.files.ghostty.org/zig_wayland-1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz", - "dest": "vendor/p/wayland-0.5.0-dev-lQa1khrMAQDJDwYFKpdH3HizherB7sHo5dKMECfvxQHe", - "sha256": "4f146b735ed0d527f520e3bf71d3e93f72c3d0fa583ae8edd3a4851f7079124e" + "url": "https://github.com/mitchellh/zig-objc/archive/c8de82ff80281215ad92900866dab7103a8efa8b.tar.gz", + "dest": "vendor/p/zig_objc-0.0.0-Ir_Sp9gsAQCPAJc0oF5xoWePHWP6Y6tCphDeyNUThJoi", + "sha256": "831eaec554bb23ef20229f1e705d8f53e8a29c829840bb8d0f5edb05c76d6df0" }, { "type": "archive", - "url": "https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz", - "dest": "vendor/p/zigimg-0.1.0-8_eo2vHnEwCIVW34Q14Ec-xUlzIoVg86-7FU2ypPtxms", - "sha256": "2c1ed76ba2b35514544b0c27c9633ecba7c31be9080e37e7a010c93b5a1bc553" + "url": "https://codeberg.org/ifreund/zig-wayland/archive/4a150a04f76f7329e80280661355c04328369d1f.tar.gz", + "dest": "vendor/p/wayland-0.6.0-dev-lQa1krD8AQBlMqwuhAMJjPQKXvpRByZBxxqMVAZ7yzbG", + "sha256": "5419193ec683344776120a32233fd732116cf4165685af89628a1644c480d1a8" + }, + { + "type": "git", + "url": "https://github.com/zigimg/zigimg", + "commit": "d695acd97c02e57bb151e8f659d1280f5cd6ca70", + "dest": "vendor/p/zigimg-0.1.0-8_eo2oyaFwBZwJpmqPkCfVXWBrHcqbYwmrp1I6bTD3lI" }, { "type": "archive", diff --git a/pkg/android-ndk/build.zig b/pkg/android-ndk/build.zig index 5b005665bdc..9a5fb8ceaca 100644 --- a/pkg/android-ndk/build.zig +++ b/pkg/android-ndk/build.zig @@ -117,17 +117,18 @@ pub fn addPaths(b: *std.Build, step: *std.Build.Step.Compile) !void { } fn findNDKPath(b: *std.Build) ?[]const u8 { + const map = b.graph.environ_map; // Check if user has set the environment variable for the NDK path. - if (std.process.getEnvVarOwned(b.allocator, "ANDROID_NDK_HOME") catch null) |value| { + if (map.get("ANDROID_NDK_HOME")) |value| { if (value.len == 0) return null; - var dir = std.fs.openDirAbsolute(value, .{}) catch return null; - defer dir.close(); + var dir = std.Io.Dir.openDirAbsolute(b.graph.io, value, .{}) catch return null; + defer dir.close(b.graph.io); return value; } // Check the common environment variables for the Android SDK path and look for the NDK inside it. inline for (.{ "ANDROID_HOME", "ANDROID_SDK_ROOT" }) |env| { - if (std.process.getEnvVarOwned(b.allocator, env) catch null) |sdk| { + if (map.get(env)) |sdk| { if (sdk.len > 0) { if (findLatestNDK(b, sdk)) |ndk| return ndk; } @@ -135,10 +136,9 @@ fn findNDKPath(b: *std.Build) ?[]const u8 { } // As a fallback, we assume the most common/default SDK path based on the OS. - const home = std.process.getEnvVarOwned( - b.allocator, + const home = map.get( if (builtin.os.tag == .windows) "LOCALAPPDATA" else "HOME", - ) catch return null; + ) orelse return null; const default_sdk_path = b.pathJoin( &.{ @@ -157,8 +157,8 @@ fn findNDKPath(b: *std.Build) ?[]const u8 { fn findLatestNDK(b: *std.Build, sdk_path: []const u8) ?[]const u8 { const ndk_dir = b.pathJoin(&.{ sdk_path, "ndk" }); - var dir = std.fs.openDirAbsolute(ndk_dir, .{ .iterate = true }) catch return null; - defer dir.close(); + var dir = std.Io.Dir.openDirAbsolute(b.graph.io, ndk_dir, .{ .iterate = true }) catch return null; + defer dir.close(b.graph.io); var latest_: ?struct { name: []const u8, @@ -166,7 +166,7 @@ fn findLatestNDK(b: *std.Build, sdk_path: []const u8) ?[]const u8 { } = null; var iterator = dir.iterate(); - while (iterator.next() catch null) |file| { + while (iterator.next(b.graph.io) catch null) |file| { if (file.kind != .directory) continue; const version = std.SemanticVersion.parse(file.name) catch continue; if (latest_) |latest| { diff --git a/pkg/apple-sdk/build.zig b/pkg/apple-sdk/build.zig index c573c391087..354ce757449 100644 --- a/pkg/apple-sdk/build.zig +++ b/pkg/apple-sdk/build.zig @@ -44,14 +44,14 @@ pub fn addPaths( // Detect our SDK using the "findNative" Zig stdlib function. // This is really important because it forces using `xcrun` to // find the SDK path. - const libc = try std.zig.LibCInstallation.findNative(.{ - .allocator = b.allocator, + const libc = try std.zig.LibCInstallation.findNative(b.allocator, b.graph.io, .{ .target = &step.rootModuleTarget(), + .environ_map = &b.graph.environ_map, .verbose = false, }); // Render the file compatible with the `--libc` Zig flag. - var stream: std.io.Writer.Allocating = .init(b.allocator); + var stream: std.Io.Writer.Allocating = .init(b.allocator); defer stream.deinit(); try libc.render(&stream.writer); @@ -64,9 +64,9 @@ pub fn addPaths( // parse this from the libc txt file for `-framework` flags: // https://github.com/ziglang/zig/issues/24024 const framework_path = framework: { - const down1 = std.fs.path.dirname(libc.sys_include_dir.?).?; - const down2 = std.fs.path.dirname(down1).?; - break :framework try std.fs.path.join(b.allocator, &.{ + const down1 = std.Io.Dir.path.dirname(libc.sys_include_dir.?).?; + const down2 = std.Io.Dir.path.dirname(down1).?; + break :framework try std.Io.Dir.path.join(b.allocator, &.{ down2, "System", "Library", @@ -75,8 +75,8 @@ pub fn addPaths( }; const library_path = library: { - const down1 = std.fs.path.dirname(libc.sys_include_dir.?).?; - break :library try std.fs.path.join(b.allocator, &.{ + const down1 = std.Io.Dir.path.dirname(libc.sys_include_dir.?).?; + break :library try std.Io.Dir.path.join(b.allocator, &.{ down1, "lib", }); diff --git a/pkg/breakpad/build.zig b/pkg/breakpad/build.zig index 56d51b159d5..94ca6994a2d 100644 --- a/pkg/breakpad/build.zig +++ b/pkg/breakpad/build.zig @@ -12,8 +12,8 @@ pub fn build(b: *std.Build) !void { }), .linkage = .static, }); - lib.linkLibCpp(); - lib.addIncludePath(b.path("vendor")); + lib.root_module.link_libcpp = true; + lib.root_module.addIncludePath(b.path("vendor")); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); try apple_sdk.addPaths(b, lib); @@ -23,20 +23,20 @@ pub fn build(b: *std.Build) !void { defer flags.deinit(b.allocator); if (b.lazyDependency("breakpad", .{})) |upstream| { - lib.addIncludePath(upstream.path("src")); - lib.addCSourceFiles(.{ + lib.root_module.addIncludePath(upstream.path("src")); + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = common, .flags = flags.items, }); if (target.result.os.tag.isDarwin()) { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = common_apple, .flags = flags.items, }); - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = client_apple, .flags = flags.items, @@ -44,19 +44,19 @@ pub fn build(b: *std.Build) !void { switch (target.result.os.tag) { .macos => { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = common_mac, .flags = flags.items, }); - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = client_mac, .flags = flags.items, }); }, - .ios => lib.addCSourceFiles(.{ + .ios => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = client_ios, .flags = flags.items, @@ -67,12 +67,12 @@ pub fn build(b: *std.Build) !void { } if (target.result.os.tag == .linux) { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = common_linux, .flags = flags.items, }); - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = client_linux, .flags = flags.items, diff --git a/pkg/dcimgui/build.zig b/pkg/dcimgui/build.zig index 01a5879d6ae..17d6f0bd9aa 100644 --- a/pkg/dcimgui/build.zig +++ b/pkg/dcimgui/build.zig @@ -22,17 +22,17 @@ pub fn build(b: *std.Build) !void { .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); // On MSVC, we must not use linkLibCpp because Zig unconditionally // passes -nostdinc++ and then adds its bundled libc++/libc++abi // include paths, which conflict with MSVC's own C++ runtime headers. // The MSVC SDK include directories (added via linkLibC) contain // both C and C++ headers, so linkLibCpp is not needed. if (target.result.abi != .msvc) { - lib.linkLibCpp(); + lib.root_module.link_libcpp = true; } b.installArtifact(lib); @@ -81,8 +81,8 @@ pub fn build(b: *std.Build) !void { // Add the core Dear Imgui source files if (b.lazyDependency("imgui", .{})) |upstream| { - lib.addIncludePath(upstream.path("")); - lib.addCSourceFiles(.{ + lib.root_module.addIncludePath(upstream.path("")); + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "imgui_demo.cpp", @@ -101,20 +101,20 @@ pub fn build(b: *std.Build) !void { ); if (freetype) { - lib.addCSourceFile(.{ + lib.root_module.addCSourceFile(.{ .file = upstream.path("misc/freetype/imgui_freetype.cpp"), .flags = flags.items, }); if (b.systemIntegrationOption("freetype", .{})) { - lib.linkSystemLibrary2("freetype2", dynamic_link_opts); + lib.root_module.linkSystemLibrary("freetype2", .{}); } else { const freetype_dep = b.dependency("freetype", .{ .target = target, .optimize = optimize, .@"enable-libpng" = true, }); - lib.linkLibrary(freetype_dep.artifact("freetype")); + lib.root_module.linkLibrary(freetype_dep.artifact("freetype")); if (freetype_dep.builder.lazyDependency( "freetype", .{}, @@ -125,7 +125,7 @@ pub fn build(b: *std.Build) !void { } if (backend_metal) { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path("backends"), .files = &.{"imgui_impl_metal.mm"}, .flags = flags.items, @@ -137,7 +137,7 @@ pub fn build(b: *std.Build) !void { ); } if (backend_osx) { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path("backends"), .files = &.{"imgui_impl_osx.mm"}, .flags = flags.items, @@ -149,7 +149,7 @@ pub fn build(b: *std.Build) !void { ); } if (backend_opengl3) { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path("backends"), .files = &.{"imgui_impl_opengl3.cpp"}, .flags = flags.items, @@ -164,8 +164,8 @@ pub fn build(b: *std.Build) !void { // Add the C bindings if (b.lazyDependency("bindings", .{})) |upstream| { - lib.addIncludePath(upstream.path("")); - lib.addCSourceFiles(.{ + lib.root_module.addIncludePath(upstream.path("")); + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "dcimgui.cpp", @@ -173,7 +173,7 @@ pub fn build(b: *std.Build) !void { }, .flags = flags.items, }); - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = b.path(""), .files = &.{"ext.cpp"}, .flags = flags.items, @@ -195,7 +195,7 @@ pub fn build(b: *std.Build) !void { }), }); test_exe.root_module.addOptions("build_options", options); - test_exe.linkLibrary(lib); + test_exe.root_module.linkLibrary(lib); const tests_run = b.addRunArtifact(test_exe); const test_step = b.step("test", "Run tests"); test_step.dependOn(&tests_run.step); diff --git a/pkg/fontconfig/build.zig b/pkg/fontconfig/build.zig index 7c87d1f2ee2..c10701fd92b 100644 --- a/pkg/fontconfig/build.zig +++ b/pkg/fontconfig/build.zig @@ -41,7 +41,7 @@ pub fn build(b: *std.Build) !void { if (b.systemIntegrationOption("fontconfig", .{})) { module.linkSystemLibrary("fontconfig", dynamic_link_opts); - test_exe.linkSystemLibrary2("fontconfig", dynamic_link_opts); + test_exe.root_module.linkSystemLibrary("fontconfig", dynamic_link_opts); } else { const lib = try buildLib(b, module, .{ .target = target, @@ -54,7 +54,7 @@ pub fn build(b: *std.Build) !void { .dynamic_link_opts = dynamic_link_opts, }); - test_exe.linkLibrary(lib); + test_exe.root_module.linkLibrary(lib); } } @@ -65,21 +65,23 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu const libxml2_enabled = options.libxml2_enabled; const libxml2_iconv_enabled = options.libxml2_iconv_enabled; const freetype_enabled = options.freetype_enabled; + const dynamic_link_opts = options.dynamic_link_opts; const lib = b.addLibrary(.{ .name = "fontconfig", .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); + lib.root_module.linkSystemLibrary("pthread", .{}); if (target.result.os.tag != .windows) { - lib.linkSystemLibrary("pthread"); + lib.root_module.linkSystemLibrary("pthread", .{}); } - lib.addIncludePath(b.path("override/include")); + lib.root_module.addIncludePath(b.path("override/include")); module.addIncludePath(b.path("override/include")); var flags: std.ArrayList([]const u8) = .empty; @@ -194,19 +196,17 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu } } - const dynamic_link_opts = options.dynamic_link_opts; - // Freetype2 _ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help if (freetype_enabled) { if (b.systemIntegrationOption("freetype", .{})) { - lib.linkSystemLibrary2("freetype2", dynamic_link_opts); + lib.root_module.linkSystemLibrary("freetype2", dynamic_link_opts); } else { if (b.lazyDependency( "freetype", .{ .target = target, .optimize = optimize }, )) |freetype_dep| { - lib.linkLibrary(freetype_dep.artifact("freetype")); + lib.root_module.linkLibrary(freetype_dep.artifact("freetype")); } } } @@ -227,22 +227,22 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu } if (b.systemIntegrationOption("libxml2", .{})) { - lib.linkSystemLibrary2("libxml-2.0", dynamic_link_opts); + lib.root_module.linkSystemLibrary("libxml-2.0", .{}); } else { if (b.lazyDependency("libxml2", .{ .target = target, .optimize = optimize, .iconv = libxml2_iconv_enabled, })) |libxml2_dep| { - lib.linkLibrary(libxml2_dep.artifact("xml2")); + lib.root_module.linkLibrary(libxml2_dep.artifact("xml2")); } } } if (b.lazyDependency("fontconfig", .{})) |upstream| { - lib.addIncludePath(upstream.path("")); + lib.root_module.addIncludePath(upstream.path("")); module.addIncludePath(upstream.path("")); - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = srcs, .flags = flags.items, diff --git a/pkg/freetype/build.zig b/pkg/freetype/build.zig index b85310a5b1c..4a814d1b936 100644 --- a/pkg/freetype/build.zig +++ b/pkg/freetype/build.zig @@ -37,9 +37,9 @@ pub fn build(b: *std.Build) !void { module.addIncludePath(b.path("")); if (b.systemIntegrationOption("freetype", .{})) { - module.linkSystemLibrary("freetype2", dynamic_link_opts); + module.linkSystemLibrary("freetype2", .{}); if (test_exe) |exe| { - exe.linkSystemLibrary2("freetype2", dynamic_link_opts); + exe.root_module.linkSystemLibrary("freetype2", .{}); } } else { const lib = try buildLib(b, module, .{ @@ -52,7 +52,7 @@ pub fn build(b: *std.Build) !void { }); if (test_exe) |exe| { - exe.linkLibrary(lib); + exe.root_module.linkLibrary(lib); } } } @@ -68,10 +68,10 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); try apple_sdk.addPaths(b, lib); @@ -97,14 +97,12 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu try flags.append(b.allocator, "-fPIC"); } - const dynamic_link_opts = options.dynamic_link_opts; - // Zlib if (b.systemIntegrationOption("zlib", .{})) { - lib.linkSystemLibrary2("zlib", dynamic_link_opts); + lib.root_module.linkSystemLibrary("zlib", .{}); } else { const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize }); - lib.linkLibrary(zlib_dep.artifact("z")); + lib.root_module.linkLibrary(zlib_dep.artifact("z")); } // Libpng @@ -113,50 +111,50 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu try flags.append(b.allocator, "-DFT_CONFIG_OPTION_USE_PNG=1"); if (b.systemIntegrationOption("libpng", .{})) { - lib.linkSystemLibrary2("libpng", dynamic_link_opts); + lib.root_module.linkSystemLibrary("libpng", .{}); } else { const libpng_dep = b.dependency( "libpng", .{ .target = target, .optimize = optimize }, ); - lib.linkLibrary(libpng_dep.artifact("png")); + lib.root_module.linkLibrary(libpng_dep.artifact("png")); } } if (b.lazyDependency("freetype", .{})) |upstream| { - lib.addIncludePath(upstream.path("include")); + lib.root_module.addIncludePath(upstream.path("include")); module.addIncludePath(upstream.path("include")); - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = srcs, .flags = flags.items, }); switch (target.result.os.tag) { - .linux => lib.addCSourceFile(.{ + .linux => lib.root_module.addCSourceFile(.{ .file = upstream.path("builds/unix/ftsystem.c"), .flags = flags.items, }), - .windows => lib.addCSourceFile(.{ + .windows => lib.root_module.addCSourceFile(.{ .file = upstream.path("builds/windows/ftsystem.c"), .flags = flags.items, }), - else => lib.addCSourceFile(.{ + else => lib.root_module.addCSourceFile(.{ .file = upstream.path("src/base/ftsystem.c"), .flags = flags.items, }), } switch (target.result.os.tag) { .windows => { - lib.addCSourceFile(.{ + lib.root_module.addCSourceFile(.{ .file = upstream.path("builds/windows/ftdebug.c"), .flags = flags.items, }); - lib.addWin32ResourceFile(.{ + lib.root_module.addWin32ResourceFile(.{ .file = upstream.path("src/base/ftver.rc"), }); }, - else => lib.addCSourceFile(.{ + else => lib.root_module.addCSourceFile(.{ .file = upstream.path("src/base/ftdebug.c"), .flags = flags.items, }), diff --git a/pkg/glslang/build.zig b/pkg/glslang/build.zig index 1dc82a6e304..7a80ec62f3e 100644 --- a/pkg/glslang/build.zig +++ b/pkg/glslang/build.zig @@ -26,7 +26,7 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }), }); - test_exe.linkLibrary(lib); + test_exe.root_module.linkLibrary(lib); const tests_run = b.addRunArtifact(test_exe); const test_step = b.step("test", "Run tests"); test_step.dependOn(&tests_run.step); @@ -47,20 +47,20 @@ fn buildGlslang( .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); // On MSVC, we must not use linkLibCpp because Zig unconditionally // passes -nostdinc++ and then adds its bundled libc++/libc++abi // include paths, which conflict with MSVC's own C++ runtime headers. // The MSVC SDK include directories (added via linkLibC) contain // both C and C++ headers, so linkLibCpp is not needed. if (target.result.abi != .msvc) { - lib.linkLibCpp(); + lib.root_module.link_libcpp = true; } - if (upstream_) |upstream| lib.addIncludePath(upstream.path("")); - lib.addIncludePath(b.path("override")); + if (upstream_) |upstream| lib.root_module.addIncludePath(upstream.path("")); + lib.root_module.addIncludePath(b.path("override")); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); try apple_sdk.addPaths(b, lib); @@ -82,7 +82,7 @@ fn buildGlslang( } if (upstream_) |upstream| { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .flags = flags.items, .files = &.{ @@ -141,7 +141,7 @@ fn buildGlslang( }); if (target.result.os.tag != .windows) { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .flags = flags.items, .files = &.{ @@ -149,7 +149,7 @@ fn buildGlslang( }, }); } else { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .flags = flags.items, .files = &.{ diff --git a/pkg/gtk4-layer-shell/build.zig b/pkg/gtk4-layer-shell/build.zig index 818b48f45de..ea144cd7630 100644 --- a/pkg/gtk4-layer-shell/build.zig +++ b/pkg/gtk4-layer-shell/build.zig @@ -6,7 +6,6 @@ const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{ .preferred_link_mode = .dynamic, .search_strategy = .mode_first, }; - pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); @@ -42,6 +41,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), }); b.installArtifact(lib); @@ -52,13 +52,12 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu const upstream = upstream_ orelse return lib; const wayland_protocols = wayland_protocols_ orelse return lib; - lib.linkLibC(); - lib.addIncludePath(upstream.path("include")); - lib.addIncludePath(upstream.path("src")); + lib.root_module.addIncludePath(upstream.path("include")); + lib.root_module.addIncludePath(upstream.path("src")); module.addIncludePath(upstream.path("include")); // GTK - lib.linkSystemLibrary2("gtk4", dynamic_link_opts); + lib.root_module.linkSystemLibrary("gtk4", .{}); // Wayland headers and source files { @@ -92,9 +91,9 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu const source_scanner = b.addSystemCommand(&.{ "wayland-scanner", "private-code" }); source_scanner.addFileArg(xml); const source = source_scanner.addOutputFileArg(b.fmt("{s}.c", .{name})); - lib.addCSourceFile(.{ .file = source }); + lib.root_module.addCSourceFile(.{ .file = source }); } - lib.addIncludePath(wf.getDirectory()); + lib.root_module.addIncludePath(wf.getDirectory()); } lib.installHeadersDirectory( @@ -113,7 +112,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu "stubbed-surface.c", "xdg-surface-server.c", }; - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path("src"), .files = srcs, .flags = &.{ diff --git a/pkg/harfbuzz/build.zig b/pkg/harfbuzz/build.zig index 6d8f3be70ad..5465cf7cb72 100644 --- a/pkg/harfbuzz/build.zig +++ b/pkg/harfbuzz/build.zig @@ -54,9 +54,9 @@ pub fn build(b: *std.Build) !void { var it = module.import_table.iterator(); while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*); if (b.systemIntegrationOption("freetype", .{})) { - test_exe.linkSystemLibrary2("freetype2", dynamic_link_opts); + test_exe.root_module.linkSystemLibrary("freetype2", .{}); } else { - test_exe.linkLibrary(freetype.artifact("freetype")); + test_exe.root_module.linkLibrary(freetype.artifact("freetype")); } const tests_run = b.addRunArtifact(test_exe); const test_step = b.step("test", "Run tests"); @@ -64,8 +64,8 @@ pub fn build(b: *std.Build) !void { } if (b.systemIntegrationOption("harfbuzz", .{})) { - module.linkSystemLibrary("harfbuzz", dynamic_link_opts); - test_exe.linkSystemLibrary2("harfbuzz", dynamic_link_opts); + module.linkSystemLibrary("harfbuzz", .{}); + test_exe.root_module.linkSystemLibrary("harfbuzz", .{}); } else { const lib = try buildLib(b, module, .{ .target = target, @@ -77,7 +77,7 @@ pub fn build(b: *std.Build) !void { .dynamic_link_opts = dynamic_link_opts, }); - test_exe.linkLibrary(lib); + test_exe.root_module.linkLibrary(lib); } } @@ -87,6 +87,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu const coretext_enabled = options.coretext_enabled; const freetype_enabled = options.freetype_enabled; + const dynamic_link_opts = options.dynamic_link_opts; const freetype = b.dependency("freetype", .{ .target = target, @@ -99,25 +100,23 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); // On MSVC, we must not use linkLibCpp because Zig unconditionally // passes -nostdinc++ and then adds its bundled libc++/libc++abi // include paths, which conflict with MSVC's own C++ runtime headers. // The MSVC SDK include directories (added via linkLibC) contain // both C and C++ headers, so linkLibCpp is not needed. if (target.result.abi != .msvc) { - lib.linkLibCpp(); + lib.root_module.link_libcpp = true; } if (target.result.os.tag.isDarwin()) { try apple_sdk.addPaths(b, lib); } - const dynamic_link_opts = options.dynamic_link_opts; - var flags: std.ArrayList([]const u8) = .empty; defer flags.deinit(b.allocator); try flags.appendSlice(b.allocator, &.{ @@ -145,10 +144,10 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu }); if (b.systemIntegrationOption("freetype", .{})) { - lib.linkSystemLibrary2("freetype2", dynamic_link_opts); + lib.root_module.linkSystemLibrary("freetype2", dynamic_link_opts); module.linkSystemLibrary("freetype2", dynamic_link_opts); } else { - lib.linkLibrary(freetype.artifact("freetype")); + lib.root_module.linkLibrary(freetype.artifact("freetype")); if (freetype.builder.lazyDependency( "freetype", @@ -161,14 +160,14 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu if (coretext_enabled) { try flags.appendSlice(b.allocator, &.{"-DHAVE_CORETEXT=1"}); - lib.linkFramework("CoreText"); + lib.root_module.linkFramework("CoreText", .{}); module.linkFramework("CoreText", .{}); } if (b.lazyDependency("harfbuzz", .{})) |upstream| { - lib.addIncludePath(upstream.path("src")); + lib.root_module.addIncludePath(upstream.path("src")); module.addIncludePath(upstream.path("src")); - lib.addCSourceFile(.{ + lib.root_module.addCSourceFile(.{ .file = upstream.path("src/harfbuzz.cc"), .flags = flags.items, }); diff --git a/pkg/highway/build.zig b/pkg/highway/build.zig index 6ed721562fc..36a36c9ddc3 100644 --- a/pkg/highway/build.zig +++ b/pkg/highway/build.zig @@ -17,12 +17,12 @@ pub fn build(b: *std.Build) !void { .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); if (upstream_) |upstream| { - lib.addIncludePath(upstream.path("")); + lib.root_module.addIncludePath(upstream.path("")); module.addIncludePath(upstream.path("")); } @@ -93,9 +93,9 @@ pub fn build(b: *std.Build) !void { }); } - lib.addCSourceFiles(.{ .flags = flags.items, .files = &.{"bridge.cpp"} }); + lib.root_module.addCSourceFiles(.{ .flags = flags.items, .files = &.{"bridge.cpp"} }); if (upstream_) |upstream| { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .flags = flags.items, .files = &.{ @@ -124,7 +124,7 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }), }); - test_exe.linkLibrary(lib); + test_exe.root_module.linkLibrary(lib); var it = module.import_table.iterator(); while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*); diff --git a/pkg/libintl/build.zig b/pkg/libintl/build.zig index 32221e5aded..8ef26c7a491 100644 --- a/pkg/libintl/build.zig +++ b/pkg/libintl/build.zig @@ -35,11 +35,11 @@ pub fn build(b: *std.Build) !void { .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); - lib.addIncludePath(b.path("")); + lib.root_module.addIncludePath(b.path("")); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); @@ -47,9 +47,9 @@ pub fn build(b: *std.Build) !void { } if (b.lazyDependency("gettext", .{})) |upstream| { - lib.addIncludePath(upstream.path("gettext-runtime/intl")); - lib.addIncludePath(upstream.path("gettext-runtime/intl/gnulib-lib")); - lib.addCSourceFiles(.{ + lib.root_module.addIncludePath(upstream.path("gettext-runtime/intl")); + lib.root_module.addIncludePath(upstream.path("gettext-runtime/intl/gnulib-lib")); + lib.root_module.addCSourceFiles(.{ .root = upstream.path("gettext-runtime/intl"), .files = srcs, .flags = flags.items, diff --git a/pkg/libpng/build.zig b/pkg/libpng/build.zig index dbedac632d8..fd709d3d900 100644 --- a/pkg/libpng/build.zig +++ b/pkg/libpng/build.zig @@ -9,12 +9,12 @@ pub fn build(b: *std.Build) !void { .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); if (target.result.os.tag == .linux) { - lib.linkSystemLibrary("m"); + lib.root_module.linkSystemLibrary("m", .{}); } if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); @@ -30,18 +30,18 @@ pub fn build(b: *std.Build) !void { }; if (b.systemIntegrationOption("zlib", .{})) { - lib.linkSystemLibrary2("zlib", dynamic_link_opts); + lib.root_module.linkSystemLibrary("zlib", dynamic_link_opts); } else { if (b.lazyDependency( "zlib", .{ .target = target, .optimize = optimize }, )) |zlib_dep| { - lib.linkLibrary(zlib_dep.artifact("z")); - lib.addIncludePath(b.path("")); + lib.root_module.linkLibrary(zlib_dep.artifact("z")); + lib.root_module.addIncludePath(b.path("")); } if (b.lazyDependency("libpng", .{})) |upstream| { - lib.addIncludePath(upstream.path("")); + lib.root_module.addIncludePath(upstream.path("")); } } @@ -55,7 +55,7 @@ pub fn build(b: *std.Build) !void { "-DPNG_MIPS_MSA_OPT=0", }); - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = srcs, .flags = flags.items, diff --git a/pkg/libxml2/build.zig b/pkg/libxml2/build.zig index a9b3e4b1a35..74d9e4af742 100644 --- a/pkg/libxml2/build.zig +++ b/pkg/libxml2/build.zig @@ -11,18 +11,18 @@ pub fn build(b: *std.Build) !void { .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); - if (upstream_) |upstream| lib.addIncludePath(upstream.path("include")); - lib.addIncludePath(b.path("override/include")); + if (upstream_) |upstream| lib.root_module.addIncludePath(upstream.path("include")); + lib.root_module.addIncludePath(b.path("override/include")); if (target.result.os.tag == .windows) { - lib.addIncludePath(b.path("override/config/win32")); - lib.linkSystemLibrary("ws2_32"); + lib.root_module.addIncludePath(b.path("override/config/win32")); + lib.root_module.linkSystemLibrary("ws2_32", .{}); } else { - lib.addIncludePath(b.path("override/config/posix")); + lib.root_module.addIncludePath(b.path("override/config/posix")); } var flags: std.ArrayList([]const u8) = .empty; @@ -98,7 +98,7 @@ pub fn build(b: *std.Build) !void { } if (upstream_) |upstream| { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = srcs, .flags = flags.items, diff --git a/pkg/macos/build.zig b/pkg/macos/build.zig index 0525e481e58..448abc2805e 100644 --- a/pkg/macos/build.zig +++ b/pkg/macos/build.zig @@ -21,21 +21,21 @@ pub fn build(b: *std.Build) !void { .linkage = .static, }); - lib.addCSourceFile(.{ + lib.root_module.addCSourceFile(.{ .file = b.path("os/zig_macos.c"), .flags = &.{"-std=c99"}, }); - lib.addCSourceFile(.{ + lib.root_module.addCSourceFile(.{ .file = b.path("text/ext.c"), }); - lib.linkFramework("CoreFoundation"); - lib.linkFramework("CoreGraphics"); - lib.linkFramework("CoreText"); - lib.linkFramework("CoreVideo"); - lib.linkFramework("QuartzCore"); - lib.linkFramework("IOSurface"); + lib.root_module.linkFramework("CoreFoundation", .{}); + lib.root_module.linkFramework("CoreGraphics", .{}); + lib.root_module.linkFramework("CoreText", .{}); + lib.root_module.linkFramework("CoreVideo", .{}); + lib.root_module.linkFramework("QuartzCore", .{}); + lib.root_module.linkFramework("IOSurface", .{}); if (target.result.os.tag == .macos) { - lib.linkFramework("Carbon"); + lib.root_module.linkFramework("Carbon", .{}); module.linkFramework("Carbon", .{}); } @@ -63,7 +63,7 @@ pub fn build(b: *std.Build) !void { if (target.result.os.tag.isDarwin()) { try apple_sdk.addPaths(b, test_exe); } - test_exe.linkLibrary(lib); + test_exe.root_module.linkLibrary(lib); var it = module.import_table.iterator(); while (it.next()) |entry| { diff --git a/pkg/oniguruma/build.zig b/pkg/oniguruma/build.zig index efc013b4344..04d2caeaf25 100644 --- a/pkg/oniguruma/build.zig +++ b/pkg/oniguruma/build.zig @@ -41,7 +41,7 @@ pub fn build(b: *std.Build) !void { module.linkSystemLibrary("oniguruma", dynamic_link_opts); if (test_exe) |exe| { - exe.linkSystemLibrary2("oniguruma", dynamic_link_opts); + exe.root_module.linkSystemLibrary("oniguruma", dynamic_link_opts); } } else { const lib = try buildLib(b, module, .{ @@ -50,7 +50,7 @@ pub fn build(b: *std.Build) !void { }); if (test_exe) |exe| { - exe.linkLibrary(lib); + exe.root_module.linkLibrary(lib); } } } @@ -64,12 +64,12 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); const t = target.result; const is_windows = t.os.tag == .windows; - lib.linkLibC(); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); @@ -77,10 +77,10 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu } if (b.lazyDependency("oniguruma", .{})) |upstream| { - lib.addIncludePath(upstream.path("src")); + lib.root_module.addIncludePath(upstream.path("src")); module.addIncludePath(upstream.path("src")); - lib.addConfigHeader(b.addConfigHeader(.{ + lib.root_module.addConfigHeader(b.addConfigHeader(.{ .style = .{ .cmake = upstream.path("src/config.h.cmake.in") }, }, .{ .PACKAGE = "oniguruma", @@ -103,7 +103,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu var flags: std.ArrayList([]const u8) = .empty; defer flags.deinit(b.allocator); - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .flags = flags.items, .files = &.{ diff --git a/pkg/sentry/build.zig b/pkg/sentry/build.zig index 3c88df56d13..7eb755f65eb 100644 --- a/pkg/sentry/build.zig +++ b/pkg/sentry/build.zig @@ -17,10 +17,10 @@ pub fn build(b: *std.Build) !void { .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); try apple_sdk.addPaths(b, lib); @@ -46,9 +46,9 @@ pub fn build(b: *std.Build) !void { if (b.lazyDependency("sentry", .{})) |upstream| { module.addIncludePath(upstream.path("include")); - lib.addIncludePath(upstream.path("include")); - lib.addIncludePath(upstream.path("src")); - lib.addCSourceFiles(.{ + lib.root_module.addIncludePath(upstream.path("include")); + lib.root_module.addIncludePath(upstream.path("src")); + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = srcs, .flags = flags.items, @@ -56,7 +56,7 @@ pub fn build(b: *std.Build) !void { // Linux-only if (target.result.os.tag == .linux) { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "vendor/stb_sprintf.c", @@ -67,7 +67,7 @@ pub fn build(b: *std.Build) !void { // Symbolizer + Unwinder if (target.result.os.tag == .windows) { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/sentry_windows_dbghelp.c", @@ -78,7 +78,7 @@ pub fn build(b: *std.Build) !void { .flags = flags.items, }); } else { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/sentry_unix_pageallocator.c", @@ -92,7 +92,7 @@ pub fn build(b: *std.Build) !void { // Module finder switch (target.result.os.tag) { - .windows => lib.addCSourceFiles(.{ + .windows => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/modulefinder/sentry_modulefinder_windows.c", @@ -100,7 +100,7 @@ pub fn build(b: *std.Build) !void { .flags = flags.items, }), - .macos, .ios => lib.addCSourceFiles(.{ + .macos, .ios => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/modulefinder/sentry_modulefinder_apple.c", @@ -108,7 +108,7 @@ pub fn build(b: *std.Build) !void { .flags = flags.items, }), - .linux => lib.addCSourceFiles(.{ + .linux => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/modulefinder/sentry_modulefinder_linux.c", @@ -126,7 +126,7 @@ pub fn build(b: *std.Build) !void { // Transport switch (transport) { - .curl => lib.addCSourceFiles(.{ + .curl => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/transports/sentry_transport_curl.c", @@ -134,7 +134,7 @@ pub fn build(b: *std.Build) !void { .flags = flags.items, }), - .winhttp => lib.addCSourceFiles(.{ + .winhttp => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/transports/sentry_transport_winhttp.c", @@ -142,7 +142,7 @@ pub fn build(b: *std.Build) !void { .flags = flags.items, }), - .none => lib.addCSourceFiles(.{ + .none => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/transports/sentry_transport_none.c", @@ -153,7 +153,7 @@ pub fn build(b: *std.Build) !void { // Backend switch (backend) { - .crashpad => lib.addCSourceFiles(.{ + .crashpad => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/backends/sentry_backend_crashpad.cpp", @@ -162,7 +162,7 @@ pub fn build(b: *std.Build) !void { }), .breakpad => { - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/backends/sentry_backend_breakpad.cpp", @@ -174,15 +174,15 @@ pub fn build(b: *std.Build) !void { .target = target, .optimize = optimize, })) |breakpad_dep| { - lib.linkLibrary(breakpad_dep.artifact("breakpad")); + lib.root_module.linkLibrary(breakpad_dep.artifact("breakpad")); // We need to add this because Sentry includes some breakpad // headers that include this vendored file... - lib.addIncludePath(breakpad_dep.path("vendor")); + lib.root_module.addIncludePath(breakpad_dep.path("vendor")); } }, - .inproc => lib.addCSourceFiles(.{ + .inproc => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/backends/sentry_backend_inproc.c", @@ -190,7 +190,7 @@ pub fn build(b: *std.Build) !void { .flags = flags.items, }), - .none => lib.addCSourceFiles(.{ + .none => lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = &.{ "src/backends/sentry_backend_none.c", diff --git a/pkg/simdutf/build.zig b/pkg/simdutf/build.zig index 2b714afa2df..df3d35e88f7 100644 --- a/pkg/simdutf/build.zig +++ b/pkg/simdutf/build.zig @@ -10,10 +10,11 @@ pub fn build(b: *std.Build) !void { .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); + if (!no_libcxx) { // On MSVC, we must not use linkLibCpp because Zig unconditionally // passes -nostdinc++ and then adds its bundled libc++/libc++abi @@ -21,10 +22,10 @@ pub fn build(b: *std.Build) !void { // The MSVC SDK include directories (added via linkLibC) contain // both C and C++ headers, so linkLibCpp is not needed. if (target.result.abi != .msvc) { - lib.linkLibCpp(); + lib.root_module.link_libcpp = true; } } - lib.addIncludePath(b.path("vendor")); + lib.root_module.addIncludePath(b.path("vendor")); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); @@ -59,7 +60,7 @@ pub fn build(b: *std.Build) !void { try flags.append(b.allocator, "-fPIC"); } - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .flags = flags.items, .files = &.{ "vendor/simdutf.cpp", diff --git a/pkg/spirv-cross/build.zig b/pkg/spirv-cross/build.zig index 72ce61eb60f..1cddb349b73 100644 --- a/pkg/spirv-cross/build.zig +++ b/pkg/spirv-cross/build.zig @@ -34,12 +34,12 @@ pub fn build(b: *std.Build) !void { if (b.systemIntegrationOption("spirv-cross", .{})) { module.linkSystemLibrary("spirv-cross-c-shared", dynamic_link_opts); if (test_exe) |exe| { - exe.linkSystemLibrary2("spirv-cross-c-shared", dynamic_link_opts); + exe.root_module.linkSystemLibrary("spirv-cross-c-shared", dynamic_link_opts); } } else { const lib = try buildSpirvCross(b, module, target, optimize); b.installArtifact(lib); - if (test_exe) |exe| exe.linkLibrary(lib); + if (test_exe) |exe| exe.root_module.linkLibrary(lib); } } @@ -54,17 +54,17 @@ fn buildSpirvCross( .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); // On MSVC, we must not use linkLibCpp because Zig unconditionally // passes -nostdinc++ and then adds its bundled libc++/libc++abi // include paths, which conflict with MSVC's own C++ runtime headers. // The MSVC SDK include directories (added via linkLibC) contain // both C and C++ headers, so linkLibCpp is not needed. if (target.result.abi != .msvc) { - lib.linkLibCpp(); + lib.root_module.link_libcpp = true; } if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); @@ -86,9 +86,9 @@ fn buildSpirvCross( } if (b.lazyDependency("spirv_cross", .{})) |upstream| { - lib.addIncludePath(upstream.path("")); + lib.root_module.addIncludePath(upstream.path("")); module.addIncludePath(upstream.path("")); - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .flags = flags.items, .files = &.{ diff --git a/pkg/wuffs/build.zig b/pkg/wuffs/build.zig index 3d9f83daa04..c88f3f7ded8 100644 --- a/pkg/wuffs/build.zig +++ b/pkg/wuffs/build.zig @@ -15,7 +15,6 @@ pub fn build(b: *std.Build) !void { .name = "test", .root_module = module, }); - unit_tests.linkLibC(); var flags: std.ArrayList([]const u8) = .empty; defer flags.deinit(b.allocator); diff --git a/pkg/zlib/build.zig b/pkg/zlib/build.zig index 6bde60ec790..ef2df0b5994 100644 --- a/pkg/zlib/build.zig +++ b/pkg/zlib/build.zig @@ -9,17 +9,17 @@ pub fn build(b: *std.Build) !void { .root_module = b.createModule(.{ .target = target, .optimize = optimize, + .link_libc = true, }), .linkage = .static, }); - lib.linkLibC(); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); try apple_sdk.addPaths(b, lib); } if (b.lazyDependency("zlib", .{})) |upstream| { - lib.addIncludePath(upstream.path("")); + lib.root_module.addIncludePath(upstream.path("")); lib.installHeadersDirectory( upstream.path(""), "", @@ -42,7 +42,7 @@ pub fn build(b: *std.Build) !void { "-D_CRT_NONSTDC_NO_DEPRECATE", }); } - lib.addCSourceFiles(.{ + lib.root_module.addCSourceFiles(.{ .root = upstream.path(""), .files = srcs, .flags = flags.items, diff --git a/src/App.zig b/src/App.zig index 5446f4bf273..6e174d1cc28 100644 --- a/src/App.zig +++ b/src/App.zig @@ -18,11 +18,15 @@ const font = @import("font/main.zig"); const log = std.log.scoped(.app); -const SurfaceList = std.ArrayListUnmanaged(*apprt.Surface); +const SurfaceList = std.ArrayList(*apprt.Surface); + +io: std.Io, /// General purpose allocator alloc: Allocator, +environ: *const std.process.Environ.Map, + /// The list of surfaces that are currently active. surfaces: SurfaceList, @@ -56,7 +60,7 @@ font_grid_set: font.SharedGridSet, // Used to rate limit desktop notifications. Some platforms (notably macOS) will // run out of resources if desktop notifications are sent too fast and the OS // will kill Ghostty. -last_notification_time: ?std.time.Instant = null, +last_notification_time: ?std.Io.Timestamp = null, last_notification_digest: u64 = 0, /// The conditional state of the configuration. See the equivalent field @@ -73,10 +77,10 @@ pub const CreateError = Allocator.Error || font.SharedGridSet.InitError; /// Create a new app instance. This returns a stable pointer to the app /// instance which is required for callbacks. -pub fn create(alloc: Allocator) CreateError!*App { +pub fn create(alloc: Allocator, io: std.Io, env: *const std.process.Environ.Map) CreateError!*App { var app = try alloc.create(App); errdefer alloc.destroy(app); - try app.init(alloc); + try app.init(alloc, io, env); return app; } @@ -89,12 +93,16 @@ pub fn create(alloc: Allocator) CreateError!*App { pub fn init( self: *App, alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, ) CreateError!void { var font_grid_set = try font.SharedGridSet.init(alloc); errdefer font_grid_set.deinit(); self.* = .{ .alloc = alloc, + .io = io, + .environ = env, .surfaces = .{}, .mailbox = .{}, .font_grid_set = font_grid_set, @@ -146,6 +154,8 @@ pub fn updateConfig(self: *App, rt_app: *apprt.App, config: *const Config) !void // config applies its own conditional state. var applied_: ?configpkg.Config = config.changeConditionalState( self.config_conditional_state, + self.io, + self.environ, ) catch |err| err: { log.warn("failed to apply conditional state to config err={}", .{err}); break :err null; @@ -236,7 +246,7 @@ pub fn needsConfirmQuit(self: *const App) bool { /// Drain the mailbox. fn drainMailbox(self: *App, rt_app: *apprt.App) !void { - while (self.mailbox.pop()) |message| { + while (self.mailbox.pop(self.io)) |message| { if (comptime std.log.logEnabled(.debug, .app)) { switch (message) { // these tend to be way too verbose for normal debugging @@ -582,10 +592,11 @@ pub const Mailbox = struct { rt_app: *apprt.App, mailbox: *Queue, + io: std.Io, /// Send a message to the surface. pub fn push(self: Mailbox, msg: Message, timeout: Queue.Timeout) Queue.Size { - const result = self.mailbox.push(msg, timeout); + const result = self.mailbox.push(msg, timeout, self.io); // Wake up our app loop self.rt_app.wakeup(); diff --git a/src/Command.zig b/src/Command.zig index 2b381912b66..80e1d81b806 100644 --- a/src/Command.zig +++ b/src/Command.zig @@ -29,8 +29,8 @@ const posix = std.posix; const debug = std.debug; const testing = std.testing; const Allocator = std.mem.Allocator; -const File = std.fs.File; -const EnvMap = std.process.EnvMap; +const File = std.Io.File; +const EnvMap = std.process.Environ.Map; const apprt = @import("apprt.zig"); /// Function prototype for a function executed /in the child process/ after the @@ -65,7 +65,7 @@ env: ?*const EnvMap = null, /// Working directory to change to in the child process. If not set, the /// working directory of the calling process is preserved. -cwd: ?[]const u8 = null, +cwd: ?[:0]const u8 = null, /// The file handle to set for stdin/out/err. If this isn't set, we do /// nothing explicitly so it is up to the behavior of the operating system. @@ -157,7 +157,7 @@ pub const RtPostForkInfo = if (@hasDecl(apprt.runtime, "post_fork")) apprt.runti /// Start the subprocess. This returns immediately once the child is started. /// /// After this is successful, self.pid is available. -pub fn start(self: *Command, alloc: Allocator) !void { +pub fn start(self: *Command, alloc: Allocator, io: std.Io) !void { // Use an arena allocator for the temporary allocations we need in this func. // IMPORTANT: do all allocation prior to the fork(). I believe it is undefined // behavior if you malloc between fork and exec. The source of the Zig @@ -167,12 +167,12 @@ pub fn start(self: *Command, alloc: Allocator) !void { const arena = arena_allocator.allocator(); switch (builtin.os.tag) { - .windows => try self.startWindows(arena), - else => try self.startPosix(arena), + .windows => try self.startWindows(arena, io), + else => try self.startPosix(arena, io), } } -fn startPosix(self: *Command, arena: Allocator) !void { +fn startPosix(self: *Command, arena: Allocator, io: std.Io) !void { // Null-terminate all our arguments const argsZ = try arena.allocSentinel(?[*:0]const u8, self.args.len, null); for (self.args, 0..) |arg, i| argsZ[i] = arg.ptr; @@ -186,7 +186,8 @@ fn startPosix(self: *Command, arena: Allocator) !void { @compileError("missing env vars"); // Fork. - const pid = try posix.fork(); + const pid = std.c.fork(); + if (pid < 0) return error.ExecFailedInChild; if (pid != 0) { // Parent, return immediately. @@ -206,49 +207,55 @@ fn startPosix(self: *Command, arena: Allocator) !void { return error.ExecFailedInChild; // Setup our working directory - if (self.cwd) |cwd| posix.chdir(cwd) catch { + if (self.cwd) |cwd| { // This can fail if we don't have permission to go to // this directory or if due to race conditions it doesn't // exist or any various other reasons. We don't want to // crash the entire process if this fails so we ignore it. // We don't log because that'll show up in the output. - }; + _ = posix.system.chdir(cwd.ptr); + } // Restore any rlimits that were set by Ghostty. This might fail but // any failures are ignored (its best effort). global_state.rlimits.restore(); // If there are pre exec callbacks, call them now. - if (self.os_pre_exec) |f| if (f(self)) |exitcode| posix.exit(exitcode); - if (self.rt_pre_exec) |f| if (f(self)) |exitcode| posix.exit(exitcode); + if (self.os_pre_exec) |f| if (f(self)) |exitcode| std.process.exit(exitcode); + if (self.rt_pre_exec) |f| if (f(self)) |exitcode| std.process.exit(exitcode); // Finally, replace our process. - // Note: we must use the "p"-variant of exec here because we - // do not guarantee our command is looked up already in the path. - const err = posix.execvpeZ(self.path, argsZ, envp); - - // If we are executing this code, the exec failed. We're in the - // child process so there isn't much we can do. We try to output - // something reasonable. Its important to note we MUST NOT return - // any other error condition from here on out. + // Note: we must use the "p"-variant of exec because we + // do not guarantee our command is looked up in the path. + // We set environ explicitly then use execvp for PATH search. + if (builtin.link_libc) { + std.c.environ = envp; + } + _ = execvp(self.path, argsZ); + + // If we are executing this code, the exec failed. We're in + // the child process so there isn't much we can do. We try to + // output something reasonable. Its important to note we MUST + // NOT return any other error condition from here on out. var stderr_buf: [1024]u8 = undefined; - var stderr_writer = std.fs.File.stderr().writer(&stderr_buf); + var stderr_writer = std.Io.File.stderr().writer( + io, + &stderr_buf, + ); const stderr = &stderr_writer.interface; - switch (err) { - error.FileNotFound => stderr.print( - \\Requested executable not found. Please verify the command is on - \\the PATH and try again. + const errno_val = std.c._errno().*; + if (errno_val == @intFromEnum(posix.E.NOENT)) { + stderr.print( + \\Requested executable not found. Please + \\verify the command is on the PATH and + \\try again. \\ - , - .{}, - ) catch {}, - - else => stderr.print( - \\exec syscall failed with unexpected error: {} + , .{}) catch {}; + } else { + stderr.print( + \\exec syscall failed with errno: {} \\ - , - .{err}, - ) catch {}, + , .{errno_val}) catch {}; } stderr.flush() catch {}; @@ -257,7 +264,9 @@ fn startPosix(self: *Command, arena: Allocator) !void { return error.ExecFailedInChild; } -fn startWindows(self: *Command, arena: Allocator) !void { +fn startWindows(self: *Command, arena: Allocator, io: std.Io) !void { + _ = io; + const application_w = try std.unicode.utf8ToUtf16LeAllocZ(arena, self.path); const cwd_w = if (self.cwd) |cwd| try std.unicode.utf8ToUtf16LeAllocZ(arena, cwd) else null; const command_line_w = if (self.args.len > 0) b: { @@ -275,7 +284,7 @@ fn startWindows(self: *Command, arena: Allocator) !void { .creation = windows.OPEN_EXISTING, }, ) else null; - defer if (null_fd) |fd| posix.close(fd); + defer if (null_fd) |fd| windows.CloseHandle(fd); // TODO: In the case of having FDs instead of pty, need to set up // attributes such that the child process only inherits these handles, @@ -387,12 +396,59 @@ fn setupFd(src: File.Handle, target: i32) !void { .freebsd, .ios, .macos => { // Mac doesn't support dup3 so we use dup2. We purposely clear // CLO_ON_EXEC for this fd. - const flags = try posix.fcntl(src, posix.F.GETFD, 0); + const flags: u32 = while (true) { + const rc = posix.system.fcntl(src, posix.F.GETFD, 0); + switch (posix.errno()) { + .SUCCESS => break @intCast(rc), + .INTR => continue, + .AGAIN, .ACCES => return error.Locked, + .BADF => unreachable, + .BUSY => return error.FileBusy, + .INVAL => unreachable, // invalid parameters + .PERM => return error.PermissionDenied, + .MFILE => return error.ProcessFdQuotaExceeded, + .NOTDIR => unreachable, // invalid parameter + .DEADLK => return error.DeadLock, + .NOLCK => return error.LockedRegionLimitExceeded, + else => |err| return posix.unexpectedErrno(err), + } + }; + if (flags & posix.FD_CLOEXEC != 0) { - _ = try posix.fcntl(src, posix.F.SETFD, flags & ~@as(u32, posix.FD_CLOEXEC)); + while (true) { + const rc = posix.system.fcntl( + src, + posix.F.SETFD, + flags & ~posix.FD_CLOEXEC, + ); + switch (posix.errno(rc)) { + .SUCCESS => break, + .INTR => continue, + .AGAIN, .ACCES => return error.Locked, + .BADF => unreachable, + .BUSY => return error.FileBusy, + .INVAL => unreachable, // invalid parameters + .PERM => return error.PermissionDenied, + .MFILE => return error.ProcessFdQuotaExceeded, + .NOTDIR => unreachable, // invalid parameter + .DEADLK => return error.DeadLock, + .NOLCK => return error.LockedRegionLimitExceeded, + else => |err| return posix.unexpectedErrno(err), + } + } } - try posix.dup2(src, target); + while (true) { + const rc = posix.system.dup2(src, target); + switch (posix.errno(rc)) { + .SUCCESS => return, + .BUSY, .INTR => continue, + .MFILE => return error.ProcessFdQuotaExceeded, + .INVAL => unreachable, // invalid parameters passed to dup2 + .BADF => unreachable, // invalid file descriptor + else => |err| return posix.unexpectedErrno(err), + } + } }, else => @compileError("unsupported platform"), } @@ -538,6 +594,13 @@ fn windowsCreateCommandLine(allocator: mem.Allocator, argv: []const []const u8) return buf.toOwnedSliceSentinel(0); } +/// std.c/std.posix.system only models execve, +/// so we need to manually add execvp here +extern "c" fn execvp( + file: [*:0]const u8, + argv: [*:null]const ?[*:0]const u8, +) c_int; + test "createNullDelimitedEnvMap" { const allocator = testing.allocator; var envmap = EnvMap.init(allocator); @@ -689,7 +752,7 @@ test "Command: rt post fork 1" { try testing.expectError(error.PostForkError, cmd.testingStart()); } -fn createTestStdout(dir: std.fs.Dir) !File { +fn createTestStdout(dir: std.Io.Dir) !File { const file = try dir.createFile("stdout.txt", .{ .read = true }); if (builtin.os.tag == .windows) { try windows.SetHandleInformation( @@ -702,7 +765,7 @@ fn createTestStdout(dir: std.fs.Dir) !File { return file; } -fn createTestStderr(dir: std.fs.Dir) !File { +fn createTestStderr(dir: std.Io.Dir) !File { const file = try dir.createFile("stderr.txt", .{ .read = true }); if (builtin.os.tag == .windows) { try windows.SetHandleInformation( diff --git a/src/Surface.zig b/src/Surface.zig index dfc3a50ea13..b8eec356387 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -168,13 +168,13 @@ readonly: bool = false, /// precision timestamp. It does not necessarily need to correspond to the /// actual time, but we must be able to compare two subsequent timestamps to get /// the wall clock time that has elapsed between timestamps. -command_timer: ?std.time.Instant = null, +command_timer: ?std.Io.Timestamp = null, /// Search state search: ?Search = null, /// Used to rate limit BEL handling. -last_bell_time: ?std.time.Instant = null, +last_bell_time: ?std.Io.Timestamp = null, /// The effect of an input event. This can be used by callers to take /// the appropriate action after an input event. For example, key @@ -239,7 +239,7 @@ const Mouse = struct { /// The left click time was the last time the left click was done. This /// is always set on the first left click. left_click_count: u8 = 0, - left_click_time: std.time.Instant = undefined, + left_click_time: std.Io.Timestamp = undefined, /// The last x/y sent for mouse reports. event_point: ?terminal.point.Coordinate = null, @@ -275,13 +275,13 @@ pub const Keyboard = struct { /// /// This is naturally bounded due to the configuration maximum /// length of a sequence. - sequence_queued: std.ArrayListUnmanaged(termio.Message.WriteReq) = .empty, + sequence_queued: std.ArrayList(termio.Message.WriteReq) = .empty, /// The stack of tables that is currently active. The first value /// in this is the first activated table (NOT the default keybinding set). /// /// This is bounded by `max_active_key_tables`. - table_stack: std.ArrayListUnmanaged(struct { + table_stack: std.ArrayList(struct { set: *const input.Binding.Set, once: bool, }) = .empty, @@ -315,7 +315,7 @@ const DerivedConfig = struct { cursor_click_to_move: bool, desktop_notifications: bool, font: font.SharedGridSet.DerivedConfig, - mouse_interval: u64, + mouse_interval: i96, mouse_hide_while_typing: bool, mouse_reporting: bool, mouse_scroll_multiplier: configpkg.MouseScrollMultiplier, @@ -476,6 +476,8 @@ pub fn init( // then we log and attempt to move forward with the old config. var config_: ?configpkg.Config = config_original.changeConditionalState( app.config_conditional_state, + app.io, + app.environ, ) catch |err| err: { log.warn("failed to apply conditional state to config err={}", .{err}); break :err null; @@ -554,7 +556,7 @@ pub fn init( }; // Create our terminal grid with the initial size - const app_mailbox: App.Mailbox = .{ .rt_app = rt_app, .mailbox = &app.mailbox }; + const app_mailbox: App.Mailbox = .{ .rt_app = rt_app, .mailbox = &app.mailbox, .io = app.io }; var renderer_impl = try Renderer.init(alloc, .{ .config = try .init(alloc, config), .font_grid = font_grid, @@ -566,8 +568,8 @@ pub fn init( errdefer renderer_impl.deinit(); // The mutex used to protect our renderer state. - const mutex = try alloc.create(std.Thread.Mutex); - mutex.* = .{}; + const mutex = try alloc.create(std.Io.Mutex); + mutex.* = .init; errdefer alloc.destroy(mutex); // Create the renderer thread @@ -587,11 +589,9 @@ pub fn init( self.* = .{ .id = id: { - while (true) { - const candidate = std.crypto.random.int(u64); - if (candidate == 0) continue; - break :id candidate; - } + const rng_impl: std.Random.IoSource = .{ .io = app.io }; + const rng = rng_impl.interface(); + break :id rng.intRangeAtMost(u64, 1, std.math.maxInt(u64)); }, .alloc = alloc, .app = app, @@ -635,16 +635,16 @@ pub fn init( // This separate block ({}) is important because our errdefers must // be scoped here to be valid. { - var env = rt_surface.defaultTermioEnv() catch |err| env: { + var env: std.process.Environ.Map = rt_surface.defaultTermioEnv() catch |err| env: { // If an error occurs, we don't want to block surface startup. log.warn("error getting env map for surface err={}", .{err}); - break :env internal_os.getEnvMap(alloc) catch - std.process.EnvMap.init(alloc); + break :env internal_os.getSurfaceEnvMap(alloc, app.environ) catch + .init(alloc); }; errdefer env.deinit(); // don't leak GHOSTTY_LOG to any subprocesses - env.remove("GHOSTTY_LOG"); + _ = env.swapRemove("GHOSTTY_LOG"); var buf: [18]u8 = undefined; try env.put( @@ -724,7 +724,7 @@ pub fn init( rendererpkg.Thread.threadMain, .{&self.renderer_thread}, ); - self.renderer_thr.setName("renderer") catch {}; + self.renderer_thr.setName(self.app.io, "renderer") catch {}; // Start our IO thread self.io_thr = try std.Thread.spawn( @@ -732,7 +732,7 @@ pub fn init( termio.Thread.threadMain, .{ &self.io_thread, &self.io }, ); - self.io_thr.setName("io") catch {}; + self.io_thr.setName(self.app.io, "io") catch {}; // Determine our initial window size if configured. We need to do this // quite late in the process because our height/width are in grid dimensions, @@ -853,7 +853,7 @@ pub fn close(self: *Surface) void { inline fn surfaceMailbox(self: *Surface) Mailbox { return .{ .surface = self, - .app = .{ .rt_app = self.rt_app, .mailbox = &self.app.mailbox }, + .app = .{ .rt_app = self.rt_app, .mailbox = &self.app.mailbox, .io = self.app.io }, }; } @@ -906,14 +906,14 @@ pub fn activateInspector(self: *Surface) !void { // Put the inspector onto the render state { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); assert(self.renderer_state.inspector == null); self.renderer_state.inspector = self.inspector; } // Notify our components we have an inspector active - _ = self.renderer_thread.mailbox.push(.{ .inspector = true }, .{ .forever = {} }); + _ = self.renderer_thread.mailbox.push(.{ .inspector = true }, .{ .forever = {} }, self.app.io); self.queueIo(.{ .inspector = true }, .unlocked); } @@ -923,14 +923,14 @@ pub fn deactivateInspector(self: *Surface) void { // Remove the inspector from the render state { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); assert(self.renderer_state.inspector != null); self.renderer_state.inspector = null; } // Notify our components we have deactivated inspector - _ = self.renderer_thread.mailbox.push(.{ .inspector = false }, .{ .forever = {} }); + _ = self.renderer_thread.mailbox.push(.{ .inspector = false }, .{ .forever = {} }, self.app.io); self.queueIo(.{ .inspector = false }, .unlocked); // Deinit the inspector @@ -954,8 +954,8 @@ pub fn needsConfirmQuit(self: *Surface) bool { .always => true, .false => false, .true => true: { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); break :true !self.io.terminal.cursorIsAtPrompt(); }, }; @@ -1102,9 +1102,9 @@ pub fn handleMessage(self: *Surface, msg: Message) !void { .password_input => |v| try self.passwordInput(v), .ring_bell => bell: { - const now = std.time.Instant.now() catch unreachable; + const now = std.Io.Timestamp.now(self.app.io, .awake); if (self.last_bell_time) |last| { - if (now.since(last) < 100 * std.time.ns_per_ms) break :bell; + if (last.durationTo(now).toNanoseconds() < 100 * std.time.ns_per_ms) break :bell; } self.last_bell_time = now; _ = self.rt_app.performAction( @@ -1132,15 +1132,15 @@ pub fn handleMessage(self: *Surface, msg: Message) !void { }, .start_command => { - self.command_timer = try .now(); + self.command_timer = .now(self.app.io, .awake); }, .stop_command => |v| timer: { - const end: std.time.Instant = try .now(); + const end: std.Io.Timestamp = .now(self.app.io, .awake); const start = self.command_timer orelse break :timer; self.command_timer = null; - const duration: Duration = .{ .duration = end.since(start) }; + const duration: Duration = .{ .duration = @intCast(start.durationTo(end).toNanoseconds()) }; log.debug("command took {f}", .{duration}); _ = self.rt_app.performAction( @@ -1186,8 +1186,8 @@ fn selectionScrollTick(self: *Surface) !void { const delta: isize = if (pos.y < 0) -1 else 1; // We need our locked state for the remainder - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const t: *terminal.Terminal = self.renderer_state.terminal; // If our screen changed while this is happening, we stop our @@ -1275,8 +1275,8 @@ fn childExited(self: *Surface, info: apprt.surface.Message.ChildExited) void { // If the native GUI can't be shown, display a text message in the // terminal. - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const t: *terminal.Terminal = self.renderer_state.terminal; t.carriageReturn(); t.linefeed() catch break :terminal; @@ -1315,8 +1315,8 @@ fn childExitedAbnormally( }); const runtime_str = try std.fmt.allocPrint(alloc, "{d} ms", .{info.runtime_ms}); - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const t: *terminal.Terminal = self.renderer_state.terminal; // No matter what move the cursor back to the column 0. @@ -1382,8 +1382,8 @@ fn childExitedAbnormally( /// Called when the terminal detects there is a password input prompt. fn passwordInput(self: *Surface, v: bool) !void { { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); // If our password input state is unchanged then we don't // waste time doing anything more. @@ -1440,6 +1440,7 @@ fn searchCallback_( .matches = matches, } }, .forever, + self.app.io, ); try self.renderer_thread.wakeup.notify(); }, @@ -1458,6 +1459,7 @@ fn searchCallback_( .match = match, } }, .forever, + self.app.io, ); // Send the selected index to the surface mailbox @@ -1470,6 +1472,7 @@ fn searchCallback_( _ = self.renderer_thread.mailbox.push( .{ .search_selected_match = null }, .forever, + self.app.io, ); // Reset the selected index @@ -1494,6 +1497,7 @@ fn searchCallback_( _ = self.renderer_thread.mailbox.push( .{ .search_selected_match = null }, .forever, + self.app.io, ); _ = self.renderer_thread.mailbox.push( .{ .search_viewport_matches = .{ @@ -1501,6 +1505,7 @@ fn searchCallback_( .matches = &.{}, } }, .forever, + self.app.io, ); try self.renderer_thread.wakeup.notify(); @@ -1538,8 +1543,8 @@ fn modsChanged(self: *Surface, mods: input.Mods) void { // highlight links. Additionally, mark the screen as dirty so // that the highlight state of all links is properly updated. { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); self.renderer_state.mouse.mods = self.mouseModsWithCapture(self.mouse.mods); // We use the clear screen dirty flag to force a rebuild of all @@ -1718,6 +1723,8 @@ pub fn updateConfig( // then we log and attempt to move forward with the old config. var config_: ?configpkg.Config = original.changeConditionalState( self.config_conditional_state, + self.app.io, + self.app.environ, ) catch |err| err: { log.warn("failed to apply conditional state to config err={}", .{err}); break :err null; @@ -1785,7 +1792,7 @@ pub fn updateConfig( termio_config_ptr.* = try termio.Termio.DerivedConfig.init(self.alloc, config); errdefer termio_config_ptr.deinit(); - _ = self.renderer_thread.mailbox.push(renderer_message, .{ .forever = {} }); + _ = self.renderer_thread.mailbox.push(renderer_message, .{ .forever = {} }, self.app.io); self.queueIo(.{ .change_config = .{ .alloc = self.alloc, @@ -1906,8 +1913,8 @@ pub fn dumpText( alloc: Allocator, sel: terminal.Selection, ) !Text { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); return try self.dumpTextLocked(alloc, sel); } @@ -2032,15 +2039,15 @@ pub fn dumpTextLocked( /// Returns true if the terminal has a selection. pub fn hasSelection(self: *const Surface) bool { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); return self.io.terminal.screens.active.selection != null; } /// Returns the selected text. This is allocated. pub fn selectionString(self: *Surface, alloc: Allocator) !?[:0]const u8 { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const sel = self.io.terminal.screens.active.selection orelse return null; return try self.io.terminal.screens.active.selectionString(alloc, .{ .sel = sel, @@ -2055,8 +2062,8 @@ pub fn pwd( self: *const Surface, alloc: Allocator, ) Allocator.Error!?[]const u8 { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const terminal_pwd = self.io.terminal.getPwd() orelse return null; return try alloc.dupe(u8, terminal_pwd); } @@ -2066,14 +2073,14 @@ fn resolvePathForOpening( self: *Surface, path: []const u8, ) Allocator.Error!?[]const u8 { - if (!std.fs.path.isAbsolute(path)) { + if (!std.Io.Dir.path.isAbsolute(path)) { const terminal_pwd = self.io.terminal.getPwd() orelse { return null; }; - const resolved = try std.fs.path.resolve(self.alloc, &.{ terminal_pwd, path }); + const resolved = try std.Io.Dir.path.resolve(self.alloc, &.{ terminal_pwd, path }); - std.fs.accessAbsolute(resolved, .{}) catch { + std.Io.Dir.accessAbsolute(self.app.io, resolved, .{}) catch { self.alloc.free(resolved); return null; }; @@ -2087,10 +2094,10 @@ fn resolvePathForOpening( /// Returns the x/y coordinate of where the IME (Input Method Editor) /// keyboard should be rendered. pub fn imePoint(self: *const Surface) apprt.IMEPos { - self.renderer_state.mutex.lock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); const cursor = self.renderer_state.terminal.screens.active.cursor; const preedit_width: usize = if (self.renderer_state.preedit) |preedit| preedit.width() else 0; - self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.unlock(self.app.io); // TODO: need to handle when scrolling and the cursor is not // in the visible portion of the screen. @@ -2423,7 +2430,7 @@ pub fn setFontSize(self: *Surface, size: font.face.DesiredSize) !void { .old_key = self.font_grid_key, .new_key = font_grid_key, }, - }, .{ .forever = {} }); + }, .{ .forever = {} }, self.app.io); // Once we've sent the key we can replace our key self.font_grid_key = font_grid_key; @@ -2506,8 +2513,8 @@ pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) !void { crash.sentry.thread_state = self.crashThreadState(); defer crash.sentry.thread_state = null; - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); // We clear our selection when ANY OF: // 1. We have an existing preedit @@ -2542,7 +2549,7 @@ pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) !void { // Allocate the codepoints slice const Codepoint = rendererpkg.State.Preedit.Codepoint; - var codepoints: std.ArrayListUnmanaged(Codepoint) = .{}; + var codepoints: std.ArrayList(Codepoint) = .empty; defer codepoints.deinit(self.alloc); while (it.nextCodepoint()) |cp| { const width: usize = @intCast(unicode.table.get(cp).width); @@ -2673,8 +2680,8 @@ pub fn keyCallback( )) |v| return v; // If we allow KAM and KAM is enabled then we do nothing. if (self.config.vt_kam_allowed) { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); if (self.io.terminal.modes.get(.disable_keyboard)) return .consumed; } @@ -2704,8 +2711,8 @@ pub fn keyCallback( { // Refresh our link state const pos = self.rt_surface.getCursorPos() catch break :mouse_mods; - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); self.mouseRefreshLinks( pos, self.posToViewport(pos.x, pos.y), @@ -2797,8 +2804,8 @@ pub fn keyCallback( // some data to send to the pty, then we move the viewport down to the // bottom. We also clear the selection for any key other then modifiers. if (!event.key.modifier()) { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); if (self.config.selection_clear_on_typing or event.key == .escape) @@ -3229,8 +3236,8 @@ fn encodeKey( } fn encodeKeyOpts(self: *const Surface) input.key_encode.Options { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const t = &self.io.terminal; var opts: input.key_encode.Options = .fromTerminal(t); @@ -3273,7 +3280,7 @@ pub fn occlusionCallback(self: *Surface, visible: bool) !void { _ = self.renderer_thread.mailbox.push(.{ .visible = visible, - }, .{ .forever = {} }); + }, .{ .forever = {} }, self.app.io); try self.queueRender(); } @@ -3293,7 +3300,7 @@ pub fn focusCallback(self: *Surface, focused: bool) !void { // Notify our render thread of the new state _ = self.renderer_thread.mailbox.push(.{ .focus = focused, - }, .{ .forever = {} }); + }, .{ .forever = {} }, self.app.io); if (!focused) unfocused: { // If we lost focus and we have a keypress, then we want to send a key @@ -3358,9 +3365,9 @@ pub fn focusCallback(self: *Surface, focused: bool) !void { // Update the focus state and notify the terminal { - self.renderer_state.mutex.lock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); self.io.terminal.flags.focused = focused; - self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.unlock(self.app.io); self.queueIo(.{ .focused = focused }, .unlocked); } } @@ -3488,8 +3495,8 @@ pub fn scrollCallback( // log.info("SCROLL: delta_y={} delta_x={}", .{ y.delta, x.delta }); { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); // If we have an active mouse reporting mode, clear the selection. // The selection can occur if the user uses the shift mod key to @@ -3689,8 +3696,8 @@ fn mouseShiftCapture(self: *const Surface, lock: bool) bool { .false, .true => {}, } - if (lock) self.renderer_state.mutex.lock(); - defer if (lock) self.renderer_state.mutex.unlock(); + if (lock) self.renderer_state.mutex.lockUncancelable(self.app.io); + defer if (lock) self.renderer_state.mutex.unlock(self.app.io); // If the terminal explicitly requests it then we always allow it // since we processed never/always at this point. @@ -3711,8 +3718,8 @@ fn mouseShiftCapture(self: *const Surface, lock: bool) bool { /// Returns true if the mouse is currently captured by the terminal /// (i.e. reporting events). pub fn mouseCaptured(self: *Surface) bool { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); return self.io.terminal.flags.mouse_event != .none; } @@ -3768,20 +3775,14 @@ pub fn mouseButtonCallback( // If we are within the interval that the click would register // an increment then we do not extend the selection. - if (std.time.Instant.now()) |now| { - const since = now.since(self.mouse.left_click_time); + { + const now = std.Io.Timestamp.now(self.app.io, .awake); + const since = self.mouse.left_click_time.durationTo(now).toNanoseconds(); if (since <= self.config.mouse_interval) { // Click interval very short, we may be increasing // click counts so we don't extend the selection. break :extend_selection; } - } else |err| { - // This is a weird behavior, I think either behavior is actually - // fine. This failure should be exceptionally rare anyways. - // My thinking here is that we can't be sure if we should extend - // the selection or not so we just don't. - log.warn("failed to get time, not extending selection err={}", .{err}); - break :extend_selection; } const pos = try self.rt_surface.getCursorPos(); @@ -3804,8 +3805,8 @@ pub fn mouseButtonCallback( // the left button is released. This is to avoid the clipboard // being updated on every mouse move which would be noisy. if (self.config.copy_on_select != .false) { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const prev_ = self.io.terminal.screens.active.selection; if (prev_) |prev| { try self.setSelection(terminal.Selection.init( @@ -3840,8 +3841,8 @@ pub fn mouseButtonCallback( // Report mouse events if enabled { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); if (self.isMouseReporting()) report: { // If we have shift-pressed and we aren't allowed to capture it, // then we do not do a mouse report. @@ -3880,8 +3881,8 @@ pub fn mouseButtonCallback( // For left button clicks we always record some information for // selection/highlighting purposes. if (button == .left and action == .press) click: { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const t: *terminal.Terminal = self.renderer_state.terminal; const screen: *terminal.Screen = self.renderer_state.terminal.screens.active; @@ -3932,11 +3933,12 @@ pub fn mouseButtonCallback( self.mouse.left_click_ypos = pos.y; // Setup our click counter and timer - if (std.time.Instant.now()) |now| { + { + const now = std.Io.Timestamp.now(self.app.io, .awake); // If we have mouse clicks, then we check if the time elapsed // is less than and our interval and if so, increase the count. if (self.mouse.left_click_count > 0) { - const since = now.since(self.mouse.left_click_time); + const since = self.mouse.left_click_time.durationTo(now).toNanoseconds(); if (since > self.config.mouse_interval) { self.mouse.left_click_count = 0; } @@ -3947,9 +3949,6 @@ pub fn mouseButtonCallback( // We only support up to triple-clicks. if (self.mouse.left_click_count > 3) self.mouse.left_click_count = 1; - } else |err| { - self.mouse.left_click_count = 1; - log.err("error reading time, mouse multi-click won't work err={}", .{err}); } // In all cases below, we set the selection directly rather than use @@ -4022,8 +4021,8 @@ pub fn mouseButtonCallback( // want to be careful in the future we can add a function to apprts // that let's us know. if (button == .right and action == .press) sel: { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); // Get our viewport pin const screen: *terminal.Screen = self.renderer_state.terminal.screens.active; @@ -4094,8 +4093,8 @@ pub fn mouseButtonCallback( } else { // Pasting can trigger a lock grab in complete clipboard // request so we need to unlock. - self.renderer_state.mutex.unlock(); - defer self.renderer_state.mutex.lock(); + self.renderer_state.mutex.unlock(self.app.io); + defer self.renderer_state.mutex.lockUncancelable(self.app.io); _ = try self.startClipboardRequest(.standard, .paste); // We don't need to clear selection because we didn't have @@ -4109,8 +4108,8 @@ pub fn mouseButtonCallback( // Pasting can trigger a lock grab in complete clipboard // request so we need to unlock. - self.renderer_state.mutex.unlock(); - defer self.renderer_state.mutex.lock(); + self.renderer_state.mutex.unlock(self.app.io); + defer self.renderer_state.mutex.lockUncancelable(self.app.io); _ = try self.startClipboardRequest(.standard, .paste); }, } @@ -4123,8 +4122,8 @@ pub fn mouseButtonCallback( } fn maybePromptClick(self: *Surface) !bool { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const t: *terminal.Terminal = self.renderer_state.terminal; const screen: *terminal.Screen = t.screens.active; @@ -4397,7 +4396,8 @@ fn openUrl( // apprts to handle this themselves. log.warn("apprt did not handle open URL action, falling back to default opener", .{}); try internal_os.open( - self.alloc, + self.app.io, + self.app.environ, action.kind, action.url, ); @@ -4441,8 +4441,8 @@ pub fn mousePressureCallback( if (self.mouse.click_state[left_idx] == .press and stage == .deep) select: { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); // This should always be set in this state but we don't want // to handle state inconsistency here. @@ -4500,8 +4500,8 @@ pub fn cursorPosCallback( try self.queueRender(); } - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); // No mouse point so we don't highlight links self.renderer_state.mouse.point = null; @@ -4527,8 +4527,8 @@ pub fn cursorPosCallback( self.mouse.over_link = false; // We are reading/writing state for the remainder - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); // Stop selection scrolling when inside the viewport within a 1px buffer // for fullscreen windows, but only when selection scrolling is active. @@ -5021,8 +5021,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool // CSI/ESC triggers a scroll. { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); self.scrollToBottom() catch |err| { log.warn("error scrolling to bottom err={}", .{err}); }; @@ -5048,8 +5048,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool // Text triggers a scroll. { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); self.scrollToBottom() catch |err| { log.warn("error scrolling to bottom err={}", .{err}); }; @@ -5061,8 +5061,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool // in cursor keys mode. We're in "normal" mode if cursor // keys mode is NOT set. const normal = normal: { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); // With the lock held, we must scroll to the bottom. // We always scroll to the bottom for these inputs. @@ -5081,8 +5081,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool }, .reset => { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); self.renderer_state.terminal.fullReset(); }, @@ -5152,7 +5152,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool terminal.search.Thread.threadMain, .{&s.state}, ); - s.thread.setName("search") catch {}; + s.thread.setName(self.app.io, "search") catch {}; break :init s; }; @@ -5170,6 +5170,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool text, ) }, .forever, + self.app.io, ); s.state.wakeup.notify() catch {}; }, @@ -5182,13 +5183,14 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool .previous => .prev, } }, .forever, + self.app.io, ); s.state.wakeup.notify() catch {}; }, .copy_to_clipboard => |format| { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); if (self.io.terminal.screens.active.selection) |sel| { try self.copySelectionToClipboards( @@ -5219,8 +5221,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool if (!self.mouse.over_link) return false; const pos = try self.rt_surface.getCursorPos(); - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); if (try self.linkAtPos(pos)) |link_info| { const url_text = switch (link_info.action) { .open => url_text: { @@ -5365,8 +5367,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool // alternate screen then clear screen does nothing so we want to // return false so the keybind can be unconsumed. { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); if (self.io.terminal.screens.active_key == .alternate) return false; } @@ -5389,8 +5391,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool .scroll_to_row => |n| { { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const t: *terminal.Terminal = self.renderer_state.terminal; t.screens.active.scroll(.{ .row = n }); } @@ -5400,8 +5402,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool .scroll_to_selection => { { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const sel = self.io.terminal.screens.active.selection orelse return false; const tl = sel.topLeft(self.io.terminal.screens.active); self.io.terminal.screens.active.scroll(.{ .pin = tl }); @@ -5639,8 +5641,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool ), .select_all => { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const sel = self.io.terminal.screens.active.selectAll(); if (sel) |s| { @@ -5761,7 +5763,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool .main => @panic("crash binding action, crashing intentionally"), .render => { - _ = self.renderer_thread.mailbox.push(.{ .crash = {} }, .{ .forever = {} }); + _ = self.renderer_thread.mailbox.push(.{ .crash = {} }, .{ .forever = {} }, self.app.io); self.queueRender() catch |err| { // Not a big deal if this fails. log.warn("failed to notify renderer of crash message err={}", .{err}); @@ -5772,8 +5774,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool }, .adjust_selection => |direction| { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const screen: *terminal.Screen = self.io.terminal.screens.active; const sel = if (screen.selection) |*sel| sel else { @@ -5851,10 +5853,13 @@ fn writeScreenFile( write_screen: input.Binding.Action.WriteScreen, ) !void { // Create a temporary directory to store our scrollback. - var tmp_dir = try internal_os.TempDir.init(); - errdefer tmp_dir.deinit(); + var tmp_dir = try internal_os.TempDir.init( + self.app.io, + self.app.environ, + ); + errdefer tmp_dir.deinit(self.app.io); - var filename_buf: [std.fs.max_path_bytes]u8 = undefined; + var filename_buf: [std.Io.Dir.max_path_bytes]u8 = undefined; const filename = try std.fmt.bufPrint( &filename_buf, "{s}.{s}", @@ -5869,23 +5874,21 @@ fn writeScreenFile( // Open our scrollback file var file = try tmp_dir.dir.createFile( + self.app.io, filename, - switch (builtin.os.tag) { - .windows => .{}, - else => .{ .mode = 0o600 }, - }, + .{}, ); - defer file.close(); + defer file.close(self.app.io); // Screen.dumpString writes byte-by-byte, so buffer it var buf: [4096]u8 = undefined; - var file_writer = file.writer(&buf); + var file_writer = file.writer(self.app.io, &buf); var buf_writer = &file_writer.interface; // Write the scrollback contents. This requires a lock. { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); // We only dump history if we have history. We still keep // the file and write the empty file to the pty so that this @@ -5921,7 +5924,7 @@ fn writeScreenFile( const sel = sel_ orelse { // If we have no selection we have no data so we do nothing. - tmp_dir.deinit(); + tmp_dir.deinit(self.app.io); return; }; @@ -5947,8 +5950,9 @@ fn writeScreenFile( try buf_writer.flush(); // Get the final path - var path_buf: [std.fs.max_path_bytes]u8 = undefined; - const path = try tmp_dir.dir.realpath(filename, &path_buf); + var path_buf: [std.Io.Dir.max_path_bytes]u8 = undefined; + const path_len = try tmp_dir.dir.realPathFile(self.app.io, filename, &path_buf); + const path = path_buf[0..path_len]; switch (write_screen.action) { .copy => { @@ -6046,8 +6050,8 @@ fn completeClipboardPaste( if (data.len == 0) return; const encode_opts: input.paste.Options = encode_opts: { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); + self.renderer_state.mutex.lockUncancelable(self.app.io); + defer self.renderer_state.mutex.unlock(self.app.io); const opts: input.paste.Options = .fromTerminal(&self.io.terminal); // If we have paste protection enabled, we detect unsafe pastes and return @@ -6167,12 +6171,12 @@ fn showDesktopNotification(self: *Surface, title: [:0]const u8, body: [:0]const // how fast identical notifications can be sent sequentially. const hash_algorithm = std.hash.Wyhash; - const now = try std.time.Instant.now(); + const now = std.Io.Timestamp.now(self.app.io, .awake); // Set a limit of one desktop notification per second so that the OS // doesn't kill us when we run out of resources. if (self.app.last_notification_time) |last| { - if (now.since(last) < 1 * std.time.ns_per_s) { + if (last.durationTo(now).toNanoseconds() < 1 * std.time.ns_per_s) { log.warn("rate limiting desktop notifications", .{}); return; } @@ -6189,7 +6193,7 @@ fn showDesktopNotification(self: *Surface, title: [:0]const u8, body: [:0]const // notifications with identical content. if (self.app.last_notification_time) |last| { if (self.app.last_notification_digest == new_digest) { - if (now.since(last) < 5 * std.time.ns_per_s) { + if (last.durationTo(now).toNanoseconds() < 5 * std.time.ns_per_s) { log.warn("suppressing identical desktop notification", .{}); return; } diff --git a/src/apprt/action.zig b/src/apprt/action.zig index f6865af83dc..bc5e410c3c9 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -419,8 +419,11 @@ pub const Action = union(Key) { /// Sync with: ghostty_action_u pub const CValue = cvalue: { const key_fields = @typeInfo(Key).@"enum".fields; - var union_fields: [key_fields.len]std.builtin.Type.UnionField = undefined; - for (key_fields, 0..) |field, i| { + var names: [key_fields.len][]const u8 = undefined; + var types: [key_fields.len]type = undefined; + var attrs: [key_fields.len]std.builtin.Type.UnionField.Attributes = undefined; + + for (key_fields, &names, &types, &attrs) |field, *name, *ty, *attr| { const action = @unionInit(Action, field.name, undefined); const Type = t: { const Type = @TypeOf(@field(action, field.name)); @@ -429,19 +432,12 @@ pub const Action = union(Key) { break :t Type; }; - union_fields[i] = .{ - .name = field.name, - .type = Type, - .alignment = @alignOf(Type), - }; + name.* = field.name; + ty.* = Type; + attr.* = .{ .@"align" = @alignOf(Type) }; } - break :cvalue @Type(.{ .@"union" = .{ - .layout = .@"extern", - .tag_type = null, - .fields = &union_fields, - .decls = &.{}, - } }); + break :cvalue @Union(.@"extern", null, &names, &types, &attrs); }; /// Sync with: ghostty_action_s diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index ad20cf46529..c58c451837f 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -18,6 +18,7 @@ const terminal = @import("../terminal/main.zig"); const CoreApp = @import("../App.zig"); const CoreInspector = @import("../inspector/main.zig").Inspector; const CoreSurface = @import("../Surface.zig"); +const global_state = &@import("../global.zig").state; const configpkg = @import("../config.zig"); const Config = configpkg.Config; const String = @import("../main_c.zig").String; @@ -333,7 +334,7 @@ pub const App = struct { _: apprt.ipc.Target, comptime action: apprt.ipc.Action.Key, _: apprt.ipc.Action.Value(action), - ) (Allocator.Error || std.posix.WriteError || apprt.ipc.Errors)!bool { + ) (Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors)!bool { switch (action) { .new_window => return false, } @@ -371,7 +372,8 @@ pub const Platform = union(PlatformTag) { /// Initialize a Platform a tag and configuration from the C ABI. pub fn init(tag_int: c_int, c_platform: C) !Platform { - const tag = try std.meta.intToEnum(PlatformTag, tag_int); + const tag = std.enums.fromInt(PlatformTag, tag_int) orelse + return error.UnsupportedPlatform; return switch (tag) { .macos => if (MacOS != void) macos: { const config = c_platform.macos; @@ -489,16 +491,16 @@ pub const Surface = struct { if (opts.working_directory) |c_wd| { const wd = std.mem.sliceTo(c_wd, 0); if (wd.len > 0) wd: { - var dir = std.fs.openDirAbsolute(wd, .{}) catch |err| { + var dir = std.Io.Dir.openDirAbsolute(app.io, wd, .{}) catch |err| { log.warn( "error opening requested working directory dir={s} err={}", .{ wd, err }, ); break :wd; }; - defer dir.close(); + defer dir.close(app.io); - const stat = dir.stat() catch |err| { + const stat = dir.stat(app.io) catch |err| { log.warn( "failed to stat requested working directory dir={s} err={}", .{ wd, err }, @@ -515,7 +517,7 @@ pub const Surface = struct { } var wd_val: configpkg.WorkingDirectory = .{ .path = wd }; - if (wd_val.finalize(config.arenaAlloc())) |_| { + if (wd_val.finalize(config.arenaAlloc(), app.io, &global_state.environ_map)) |_| { config.@"working-directory" = wd_val; } else |err| { log.warn( @@ -949,9 +951,9 @@ pub const Surface = struct { }; } - pub fn defaultTermioEnv(self: *const Surface) !std.process.EnvMap { + pub fn defaultTermioEnv(self: *const Surface) !std.process.Environ.Map { const alloc = self.app.core_app.alloc; - var env = try internal_os.getEnvMap(alloc); + var env = try internal_os.getSurfaceEnvMap(alloc, &global_state.environ_map); errdefer env.deinit(); if (comptime builtin.target.os.tag.isDarwin()) { @@ -999,7 +1001,7 @@ pub const Inspector = struct { content_scale: f64 = 1, /// Our previous instant used to calculate delta time for animations. - instant: ?std.time.Instant = null, + instant: ?std.Io.Timestamp = null, const Backend = enum { metal, @@ -1064,6 +1066,7 @@ pub const Inspector = struct { pub fn renderMetal( self: *Inspector, + io: std.Io, command_buffer: objc.Object, desc: objc.Object, ) !void { @@ -1079,7 +1082,7 @@ pub const Inspector = struct { // this. for (0..2) |_| { cimgui.ImGui_ImplMetal_NewFrame(desc.value); - try self.newFrame(); + try self.newFrame(io); cimgui.c.ImGui_NewFrame(); // Build our UI @@ -1224,13 +1227,13 @@ pub const Inspector = struct { } } - fn newFrame(self: *Inspector) !void { - const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + fn newFrame(self: *Inspector, io: std.Io) !void { + const cio: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); // Determine our delta time - const now = try std.time.Instant.now(); - io.DeltaTime = if (self.instant) |prev| delta: { - const since_ns: f64 = @floatFromInt(now.since(prev)); + const now = std.Io.Timestamp.now(io, .awake); + cio.DeltaTime = if (self.instant) |prev| delta: { + const since_ns: f64 = @floatFromInt(prev.durationTo(now).toNanoseconds()); const ns_per_s: f64 = @floatFromInt(std.time.ns_per_s); const since_s: f32 = @floatCast(since_ns / ns_per_s); break :delta @max(0.00001, since_s); @@ -1409,7 +1412,7 @@ pub const CAPI = struct { opts: *const apprt.runtime.App.Options, config: *const Config, ) !*App { - const core_app = try CoreApp.create(global.alloc); + const core_app = try CoreApp.create(global.alloc, global.io(), &global.environ_map); errdefer core_app.destroy(); // Create our runtime app @@ -1519,7 +1522,7 @@ pub const CAPI = struct { /// Update the color scheme of the app. export fn ghostty_app_set_color_scheme(v: *App, scheme_raw: c_int) void { - const scheme = std.meta.intToEnum(apprt.ColorScheme, scheme_raw) catch { + const scheme = std.enums.fromInt(apprt.ColorScheme, scheme_raw) orelse { log.warn( "invalid color scheme to ghostty_surface_set_color_scheme value={}", .{scheme_raw}, @@ -1611,8 +1614,8 @@ pub const CAPI = struct { result: *Text, ) bool { const core_surface = &surface.core_surface; - core_surface.renderer_state.mutex.lock(); - defer core_surface.renderer_state.mutex.unlock(); + core_surface.renderer_state.mutex.lockUncancelable(global.io()); + defer core_surface.renderer_state.mutex.unlock(global.io()); // If we don't have a selection, do nothing. const core_sel = core_surface.io.terminal.screens.active.selection orelse return false; @@ -1631,8 +1634,8 @@ pub const CAPI = struct { sel: Selection, result: *Text, ) bool { - surface.core_surface.renderer_state.mutex.lock(); - defer surface.core_surface.renderer_state.mutex.unlock(); + surface.core_surface.renderer_state.mutex.lockUncancelable(global.io()); + defer surface.core_surface.renderer_state.mutex.unlock(global.io()); const core_sel = sel.core( surface.core_surface.renderer_state.terminal.screens.active, @@ -1729,7 +1732,7 @@ pub const CAPI = struct { /// Update the color scheme of the surface. export fn ghostty_surface_set_color_scheme(surface: *Surface, scheme_raw: c_int) void { - const scheme = std.meta.intToEnum(apprt.ColorScheme, scheme_raw) catch { + const scheme = std.enums.fromInt(apprt.ColorScheme, scheme_raw) orelse { log.warn( "invalid color scheme to ghostty_surface_set_color_scheme value={}", .{scheme_raw}, @@ -1890,16 +1893,7 @@ pub const CAPI = struct { stage_raw: u32, pressure: f64, ) void { - const stage = std.meta.intToEnum( - input.MousePressureStage, - stage_raw, - ) catch { - log.warn( - "invalid mouse pressure stage value={}", - .{stage_raw}, - ); - return; - }; + const stage = std.enums.fromInt(input.MousePressureStage, stage_raw) orelse return; surface.mousePressureCallback(stage, pressure); } @@ -2194,8 +2188,8 @@ pub const CAPI = struct { result: *Text, ) bool { const surface = &ptr.core_surface; - surface.renderer_state.mutex.lock(); - defer surface.renderer_state.mutex.unlock(); + surface.renderer_state.mutex.lockUncancelable(global.io()); + defer surface.renderer_state.mutex.unlock(global.io()); // Get our word selection const sel = sel: { @@ -2232,6 +2226,7 @@ pub const CAPI = struct { ) void { return ptr.renderMetal( .fromId(command_buffer), + global_state.io(), .fromId(descriptor), ) catch |err| { log.err("error rendering inspector err={}", .{err}); diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 715973671c0..c5ba39277f8 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -94,7 +94,7 @@ pub fn setClipboard( ); } -pub fn defaultTermioEnv(self: *Self) !std.process.EnvMap { +pub fn defaultTermioEnv(self: *Self) !std.process.Environ.Map { return try self.surface.defaultTermioEnv(); } diff --git a/src/apprt/gtk/build/blueprint.zig b/src/apprt/gtk/build/blueprint.zig index 4920ce6f8ab..85fb79bc479 100644 --- a/src/apprt/gtk/build/blueprint.zig +++ b/src/apprt/gtk/build/blueprint.zig @@ -6,10 +6,7 @@ //! Example: blueprint.zig 1 5 output.ui input.blp const std = @import("std"); - -pub const c = @cImport({ - @cInclude("adwaita.h"); -}); +const adw = @import("adw"); pub const blueprint_compiler_help = \\ @@ -25,31 +22,29 @@ pub const blueprint_compiler_help = \\more information on the recommended build instructions. ; -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 debug_allocator: std.heap.DebugAllocator(.{}) = .init; - defer _ = debug_allocator.deinit(); - const alloc = debug_allocator.allocator(); - +pub fn main(init: std.process.Init) !void { + const alloc = init.arena.allocator(); // Get our args - var it = try std.process.argsWithAllocator(alloc); + var it = try init.minimal.args.iterateAllocator(alloc); defer it.deinit(); + _ = it.next(); // Skip argv0 const arg_major = it.next() orelse return error.NoMajorVersion; const arg_minor = it.next() orelse return error.NoMinorVersion; const output = it.next() orelse return error.NoOutput; const input = it.next() orelse return error.NoInput; + const adwaita_version = std.SemanticVersion{ + .major = adw.getMajorVersion(), + .minor = adw.getMinorVersion(), + .patch = adw.getMicroVersion(), + }; const required_adwaita_version = std.SemanticVersion{ .major = try std.fmt.parseUnsigned(u8, arg_major, 10), .minor = try std.fmt.parseUnsigned(u8, arg_minor, 10), @@ -63,51 +58,33 @@ pub fn main() !void { \\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); + std.process.exit(1); } // Version checks { - 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", - "--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) { + const blueprint_compiler = std.process.run(alloc, init.io, .{ + .argv = &.{ "blueprint-compiler", "--version" }, + }) catch |err| switch (err) { error.FileNotFound => { std.debug.print( \\`blueprint-compiler` not found. ++ blueprint_compiler_help, .{required_blueprint_version}, ); - std.posix.exit(1); + std.process.exit(1); }, else => return err, }; - switch (term) { - .Exited => |rc| if (rc != 0) std.process.exit(1), + + switch (blueprint_compiler.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, + blueprint_compiler.stdout, &std.ascii.whitespace, )); if (version.order(required_blueprint_version) == .lt) { @@ -116,57 +93,41 @@ pub fn main() !void { ++ blueprint_compiler_help, .{required_blueprint_version}, ); - std.posix.exit(1); + std.process.exit(1); } } // Compilation { - 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( - &.{ + const blueprint_compiler = std.process.run(alloc, init.io, .{ + .argv = &.{ "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) { + }) catch |err| switch (err) { error.FileNotFound => { std.debug.print( \\`blueprint-compiler` not found. ++ blueprint_compiler_help, .{required_blueprint_version}, ); - std.posix.exit(1); + std.process.exit(1); }, else => return err, }; - switch (term) { - .Exited => |rc| { + switch (blueprint_compiler.term) { + .exited => |rc| { if (rc != 0) { - std.debug.print("{s}", .{stderr.items}); + std.debug.print("{s}", .{blueprint_compiler.stderr}); std.process.exit(1); } }, else => { - std.debug.print("{s}", .{stderr.items}); + std.debug.print("{s}", .{blueprint_compiler.stderr}); std.process.exit(1); }, } diff --git a/src/apprt/gtk/build/gresource.zig b/src/apprt/gtk/build/gresource.zig index c50ea8cd5ca..57c32a3532d 100644 --- a/src/apprt/gtk/build/gresource.zig +++ b/src/apprt/gtk/build/gresource.zig @@ -122,18 +122,17 @@ pub fn blueprint(comptime bp: Blueprint) [:0]const u8 { } } -pub fn main() !void { - var debug_allocator: std.heap.DebugAllocator(.{}) = .init; - defer _ = debug_allocator.deinit(); - const alloc = debug_allocator.allocator(); +pub fn main(init: std.process.Init) !void { + const alloc = init.arena.allocator(); // Collect the UI files that are passed in as arguments. - var ui_files: std.ArrayListUnmanaged([]const u8) = .empty; + var ui_files: std.ArrayList([]const u8) = .empty; defer { for (ui_files.items) |item| alloc.free(item); ui_files.deinit(alloc); } - var it = try std.process.argsWithAllocator(alloc); + + var it = try init.minimal.args.iterateAllocator(alloc); defer it.deinit(); while (it.next()) |arg| { if (!std.mem.endsWith(u8, arg, ".ui")) continue; @@ -144,7 +143,7 @@ pub fn main() !void { } var buf: [4096]u8 = undefined; - var stdout = std.fs.File.stdout().writer(&buf); + var stdout = std.Io.File.stdout().writer(init.io, &buf); const writer = &stdout.interface; try writer.writeAll( \\ @@ -152,8 +151,8 @@ pub fn main() !void { \\ ); - try genRoot(writer); - try genIcons(writer); + try genRoot(init.io, writer); + try genIcons(init.io, writer); try genUi(alloc, writer, &ui_files); try writer.writeAll( @@ -167,19 +166,19 @@ pub fn main() !void { /// Generate the icon resources. This works by looking up all the icons /// specified by `icon_sizes` in `images/icons/`. They are asserted to exist /// by trying to access the file. -fn genIcons(writer: *std.Io.Writer) !void { +fn genIcons(io: std.Io, writer: *std.Io.Writer) !void { try writer.print( \\ \\ , .{build_info.resource_path}); - const cwd = std.fs.cwd(); + const cwd: std.Io.Dir = .cwd(); inline for (icon_sizes) |size| { // 1x { const alias = std.fmt.comptimePrint("{d}x{d}", .{ size, size }); const source = std.fmt.comptimePrint("images/gnome/{d}.png", .{size}); - try cwd.access(source, .{}); + try cwd.access(io, source, .{}); try writer.print( \\ {s} \\ @@ -192,7 +191,7 @@ fn genIcons(writer: *std.Io.Writer) !void { { const alias = std.fmt.comptimePrint("{d}x{d}@2", .{ size, size }); const source = std.fmt.comptimePrint("images/gnome/{d}.png", .{size * 2}); - try cwd.access(source, .{}); + try cwd.access(io, source, .{}); try writer.print( \\ {s} \\ @@ -209,19 +208,19 @@ fn genIcons(writer: *std.Io.Writer) !void { } /// Generate the resources at the root prefix. -fn genRoot(writer: *std.Io.Writer) !void { +fn genRoot(io: std.Io, writer: *std.Io.Writer) !void { try writer.print( \\ \\ , .{build_info.resource_path}); - const cwd = std.fs.cwd(); + const cwd: std.Io.Dir = .cwd(); inline for (css) |name| { const source = std.fmt.comptimePrint( "{s}/{s}", .{ css_path, name }, ); - try cwd.access(source, .{}); + try cwd.access(io, source, .{}); try writer.print( \\ {s} \\ @@ -242,7 +241,7 @@ fn genRoot(writer: *std.Io.Writer) !void { fn genUi( alloc: Allocator, writer: *std.Io.Writer, - files: *const std.ArrayListUnmanaged([]const u8), + files: *const std.ArrayList([]const u8), ) !void { try writer.print( \\ diff --git a/src/apprt/gtk/class.zig b/src/apprt/gtk/class.zig index 942666cf487..dbf3ae8d105 100644 --- a/src/apprt/gtk/class.zig +++ b/src/apprt/gtk/class.zig @@ -145,16 +145,24 @@ pub fn Common( /// as the virtual method but the self parameter points to the /// target instead of the original class. fn ImplementFunc(comptime T: type) type { - var params: [fn_info.params.len]std.builtin.Type.Fn.Param = undefined; - @memcpy(¶ms, fn_info.params); - params[0].type = *ClassInstance(T); - return @Type(.{ .@"fn" = .{ - .calling_convention = fn_info.calling_convention, - .is_generic = fn_info.is_generic, - .is_var_args = fn_info.is_var_args, - .return_type = fn_info.return_type, - .params = ¶ms, - } }); + var types: [fn_info.params.len]type = undefined; + var attrs: [fn_info.params.len]std.builtin.Type.Fn.Param.Attributes = undefined; + + for (fn_info.params, &types, &attrs) |info, *ty, *attr| { + ty.* = info.type.?; + attr.* = .{ .@"noalias" = info.is_noalias }; + } + types[0] = *ClassInstance(T); + + return @Fn( + types, + &attrs, + fn_info.return_type.?, + .{ + .@"callconv" = fn_info.calling_convention, + .varargs = fn_info.is_var_args, + }, + ); } }; } diff --git a/src/apprt/gtk/class/application.zig b/src/apprt/gtk/class/application.zig index 873674cecd6..4728a43da2e 100644 --- a/src/apprt/gtk/class/application.zig +++ b/src/apprt/gtk/class/application.zig @@ -23,7 +23,6 @@ const xev = @import("../../../global.zig").xev; const Binding = @import("../../../input.zig").Binding; const CoreConfig = configpkg.Config; const CoreSurface = @import("../../../Surface.zig"); -const lib = @import("../../../lib/main.zig"); const ext = @import("../ext.zig"); const key = @import("../key.zig"); @@ -209,7 +208,7 @@ pub const Application = extern struct { css_provider: *gtk.CssProvider, /// Providers for loading custom stylesheets defined by user - custom_css_providers: std.ArrayListUnmanaged(*gtk.CssProvider) = .empty, + custom_css_providers: std.ArrayList(*gtk.CssProvider) = .empty, /// A copy of the LANG environment variable that was provided to Ghostty /// by the system. If this is null, the LANG environment variable did @@ -273,8 +272,8 @@ pub const Application = extern struct { const saved_language: ?[:0]const u8 = saved_language: { const old_language = old_language: { - const result = (internal_os.getenv(alloc, "LANG") catch break :old_language null) orelse break :old_language null; - defer result.deinit(alloc); + const env = core_app.environ; + const result = env.get("LANG") orelse break :old_language null; break :old_language alloc.dupeZ(u8, result.value) catch break :old_language null; }; @@ -1060,7 +1059,7 @@ pub const Application = extern struct { } } - fn loadCustomCss(self: *Self) (std.fs.File.ReadError || Allocator.Error)!void { + fn loadCustomCss(self: *Self) (std.Io.File.ReadError || Allocator.Error)!void { const priv: *Private = self.private(); const alloc = self.allocator(); const display = gdk.Display.getDefault() orelse { @@ -1084,7 +1083,8 @@ pub const Application = extern struct { .optional => |path| .{ path, true }, .required => |path| .{ path, false }, }; - const file = std.fs.openFileAbsolute(path, .{}) catch |err| { + const io = Application.default().core().io; + const file = std.Io.Dir.openFileAbsolute(io, path, .{}) catch |err| { if (err != error.FileNotFound or !optional) { log.warn( "error opening gtk-custom-css file {s}: {}", @@ -1752,7 +1752,7 @@ pub const Application = extern struct { continue; } - if (lib.cutPrefix(u8, str, "--command=")) |v| { + if (std.mem.cutPrefix(u8, str, "--command=")) |v| { var cmd: configpkg.Command = undefined; cmd.parseCLI(alloc, v) catch |err| { log.warn("unable to parse command: {t}", .{err}); @@ -1761,14 +1761,14 @@ pub const Application = extern struct { command = cmd; continue; } - if (lib.cutPrefix(u8, str, "--working-directory=")) |v| { + if (std.mem.cutPrefix(u8, str, "--working-directory=")) |v| { working_directory = alloc.dupeZ(u8, std.mem.trim(u8, v, &std.ascii.whitespace)) catch |err| wd: { log.warn("unable to duplicate working directory: {t}", .{err}); break :wd null; }; continue; } - if (lib.cutPrefix(u8, str, "--title=")) |v| { + if (std.mem.cutPrefix(u8, str, "--title=")) |v| { title = alloc.dupeZ(u8, std.mem.trim(u8, v, &std.ascii.whitespace)) catch |err| t: { log.warn("unable to duplicate title: {t}", .{err}); break :t null; diff --git a/src/apprt/gtk/class/command_palette.zig b/src/apprt/gtk/class/command_palette.zig index 6101e82e807..0d66bbe9450 100644 --- a/src/apprt/gtk/class/command_palette.zig +++ b/src/apprt/gtk/class/command_palette.zig @@ -153,7 +153,7 @@ pub const CommandPalette = extern struct { priv.source.removeAll(); const alloc = Application.default().allocator(); - var commands: std.ArrayList(*Command) = .{}; + var commands: std.ArrayList(*Command) = .empty; defer { for (commands.items) |cmd| cmd.unref(); commands.deinit(alloc); diff --git a/src/apprt/gtk/class/imgui_widget.zig b/src/apprt/gtk/class/imgui_widget.zig index 0ef753a87d5..4c61ec16574 100644 --- a/src/apprt/gtk/class/imgui_widget.zig +++ b/src/apprt/gtk/class/imgui_widget.zig @@ -61,13 +61,13 @@ pub const ImguiWidget = extern struct { ig_context: ?*cimgui.c.ImGuiContext = null, /// Our previous instant used to calculate delta time for animations. - instant: ?std.time.Instant = null, + instant: ?std.Io.Timestamp = null, /// Tick callback ID for timed updates. tick_callback_id: c_uint = 0, /// Last render time for throttling to 30 FPS. - last_render_time: ?std.time.Instant = null, + last_render_time: ?std.Io.Timestamp = null, pub var offset: c_int = 0; }; @@ -141,7 +141,7 @@ pub const ImguiWidget = extern struct { const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); // Determine our delta time - const now = std.time.Instant.now() catch unreachable; + const now = std.Io.Timestamp.now() catch unreachable; io.DeltaTime = if (priv.instant) |prev| delta: { const since_ns: f64 = @floatFromInt(now.since(prev)); const ns_per_s: f64 = @floatFromInt(std.time.ns_per_s); @@ -298,7 +298,7 @@ pub const ImguiWidget = extern struct { // Update last render time for tick callback throttling. const priv = self.private(); - priv.last_render_time = std.time.Instant.now() catch null; + priv.last_render_time = std.Io.Timestamp.now() catch null; // Setup our frame. We render twice because some ImGui behaviors // take multiple renders to process. I don't know how to make this @@ -457,7 +457,7 @@ pub const ImguiWidget = extern struct { const self: *Self = gobject.ext.cast(Self, widget) orelse return 0; const priv = self.private(); - const now = std.time.Instant.now() catch { + const now = std.Io.Timestamp.now() catch { self.queueRender(); return 1; }; diff --git a/src/apprt/gtk/class/surface.zig b/src/apprt/gtk/class/surface.zig index 179c779d7be..e0370931e3b 100644 --- a/src/apprt/gtk/class/surface.zig +++ b/src/apprt/gtk/class/surface.zig @@ -678,8 +678,8 @@ pub const Surface = extern struct { vadj_signal_group: ?*gobject.SignalGroup = null, // Key state tracking for key sequences and tables - key_sequence: std.ArrayListUnmanaged([:0]const u8) = .empty, - key_tables: std.ArrayListUnmanaged([:0]const u8) = .empty, + key_sequence: std.ArrayList([:0]const u8) = .empty, + key_tables: std.ArrayList([:0]const u8) = .empty, // Template binds child_exited_overlay: *ChildExited, @@ -1561,7 +1561,7 @@ pub const Surface = extern struct { return self.private().cursor_pos; } - pub fn defaultTermioEnv(self: *Self) !std.process.EnvMap { + pub fn defaultTermioEnv(self: *Self) !std.process.Environ.Map { const app = Application.default(); const alloc = app.allocator(); var env = try internal_os.getEnvMap(alloc); @@ -1613,7 +1613,7 @@ pub const Surface = extern struct { } /// Filter out environment variables that start with forbidden prefixes. - fn filterSnapPaths(gpa: std.mem.Allocator, env_map: *std.process.EnvMap) !void { + fn filterSnapPaths(gpa: std.mem.Allocator, env_map: *std.process.Environ.Map) !void { comptime assert(build_config.snap); const snap_vars = [_][]const u8{ diff --git a/src/apprt/gtk/flatpak.zig b/src/apprt/gtk/flatpak.zig index dc47c671bd5..523ae50fd0b 100644 --- a/src/apprt/gtk/flatpak.zig +++ b/src/apprt/gtk/flatpak.zig @@ -20,7 +20,7 @@ pub fn resourcesDir(alloc: Allocator) !internal_os.ResourcesDir { const app_dir = std.mem.span(keyfile.getString("Instance", "app-path", null)) orelse return result; defer glib.free(app_dir.ptr); - result.host_path = try std.fs.path.join(alloc, &[_][]const u8{ app_dir, "share", "ghostty" }); + result.host_path = try std.Io.Dir.path.join(alloc, &[_][]const u8{ app_dir, "share", "ghostty" }); return result; } } diff --git a/src/apprt/gtk/ipc/DBus.zig b/src/apprt/gtk/ipc/DBus.zig index fa4a6723eee..dbeec8f5ebc 100644 --- a/src/apprt/gtk/ipc/DBus.zig +++ b/src/apprt/gtk/ipc/DBus.zig @@ -31,7 +31,7 @@ parameters_builder: *glib.VariantBuilder, /// Initialize the helper. pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) (Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors)!Self { var buf: [256]u8 = undefined; - var stderr_writer = std.fs.File.stderr().writer(&buf); + var stderr_writer = std.Io.File.stderr().writer(&buf); const stderr = &stderr_writer.interface; // Get the appropriate bus name and object path for contacting the @@ -133,7 +133,7 @@ pub fn addParameter(self: *Self, variant: *glib.Variant) void { /// should be done with this object other than call `deinit`. pub fn send(self: *Self) (std.Io.Writer.Error || apprt.ipc.Errors)!void { var buf: [256]u8 = undefined; - var stderr_writer = std.fs.File.stderr().writer(&buf); + var stderr_writer = std.Io.File.stderr().writer(&buf); const stderr = &stderr_writer.interface; // finish building the parameters diff --git a/src/apprt/gtk/media.zig b/src/apprt/gtk/media.zig index 1015c933f0e..da781ab24d5 100644 --- a/src/apprt/gtk/media.zig +++ b/src/apprt/gtk/media.zig @@ -8,9 +8,9 @@ const glib = @import("glib"); const gobject = @import("gobject"); const gtk = @import("gtk"); -pub fn fromFilename(path: [:0]const u8) ?*gtk.MediaFile { - assert(std.fs.path.isAbsolute(path)); - std.fs.accessAbsolute(path, .{ .mode = .read_only }) catch |err| { +pub fn fromFilename(io: std.Io, path: [:0]const u8) ?*gtk.MediaFile { + assert(std.Io.Dir.path.isAbsolute(path)); + std.Io.Dir.accessAbsolute(io, path, .{ .mode = .read_only }) catch |err| { log.warn("unable to access {s}: {t}", .{ path, err }); return null; }; @@ -18,7 +18,7 @@ pub fn fromFilename(path: [:0]const u8) ?*gtk.MediaFile { } pub fn fromResource(path: [:0]const u8) ?*gtk.MediaFile { - assert(std.fs.path.isAbsolute(path)); + assert(std.Io.Dir.path.isAbsolute(path)); var gerr: ?*glib.Error = null; const found = gio.resourcesGetInfo(path, .{}, null, null, &gerr); diff --git a/src/apprt/gtk/portal/OpenURI.zig b/src/apprt/gtk/portal/OpenURI.zig index 97aa013e55a..3c05d9a9712 100644 --- a/src/apprt/gtk/portal/OpenURI.zig +++ b/src/apprt/gtk/portal/OpenURI.zig @@ -23,7 +23,7 @@ app: *App, dbus: ?*gio.DBusConnection = null, /// Mutex to protect modification of the entries map or the cleanup timer. -mutex: std.Thread.Mutex = .{}, +mutex: std.Io.Mutex = .init, /// Map to store data about any in-flight calls to the portal. entries: std.AutoArrayHashMapUnmanaged(usize, *Entry) = .empty, @@ -78,7 +78,7 @@ const RequestData = struct { /// Data about any in-flight calls to the portal. pub const Entry = struct { /// When the request started. - start: std.time.Instant, + start: std.Io.Timestamp, /// A token used by the portal to identify requests and responses. The /// actual format of the token does not really matter as long as it can be /// used as part of a D-Bus object path. `usize` was chosen since it's easy @@ -166,7 +166,7 @@ pub fn start(self: *OpenURI, value: apprt.action.OpenUrl) (Allocator.Error || Er const entry = try alloc.create(Entry); errdefer alloc.destroy(entry); entry.* = .{ - .start = std.time.Instant.now() catch return error.TimerUnavailable, + .start = std.Io.Timestamp.now() catch return error.TimerUnavailable, .token = token, .kind = value.kind, .uri = try alloc.dupeZ(u8, value.url), @@ -540,7 +540,7 @@ fn cleanup(ud: ?*anyopaque) callconv(.c) c_int { self.cleanup_timer = null; if (!self.alive) return @intFromBool(glib.SOURCE_REMOVE); - const now = std.time.Instant.now() catch { + const now = std.Io.Timestamp.now() catch { // `now()` should never fail, but if it does, don't crash, just return. // This might cause a small memory leak in rare circumstances but it // should get cleaned up the next time a URL is clicked. diff --git a/src/apprt/gtk/post_fork.zig b/src/apprt/gtk/post_fork.zig index ff0219508cb..7c20043d24f 100644 --- a/src/apprt/gtk/post_fork.zig +++ b/src/apprt/gtk/post_fork.zig @@ -85,10 +85,10 @@ pub fn postFork(cmd: *Command) Command.PostForkError!void { return; }; - const start = std.time.Instant.now() catch unreachable; + const start = std.Io.Timestamp.now() catch unreachable; loop: while (true) { - const now = std.time.Instant.now() catch unreachable; + const now = std.Io.Timestamp.now() catch unreachable; if (now.since(start) > 250 * std.time.ns_per_ms) { if (cmd.rt_pre_exec_info.linux_cgroup_hard_fail) { diff --git a/src/apprt/gtk/pre_exec.zig b/src/apprt/gtk/pre_exec.zig index 6f6a9ed51ec..2f7f18b0d6f 100644 --- a/src/apprt/gtk/pre_exec.zig +++ b/src/apprt/gtk/pre_exec.zig @@ -47,10 +47,10 @@ pub fn preExec(cmd: *Command) ?u8 { var expected_cgroup_buf: [256]u8 = undefined; const expected_cgroup = cgroup.fmtScope(&expected_cgroup_buf, pid); - const start = std.time.Instant.now() catch unreachable; + const start = std.Io.Timestamp.now() catch unreachable; while (true) { - const now = std.time.Instant.now() catch unreachable; + const now = std.Io.Timestamp.now() catch unreachable; if (now.since(start) > 250 * std.time.ns_per_ms) { if (cmd.rt_pre_exec_info.linux_cgroup_hard_fail) { diff --git a/src/apprt/gtk/winproto.zig b/src/apprt/gtk/winproto.zig index d409d6c2688..a6bc14d964e 100644 --- a/src/apprt/gtk/winproto.zig +++ b/src/apprt/gtk/winproto.zig @@ -141,7 +141,7 @@ pub const Window = union(Protocol) { }; } - pub fn addSubprocessEnv(self: *Window, env: *std.process.EnvMap) !void { + pub fn addSubprocessEnv(self: *Window, env: *std.process.Environ.Map) !void { switch (self.*) { inline else => |*v| try v.addSubprocessEnv(env), } diff --git a/src/apprt/gtk/winproto/noop.zig b/src/apprt/gtk/winproto/noop.zig index 950ee0f373a..3b9a3fbc052 100644 --- a/src/apprt/gtk/winproto/noop.zig +++ b/src/apprt/gtk/winproto/noop.zig @@ -67,7 +67,7 @@ pub const Window = struct { return true; } - pub fn addSubprocessEnv(_: *Window, _: *std.process.EnvMap) !void {} + pub fn addSubprocessEnv(_: *Window, _: *std.process.Environ.Map) !void {} pub fn setUrgent(_: *Window, _: bool) !void {} }; diff --git a/src/apprt/gtk/winproto/wayland.zig b/src/apprt/gtk/winproto/wayland.zig index 12c7fb8a218..a53035869e4 100644 --- a/src/apprt/gtk/winproto/wayland.zig +++ b/src/apprt/gtk/winproto/wayland.zig @@ -225,7 +225,7 @@ pub const Window = struct { }; } - pub fn addSubprocessEnv(self: *Window, env: *std.process.EnvMap) !void { + pub fn addSubprocessEnv(self: *Window, env: *std.process.Environ.Map) !void { _ = self; _ = env; } diff --git a/src/apprt/gtk/winproto/x11.zig b/src/apprt/gtk/winproto/x11.zig index 8109959dafb..3ef33137a89 100644 --- a/src/apprt/gtk/winproto/x11.zig +++ b/src/apprt/gtk/winproto/x11.zig @@ -317,7 +317,7 @@ pub const Window = struct { self.last_applied_decoration_hints = hints; } - pub fn addSubprocessEnv(self: *Window, env: *std.process.EnvMap) !void { + pub fn addSubprocessEnv(self: *Window, env: *std.process.Environ.Map) !void { var buf: [64]u8 = undefined; const window_id = try std.fmt.bufPrint( &buf, diff --git a/src/apprt/ipc.zig b/src/apprt/ipc.zig index b37647e0212..e4a3e41d1b9 100644 --- a/src/apprt/ipc.zig +++ b/src/apprt/ipc.zig @@ -122,8 +122,12 @@ pub const Action = union(enum) { /// Sync with: ghostty_ipc_action_u pub const CValue = cvalue: { const key_fields = @typeInfo(Key).@"enum".fields; - var union_fields: [key_fields.len]std.builtin.Type.UnionField = undefined; - for (key_fields, 0..) |field, i| { + + var names: [key_fields.len][]const u8 = undefined; + var types: [key_fields.len]type = undefined; + var attrs: [key_fields.len]std.builtin.Type.UnionField.Attributes = undefined; + + for (key_fields, &names, &types, &attrs) |field, *name, *ty, *attr| { const action = @unionInit(Action, field.name, undefined); const Type = t: { const Type = @TypeOf(@field(action, field.name)); @@ -131,20 +135,12 @@ pub const Action = union(enum) { if (Type != void and @hasDecl(Type, "C")) break :t Type.C; break :t Type; }; - - union_fields[i] = .{ - .name = field.name, - .type = Type, - .alignment = @alignOf(Type), - }; + name.* = field.name; + ty.* = Type; + attr.* = .{ .@"align" = @alignOf(Type) }; } - break :cvalue @Type(.{ .@"union" = .{ - .layout = .@"extern", - .tag_type = null, - .fields = &union_fields, - .decls = &.{}, - } }); + break :cvalue @Union(.@"extern", null, &names, &types, &attrs); }; /// Sync with: ghostty_ipc_action_s diff --git a/src/benchmark/Benchmark.zig b/src/benchmark/Benchmark.zig index 41c3695d40e..e2425d7b428 100644 --- a/src/benchmark/Benchmark.zig +++ b/src/benchmark/Benchmark.zig @@ -28,6 +28,7 @@ pub fn init( /// Run the benchmark. pub fn run( self: Benchmark, + io: std.Io, mode: RunMode, ) Error!RunResult { // Run our setup function if it exists. We do this first because @@ -64,21 +65,21 @@ pub fn run( signpost.log.release(); }; - const start = std.time.Instant.now() catch return error.BenchmarkFailed; + const start: std.Io.Timestamp = .now(io, .awake); while (true) { // Run our step function. If it fails, we return the error. try self.vtable.stepFn(self.ptr); result.iterations += 1; // Get our current monotonic time and check our exit conditions. - const now = std.time.Instant.now() catch return error.BenchmarkFailed; + const now: std.Io.Timestamp = .now(io, .awake); const exit = switch (mode) { .once => true, - .duration => |ns| now.since(start) >= ns, + .duration => |ns| start.durationTo(now).nanoseconds >= ns, }; if (exit) { - result.duration = now.since(start); + result.duration = start.durationTo(now).nanoseconds; return result; } } @@ -96,7 +97,7 @@ pub const RunMode = union(enum) { /// Run the benchmark for a fixed duration in nanoseconds. This /// will not interrupt a running step so if the granularity of the /// duration is too low, benchmark results may be inaccurate. - duration: u64, + duration: i96, }; /// The result of a benchmark run. @@ -108,7 +109,7 @@ pub const RunResult = struct { /// The total time taken for the run. For "duration" run modes /// this will be relatively close to the requested duration. /// The units are nanoseconds. - duration: u64 = 0, + duration: i96 = 0, }; /// The possible errors that can occur during various stages of the @@ -122,7 +123,7 @@ pub const VTable = struct { /// testing throughput. stepFn: *const fn (ptr: *anyopaque) Error!void, - /// Setup and teardown functions. These are called once before + /// Setup and teardown functns. These are called once before /// the first step and once after the last step. They are not part /// of the benchmark results (unless you're benchmarking the full /// binary). diff --git a/src/benchmark/CApi.zig b/src/benchmark/CApi.zig index 3bef8b2698d..d7516167064 100644 --- a/src/benchmark/CApi.zig +++ b/src/benchmark/CApi.zig @@ -21,6 +21,8 @@ export fn ghostty_benchmark_cli( cli.mainAction( state.alloc, action, + state.io(), + &state.environ_map, .{ .string = std.mem.sliceTo(args, 0) }, ) catch |err| { log.warn("failed to run action={s} err={}", .{ diff --git a/src/benchmark/CodepointWidth.zig b/src/benchmark/CodepointWidth.zig index 30d3f91e75f..426a0cb75c0 100644 --- a/src/benchmark/CodepointWidth.zig +++ b/src/benchmark/CodepointWidth.zig @@ -17,10 +17,11 @@ const table = @import("../unicode/main.zig").table; const log = std.log.scoped(.@"terminal-stream-bench"); +io: std.Io, opts: Options, /// The file, opened in the setup function. -data_f: ?std.fs.File = null, +data_f: ?std.Io.File = null, pub const Options = struct { /// The type of codepoint width calculation to use. @@ -53,11 +54,14 @@ pub const Mode = enum { /// Create a new terminal stream handler for the given arguments. pub fn create( alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, opts: Options, ) !*CodepointWidth { + _ = env; const ptr = try alloc.create(CodepointWidth); errdefer alloc.destroy(ptr); - ptr.* = .{ .opts = opts }; + ptr.* = .{ .io = io, .opts = opts }; return ptr; } @@ -84,7 +88,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { // Open our data file to prepare for reading. We can do more // validation here eventually. assert(self.data_f == null); - self.data_f = options.dataFile(self.opts.data) catch |err| { + self.data_f = options.dataFile(self.io, self.opts.data) catch |err| { log.warn("error opening data file err={}", .{err}); return error.BenchmarkFailed; }; @@ -93,7 +97,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { fn teardown(ptr: *anyopaque) void { const self: *CodepointWidth = @ptrCast(@alignCast(ptr)); if (self.data_f) |f| { - f.close(); + f.close(self.io); self.data_f = null; } } @@ -114,7 +118,7 @@ fn stepWcwidth(ptr: *anyopaque) Benchmark.Error!void { const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = f.reader(&read_buf); + var f_reader = f.reader(self.io, &read_buf); var r = &f_reader.interface; var d: UTF8Decoder = .{}; @@ -141,7 +145,7 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void { const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = f.reader(&read_buf); + var f_reader = f.reader(self.io, &read_buf); var r = &f_reader.interface; var d: UTF8Decoder = .{}; @@ -173,7 +177,7 @@ fn stepSimd(ptr: *anyopaque) Benchmark.Error!void { const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = f.reader(&read_buf); + var f_reader = f.reader(self.io, &read_buf); var r = &f_reader.interface; var d: UTF8Decoder = .{}; diff --git a/src/benchmark/GraphemeBreak.zig b/src/benchmark/GraphemeBreak.zig index 8278c5c2f15..fce4abeb4b7 100644 --- a/src/benchmark/GraphemeBreak.zig +++ b/src/benchmark/GraphemeBreak.zig @@ -14,10 +14,11 @@ const uucode = @import("uucode"); const log = std.log.scoped(.@"terminal-stream-bench"); +io: std.Io, opts: Options, /// The file, opened in the setup function. -data_f: ?std.fs.File = null, +data_f: ?std.Io.File = null, pub const Options = struct { /// The type of codepoint width calculation to use. @@ -44,11 +45,14 @@ pub const Mode = enum { /// Create a new terminal stream handler for the given arguments. pub fn create( alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, opts: Options, ) !*GraphemeBreak { + _ = env; const ptr = try alloc.create(GraphemeBreak); errdefer alloc.destroy(ptr); - ptr.* = .{ .opts = opts }; + ptr.* = .{ .io = io, .opts = opts }; return ptr; } @@ -73,7 +77,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { // Open our data file to prepare for reading. We can do more // validation here eventually. assert(self.data_f == null); - self.data_f = options.dataFile(self.opts.data) catch |err| { + self.data_f = options.dataFile(self.io, self.opts.data) catch |err| { log.warn("error opening data file err={}", .{err}); return error.BenchmarkFailed; }; @@ -82,7 +86,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { fn teardown(ptr: *anyopaque) void { const self: *GraphemeBreak = @ptrCast(@alignCast(ptr)); if (self.data_f) |f| { - f.close(); + f.close(self.io); self.data_f = null; } } @@ -92,7 +96,7 @@ fn stepNoop(ptr: *anyopaque) Benchmark.Error!void { const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = f.reader(&read_buf); + var f_reader = f.reader(self.io, &read_buf); var r = &f_reader.interface; var d: UTF8Decoder = .{}; @@ -115,7 +119,7 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void { const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = f.reader(&read_buf); + var f_reader = f.reader(self.io, &read_buf); var r = &f_reader.interface; var d: UTF8Decoder = .{}; diff --git a/src/benchmark/IsSymbol.zig b/src/benchmark/IsSymbol.zig index 4fbffd1ec81..d5fdb16b554 100644 --- a/src/benchmark/IsSymbol.zig +++ b/src/benchmark/IsSymbol.zig @@ -14,10 +14,11 @@ const symbols_table = @import("../unicode/symbols_table.zig").table; const log = std.log.scoped(.@"is-symbol-bench"); +io: std.Io, opts: Options, /// The file, opened in the setup function. -data_f: ?std.fs.File = null, +data_f: ?std.Io.File = null, pub const Options = struct { /// Which test to run. @@ -42,11 +43,14 @@ pub const Mode = enum { /// Create a new terminal stream handler for the given arguments. pub fn create( alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, opts: Options, ) !*IsSymbol { + _ = env; const ptr = try alloc.create(IsSymbol); errdefer alloc.destroy(ptr); - ptr.* = .{ .opts = opts }; + ptr.* = .{ .io = io, .opts = opts }; return ptr; } @@ -71,7 +75,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { // Open our data file to prepare for reading. We can do more // validation here eventually. assert(self.data_f == null); - self.data_f = options.dataFile(self.opts.data) catch |err| { + self.data_f = options.dataFile(self.io, self.opts.data) catch |err| { log.warn("error opening data file err={}", .{err}); return error.BenchmarkFailed; }; @@ -80,7 +84,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { fn teardown(ptr: *anyopaque) void { const self: *IsSymbol = @ptrCast(@alignCast(ptr)); if (self.data_f) |f| { - f.close(); + f.close(self.io); self.data_f = null; } } @@ -90,7 +94,7 @@ fn stepUucode(ptr: *anyopaque) Benchmark.Error!void { const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = f.reader(&read_buf); + var f_reader = f.reader(self.io, &read_buf); var r = &f_reader.interface; var d: UTF8Decoder = .{}; @@ -117,7 +121,7 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void { const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = f.reader(&read_buf); + var f_reader = f.reader(self.io, &read_buf); var r = &f_reader.interface; var d: UTF8Decoder = .{}; diff --git a/src/benchmark/OscParser.zig b/src/benchmark/OscParser.zig index d4b416de8e3..b54c29112a1 100644 --- a/src/benchmark/OscParser.zig +++ b/src/benchmark/OscParser.zig @@ -10,10 +10,11 @@ const options = @import("options.zig"); const Parser = @import("../terminal/osc.zig").Parser; const log = std.log.scoped(.@"osc-parser-bench"); +io: std.Io, opts: Options, /// The file, opened in the setup function. -data_f: ?std.fs.File = null, +data_f: ?std.Io.File = null, parser: Parser, @@ -29,11 +30,15 @@ pub const Options = struct { /// Create a new terminal stream handler for the given arguments. pub fn create( alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, opts: Options, ) !*OscParser { + _ = env; const ptr = try alloc.create(OscParser); errdefer alloc.destroy(ptr); ptr.* = .{ + .io = io, .opts = opts, .data_f = null, .parser = .init(alloc), @@ -60,7 +65,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { // Open our data file to prepare for reading. We can do more // validation here eventually. assert(self.data_f == null); - self.data_f = options.dataFile(self.opts.data) catch |err| { + self.data_f = options.dataFile(self.io, self.opts.data) catch |err| { log.warn("error opening data file err={}", .{err}); return error.BenchmarkFailed; }; @@ -70,7 +75,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { fn teardown(ptr: *anyopaque) void { const self: *OscParser = @ptrCast(@alignCast(ptr)); if (self.data_f) |f| { - f.close(); + f.close(self.io); self.data_f = null; } } @@ -80,7 +85,7 @@ fn step(ptr: *anyopaque) Benchmark.Error!void { const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var r = f.reader(&read_buf); + var r = f.reader(self.io, &read_buf); var osc_buf: [4096]u8 align(std.atomic.cache_line) = undefined; while (true) { diff --git a/src/benchmark/ScreenClone.zig b/src/benchmark/ScreenClone.zig index 108eaa0c6a2..8617160c2cd 100644 --- a/src/benchmark/ScreenClone.zig +++ b/src/benchmark/ScreenClone.zig @@ -14,6 +14,7 @@ const Terminal = terminalpkg.Terminal; const log = std.log.scoped(.@"terminal-stream-bench"); +io: std.Io, opts: Options, terminal: Terminal, @@ -52,14 +53,17 @@ pub const Mode = enum { pub fn create( alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, opts: Options, ) !*ScreenClone { const ptr = try alloc.create(ScreenClone); errdefer alloc.destroy(ptr); ptr.* = .{ + .io = io, .opts = opts, - .terminal = try .init(alloc, .{ + .terminal = try .init(alloc, io, env, .{ .rows = opts.@"terminal-rows", .cols = opts.@"terminal-cols", }), @@ -99,7 +103,8 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { s.nextSlice("hello"); // Setup our terminal state - const data_f: std.fs.File = (options.dataFile( + const data_f: std.Io.File = (options.dataFile( + self.io, self.opts.data, ) catch |err| { log.warn("error opening data file err={}", .{err}); @@ -110,7 +115,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { defer stream.deinit(); var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = data_f.reader(&read_buf); + var f_reader = data_f.reader(self.io, &read_buf); const r = &f_reader.interface; var buf: [4096]u8 = undefined; diff --git a/src/benchmark/TerminalParser.zig b/src/benchmark/TerminalParser.zig index e00081763b0..cbc87e865fc 100644 --- a/src/benchmark/TerminalParser.zig +++ b/src/benchmark/TerminalParser.zig @@ -10,10 +10,11 @@ const options = @import("options.zig"); const log = std.log.scoped(.@"terminal-stream-bench"); +io: std.Io, opts: Options, /// The file, opened in the setup function. -data_f: ?std.fs.File = null, +data_f: ?std.Io.File = null, pub const Options = struct { /// The data to read as a filepath. If this is "-" then @@ -26,11 +27,14 @@ pub const Options = struct { pub fn create( alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, opts: Options, ) !*TerminalParser { + _ = env; const ptr = try alloc.create(TerminalParser); errdefer alloc.destroy(ptr); - ptr.* = .{ .opts = opts }; + ptr.* = .{ .io = io, .opts = opts }; return ptr; } @@ -52,7 +56,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { // Open our data file to prepare for reading. We can do more // validation here eventually. assert(self.data_f == null); - self.data_f = options.dataFile(self.opts.data) catch |err| { + self.data_f = options.dataFile(self.io, self.opts.data) catch |err| { log.warn("error opening data file err={}", .{err}); return error.BenchmarkFailed; }; @@ -61,7 +65,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { fn teardown(ptr: *anyopaque) void { const self: *TerminalParser = @ptrCast(@alignCast(ptr)); if (self.data_f) |f| { - f.close(); + f.close(self.io); self.data_f = null; } } @@ -76,7 +80,7 @@ fn step(ptr: *anyopaque) Benchmark.Error!void { // aren't currently IO bound. const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = f.reader(&read_buf); + var f_reader = f.reader(self.io, &read_buf); var r = &f_reader.interface; var p: terminalpkg.Parser = .init(); diff --git a/src/benchmark/TerminalStream.zig b/src/benchmark/TerminalStream.zig index 1cac656e278..8b14737c813 100644 --- a/src/benchmark/TerminalStream.zig +++ b/src/benchmark/TerminalStream.zig @@ -24,13 +24,14 @@ const Stream = terminalpkg.Stream(*Handler); const log = std.log.scoped(.@"terminal-stream-bench"); +io: std.Io, opts: Options, terminal: Terminal, handler: Handler, stream: Stream, /// The file, opened in the setup function. -data_f: ?std.fs.File = null, +data_f: ?std.Io.File = null, pub const Options = struct { /// The size of the terminal. This affects benchmarking when @@ -50,14 +51,17 @@ pub const Options = struct { /// Create a new terminal stream handler for the given arguments. pub fn create( alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, opts: Options, ) !*TerminalStream { const ptr = try alloc.create(TerminalStream); errdefer alloc.destroy(ptr); ptr.* = .{ + .io = io, .opts = opts, - .terminal = try .init(alloc, .{ + .terminal = try .init(alloc, io, env, .{ .rows = opts.@"terminal-rows", .cols = opts.@"terminal-cols", }), @@ -90,7 +94,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { // Open our data file to prepare for reading. We can do more // validation here eventually. assert(self.data_f == null); - self.data_f = options.dataFile(self.opts.data) catch |err| { + self.data_f = options.dataFile(self.io, self.opts.data) catch |err| { log.warn("error opening data file err={}", .{err}); return error.BenchmarkFailed; }; @@ -99,7 +103,7 @@ fn setup(ptr: *anyopaque) Benchmark.Error!void { fn teardown(ptr: *anyopaque) void { const self: *TerminalStream = @ptrCast(@alignCast(ptr)); if (self.data_f) |f| { - f.close(); + f.close(self.io); self.data_f = null; } } @@ -115,7 +119,7 @@ fn step(ptr: *anyopaque) Benchmark.Error!void { const f = self.data_f orelse return; var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; - var f_reader = f.reader(&read_buf); + var f_reader = f.reader(self.io, &read_buf); const r = &f_reader.interface; var buf: [4096]u8 = undefined; diff --git a/src/benchmark/cli.zig b/src/benchmark/cli.zig index 13f070774c5..a9e33dd70c5 100644 --- a/src/benchmark/cli.zig +++ b/src/benchmark/cli.zig @@ -36,31 +36,33 @@ pub const Action = enum { }; /// An entrypoint for the benchmark CLI. -pub fn main() !void { - const alloc = std.heap.c_allocator; - const action_ = try cli.action.detectArgs(Action, alloc); +pub fn main(init: std.process.Init) !void { + const alloc = init.arena.allocator(); + const action_ = try cli.action.detectArgs(Action, alloc, init.minimal.args); const action = action_ orelse return error.NoAction; - try mainAction(alloc, action, .cli); + try mainAction(alloc, action, init.io, .cli); } /// Arguments that can be passed to the benchmark. pub const Args = union(enum) { /// The arguments passed to the CLI via argc/argv. - cli, + cli: std.process.Args, - /// Simple string arguments, parsed via std.process.ArgIteratorGeneral. + /// Simple string arguments, parsed via std.process.Args.IteratorGeneral. string: []const u8, }; pub fn mainAction( alloc: Allocator, action: Action, + io: std.Io, + env: *const std.process.Environ.Map, args: Args, ) !void { switch (action) { inline else => |comptime_action| { const BenchmarkImpl = Action.Struct(comptime_action); - try mainActionImpl(BenchmarkImpl, alloc, args); + try mainActionImpl(BenchmarkImpl, alloc, io, env, args); }, } } @@ -68,6 +70,8 @@ pub fn mainAction( fn mainActionImpl( comptime BenchmarkImpl: type, alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, args: Args, ) !void { // First, parse our CLI options. @@ -75,13 +79,13 @@ fn mainActionImpl( var opts: Options = .{}; defer if (@hasDecl(Options, "deinit")) opts.deinit(); switch (args) { - .cli => { - var iter = try cli.args.argsIterator(alloc); + .cli => |a| { + var iter = try cli.args.argsIterator(a, alloc); defer iter.deinit(); try cli.args.parse(Options, alloc, &opts, &iter); }, .string => |str| { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, str, ); @@ -91,10 +95,10 @@ fn mainActionImpl( } // Create our implementation - const impl = try BenchmarkImpl.create(alloc, opts); + const impl = try BenchmarkImpl.create(alloc, io, env, opts); defer impl.destroy(alloc); // Initialize our benchmark const b = impl.benchmark(); - _ = try b.run(.once); + _ = try b.run(io, .once); } diff --git a/src/benchmark/options.zig b/src/benchmark/options.zig index 049e80f48e9..a933fcac51d 100644 --- a/src/benchmark/options.zig +++ b/src/benchmark/options.zig @@ -6,15 +6,15 @@ const std = @import("std"); /// across our CLI. If the path is not set then no file is returned. /// If the path is "-", then we will return stdin. If the path is /// a file then we will open and return the handle. -pub fn dataFile(path_: ?[]const u8) !?std.fs.File { +pub fn dataFile(io: std.Io, path_: ?[]const u8) !?std.Io.File { const path = path_ orelse return null; // Stdin if (std.mem.eql(u8, path, "-")) return .stdin(); // Normal file - const file = try std.fs.cwd().openFile(path, .{}); - errdefer file.close(); + const file = try std.Io.Dir.cwd().openFile(io, path, .{}); + errdefer file.close(io); return file; } diff --git a/src/build/Config.zig b/src/build/Config.zig index 797a00ddb68..8bbda6393c6 100644 --- a/src/build/Config.zig +++ b/src/build/Config.zig @@ -34,6 +34,7 @@ sentry: bool = true, simd: bool = true, i18n: bool = true, wasm_shared: bool = true, +vaxis: bool = true, /// Ghostty exe properties exe_entrypoint: ExeEntrypoint = .ghostty, @@ -66,9 +67,6 @@ emit_unicode_table_gen: bool = false, /// rather than as the root project. is_dep: bool = false, -/// Environmental properties -env: std.process.EnvMap, - pub fn init(b: *std.Build, appVersion: []const u8, libVersion: []const u8) !Config { // Setup our standard Zig target and optimize options, i.e. // `-Doptimize` and `-Dtarget`. @@ -124,16 +122,11 @@ pub fn init(b: *std.Build, appVersion: []const u8, libVersion: []const u8) !Conf // defaults. const gtk_targets = gtk.targets(b); - // We use env vars throughout the build so we grab them immediately here. - var env = try std.process.getEnvMap(b.allocator); - errdefer env.deinit(); - var config: Config = .{ .optimize = optimize, .target = target, .wasm_target = wasm_target, .is_dep = is_dep, - .env = env, }; //--------------------------------------------------------------- @@ -227,6 +220,12 @@ pub fn init(b: *std.Build, appVersion: []const u8, libVersion: []const u8) !Conf else => false, }; + config.vaxis = b.option( + bool, + "vaxis", + "Enables TUI programs like `+list-themes` based on libvaxis.", + ) orelse true; + //--------------------------------------------------------------- // Ghostty Exe Properties @@ -327,8 +326,8 @@ pub fn init(b: *std.Build, appVersion: []const u8, libVersion: []const u8) !Conf // If we're in a nix shell we default to doing this. // Note: we purposely never deinit envmap because we leak the strings - if (env.get("IN_NIX_SHELL") == null) break :patch_rpath null; - break :patch_rpath env.get("LD_LIBRARY_PATH"); + if (b.graph.environ_map.get("IN_NIX_SHELL") == null) break :patch_rpath null; + break :patch_rpath b.graph.environ_map.get("LD_LIBRARY_PATH"); }; config.pie = b.option( @@ -401,8 +400,12 @@ pub fn init(b: *std.Build, appVersion: []const u8, libVersion: []const u8) !Conf if (system_package) break :emit_docs true; // We only default to true if we can find pandoc. - const path = expandPath(b.allocator, "pandoc") catch - break :emit_docs false; + const path = expandPath( + b.graph.io, + b.allocator, + &b.graph.environ_map, + "pandoc", + ) catch break :emit_docs false; defer if (path) |p| b.allocator.free(p); break :emit_docs path != null; }; @@ -524,6 +527,7 @@ pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void { step.addOption(bool, "sentry", self.sentry); step.addOption(bool, "simd", self.simd); step.addOption(bool, "i18n", self.i18n); + step.addOption(bool, "vaxis", self.vaxis); step.addOption(ApprtRuntime, "app_runtime", self.app_runtime); step.addOption(FontBackend, "font_backend", self.font_backend); step.addOption(RendererBackend, "renderer", self.renderer); @@ -582,7 +586,7 @@ pub fn terminalOptions(self: *const Config, artifact: TerminalBuildOptions.Artif } /// Returns a baseline CPU target retaining all the other CPU configs. -pub fn baselineTarget(self: *const Config) std.Build.ResolvedTarget { +pub fn baselineTarget(self: *const Config, b: *std.Build) std.Build.ResolvedTarget { // Set our cpu model as baseline. There may need to be other modifications // we need to make such as resetting CPU features but for now this works. var q = self.target.query; @@ -592,7 +596,7 @@ pub fn baselineTarget(self: *const Config) std.Build.ResolvedTarget { // handle the native case. return .{ .query = q, - .result = std.zig.system.resolveTargetQuery(q) catch + .result = std.zig.system.resolveTargetQuery(b.graph.io, q) catch @panic("unable to resolve baseline query"), }; } @@ -606,7 +610,6 @@ pub fn fromOptions() Config { // Unused at runtime. .optimize = undefined, .target = undefined, - .env = undefined, .version = options.app_version, .flatpak = options.flatpak, @@ -618,6 +621,7 @@ pub fn fromOptions() Config { .wasm_target = std.meta.stringToEnum(WasmTarget, @tagName(options.wasm_target)).?, .wasm_shared = options.wasm_shared, .i18n = options.i18n, + .vaxis = options.vaxis, }; } diff --git a/src/build/GhosttyBench.zig b/src/build/GhosttyBench.zig index 27dda880912..fbf8029d28a 100644 --- a/src/build/GhosttyBench.zig +++ b/src/build/GhosttyBench.zig @@ -23,9 +23,9 @@ pub fn init( // We always want our datagen to be fast because it // takes awhile to run. .optimize = .ReleaseFast, + .link_libc = true, }), }); - exe.linkLibC(); _ = try deps.add(exe); try steps.append(b.allocator, exe); } @@ -39,9 +39,9 @@ pub fn init( .target = deps.config.target, // We always want our benchmarks to be in release mode. .optimize = .ReleaseFast, + .link_libc = true, }), }); - exe.linkLibC(); _ = try deps.add(exe); try steps.append(b.allocator, exe); } diff --git a/src/build/GhosttyDist.zig b/src/build/GhosttyDist.zig index 448047f4b73..fc345de7f05 100644 --- a/src/build/GhosttyDist.zig +++ b/src/build/GhosttyDist.zig @@ -24,7 +24,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { // Get the resources we're going to inject into the source tarball. // lib-vt doesn't need GTK resources or frame data. const alloc = b.allocator; - var resources: std.ArrayListUnmanaged(Resource) = .empty; + var resources: std.ArrayList(Resource) = .empty; if (!cfg.emit_lib_vt) { { const gtk = SharedDeps.gtkNgDistResources(b); @@ -66,14 +66,14 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { // generated file. const copied = b.addWriteFiles().addCopyFile( resource.generated, - std.fs.path.basename(resource.dist), + std.Io.Dir.path.basename(resource.dist), ); // --add-file uses the most recent --prefix to determine the path // in the archive to copy the file (the directory only). git_archive.addArg(b.fmt("--prefix={s}-{f}/{s}/", .{ name, cfg.version, - std.fs.path.dirname(resource.dist).?, + std.Io.Dir.path.dirname(resource.dist).?, })); git_archive.addPrefixedFileArg("--add-file=", copied); } @@ -237,11 +237,11 @@ pub const Resource = struct { /// Returns true if the dist path exists at build time. pub fn exists(self: *const Resource, b: *std.Build) bool { - if (b.build_root.handle.access(self.dist, .{})) { + if (b.build_root.handle.access(b.graph.io, self.dist, .{})) { // If we have a ".git" directory then we're a git checkout // and we never want to use the dist path. This shouldn't happen // so show a warning to the user. - if (b.build_root.handle.access(".git", .{})) { + if (b.build_root.handle.access(b.graph.io, ".git", .{})) { std.log.warn( "dist resource '{s}' should not be in a git checkout", .{self.dist}, diff --git a/src/build/GhosttyDocs.zig b/src/build/GhosttyDocs.zig index cd75fc061c2..2b207f23742 100644 --- a/src/build/GhosttyDocs.zig +++ b/src/build/GhosttyDocs.zig @@ -50,7 +50,7 @@ pub fn init( generate_markdown.root_module.addOptions("build_options", generate_markdown_options); const generate_markdown_step = b.addRunArtifact(generate_markdown); - const markdown_output = generate_markdown_step.captureStdOut(); + const markdown_output = generate_markdown_step.captureStdOut(.{}); try steps.append(b.allocator, &b.addInstallFile( markdown_output, @@ -68,7 +68,7 @@ pub fn init( generate_html.addFileArg(markdown_output); try steps.append(b.allocator, &b.addInstallFile( - generate_html.captureStdOut(), + generate_html.captureStdOut(.{}), "share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".html", ).step); @@ -83,7 +83,7 @@ pub fn init( generate_manpage.addFileArg(markdown_output); try steps.append(b.allocator, &b.addInstallFile( - generate_manpage.captureStdOut(), + generate_manpage.captureStdOut(.{}), "share/man/man" ++ manpage.section ++ "/" ++ manpage.name ++ "." ++ manpage.section, ).step); } diff --git a/src/build/GhosttyExe.zig b/src/build/GhosttyExe.zig index caa564bf008..fea026fa46a 100644 --- a/src/build/GhosttyExe.zig +++ b/src/build/GhosttyExe.zig @@ -50,7 +50,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty switch (cfg.target.result.os.tag) { .windows => { exe.subsystem = .Windows; - exe.addWin32ResourceFile(.{ + exe.root_module.addWin32ResourceFile(.{ .file = b.path("dist/windows/ghostty.rc"), }); }, @@ -75,6 +75,8 @@ pub fn install(self: *const Ghostty) void { /// our build in any way but addresses a common build-from-source issue /// for a subset of users. fn checkNixShell(exe: *std.Build.Step.Compile, cfg: *const Config) !void { + const b = exe.step.owner; + // Non-Linux doesn't have rpath issues. if (cfg.target.result.os.tag != .linux) return; @@ -85,10 +87,10 @@ fn checkNixShell(exe: *std.Build.Step.Compile, cfg: *const Config) !void { if (!cfg.target.query.isNativeOs()) return; // Verify we're in NixOS - std.fs.accessAbsolute("/etc/NIXOS", .{}) catch return; + std.Io.Dir.accessAbsolute(b.graph.io, "/etc/NIXOS", .{}) catch return; // If we're in a nix shell, not a problem - if (cfg.env.get("IN_NIX_SHELL") != null) return; + if (b.graph.environ_map.contains("IN_NIX_SHELL")) return; try exe.step.addError( "\x1b[" ++ color_map.get("yellow").? ++ diff --git a/src/build/GhosttyFrameData.zig b/src/build/GhosttyFrameData.zig index 8469759f9f9..be61f029f5b 100644 --- a/src/build/GhosttyFrameData.zig +++ b/src/build/GhosttyFrameData.zig @@ -40,16 +40,16 @@ pub fn distResources(b: *std.Build) struct { .name = "framegen", .root_module = b.createModule(.{ .target = b.graph.host, + .link_libc = true, }), }); - exe.addCSourceFile(.{ + exe.root_module.addCSourceFile(.{ .file = b.path("src/build/framegen/main.c"), .flags = &.{}, }); - exe.linkLibC(); if (b.systemIntegrationOption("zlib", .{})) { - exe.linkSystemLibrary2("zlib", .{ + exe.root_module.linkSystemLibrary("zlib", .{ .preferred_link_mode = .dynamic, .search_strategy = .mode_first, }); @@ -58,7 +58,7 @@ pub fn distResources(b: *std.Build) struct { .target = b.graph.host, .optimize = .ReleaseFast, })) |zlib_dep| { - exe.linkLibrary(zlib_dep.artifact("z")); + exe.root_module.linkLibrary(zlib_dep.artifact("z")); } } diff --git a/src/build/GhosttyI18n.zig b/src/build/GhosttyI18n.zig index 0874676cbfc..88017f5884b 100644 --- a/src/build/GhosttyI18n.zig +++ b/src/build/GhosttyI18n.zig @@ -34,7 +34,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n { msgfmt.addFileArg(b.path("po/" ++ locale ++ ".po")); try steps.append(b.allocator, &b.addInstallFile( - msgfmt.captureStdOut(), + msgfmt.captureStdOut(.{}), std.fmt.comptimePrint( "share/locale/{s}/LC_MESSAGES/{s}.mo", .{ target_locale, domain }, @@ -96,21 +96,22 @@ fn createUpdateStep(b: *std.Build) !*std.Build.Step { // order. That will minimize code churn as directory walking is not // guaranteed to happen in any particular order. - var gtk_files: std.ArrayListUnmanaged([]const u8) = .empty; + var gtk_files: std.ArrayList([]const u8) = .empty; defer { for (gtk_files.items) |item| b.allocator.free(item); gtk_files.deinit(b.allocator); } var gtk_dir = try b.build_root.handle.openDir( + b.graph.io, "src/apprt/gtk", .{ .iterate = true }, ); - defer gtk_dir.close(); + defer gtk_dir.close(b.graph.io); var walk = try gtk_dir.walk(b.allocator); defer walk.deinit(); - while (try walk.next()) |src| { + while (try walk.next(b.graph.io)) |src| { switch (src.kind) { .file => if (!std.mem.endsWith( u8, @@ -178,15 +179,15 @@ fn createUpdateStep(b: *std.Build) !*std.Build.Step { xgettext_merge.addFileArg(gtk_pot); const usf = b.addUpdateSourceFiles(); usf.addCopyFileToSource( - xgettext_merge.captureStdOut(), + xgettext_merge.captureStdOut(.{}), "po/" ++ domain ++ ".pot", ); inline for (locales) |locale| { const msgmerge = b.addSystemCommand(&.{ "msgmerge", "--quiet", "--no-fuzzy-matching" }); msgmerge.addFileArg(b.path("po/" ++ locale ++ ".po")); - msgmerge.addFileArg(xgettext_merge.captureStdOut()); - usf.addCopyFileToSource(msgmerge.captureStdOut(), "po/" ++ locale ++ ".po"); + msgmerge.addFileArg(xgettext_merge.captureStdOut(.{})); + usf.addCopyFileToSource(msgmerge.captureStdOut(.{}), "po/" ++ locale ++ ".po"); } return &usf.step; diff --git a/src/build/GhosttyLib.zig b/src/build/GhosttyLib.zig index a6667e33407..f219e010fb0 100644 --- a/src/build/GhosttyLib.zig +++ b/src/build/GhosttyLib.zig @@ -29,12 +29,12 @@ pub fn initStatic( .strip = deps.config.strip, .omit_frame_pointer = deps.config.strip, .unwind_tables = if (deps.config.strip) .none else .sync, + .link_libc = true, }), // Fails on self-hosted x86_64 on macOS .use_llvm = true, }); - lib.linkLibC(); // These must be bundled since we're compiling into a static lib. // Otherwise, you get undefined symbol errors. @@ -110,12 +110,17 @@ pub fn initShared( { // The CRT initialization code in msvcrt.lib calls __vcrt_initialize // and __acrt_initialize, which are in the static CRT libraries. - lib.linkSystemLibrary("libvcruntime"); + lib.root_module.linkSystemLibrary("libvcruntime", .{}); // ucrt.lib is in the Windows SDK 'ucrt' dir. Detect the SDK // installation and add the UCRT library path. const arch = deps.config.target.result.cpu.arch; - const sdk = std.zig.WindowsSdk.find(b.allocator, arch) catch null; + const sdk = std.zig.WindowsSdk.find( + b.allocator, + b.graph.io, + arch, + &b.graph.environ_map, + ) catch null; if (sdk) |s| { if (s.windows10sdk) |w10| { const arch_str: []const u8 = switch (arch) { @@ -131,11 +136,11 @@ pub fn initShared( ) catch null; if (ucrt_lib_path) |path| { - lib.addLibraryPath(.{ .cwd_relative = path }); + lib.root_module.addLibraryPath(.{ .cwd_relative = path }); } } } - lib.linkSystemLibrary("libucrt"); + lib.root_module.linkSystemLibrary("libucrt", .{}); } // Get our debug symbols diff --git a/src/build/GhosttyLibVt.zig b/src/build/GhosttyLibVt.zig index c59f40de7fd..d1265408b7f 100644 --- a/src/build/GhosttyLibVt.zig +++ b/src/build/GhosttyLibVt.zig @@ -174,7 +174,7 @@ pub fn initStaticAppleUniversal( .os_tag = p.os_tag, .os_version_min = Config.osVersionMin(p.os_tag), }; - if (detectAppleSDK(b.resolveTargetQuery(target_query).result)) { + if (detectAppleSDK(b, b.resolveTargetQuery(target_query).result)) { const dev_zig = try zig.retarget(b, cfg, deps, b.resolveTargetQuery(target_query)); result.put(p.device, try initStatic(b, &dev_zig)); @@ -472,12 +472,15 @@ pub fn xcframework( } /// Returns true if the Apple SDK for the given target is installed. -fn detectAppleSDK(target: std.Target) bool { - _ = std.zig.LibCInstallation.findNative(.{ - .allocator = std.heap.page_allocator, - .target = &target, - .verbose = false, - }) catch return false; +fn detectAppleSDK(b: *std.Build, target: std.Target) bool { + _ = std.zig.LibCInstallation.findNative( + b.allocator, + b.graph.io, + .{ + .target = &target, + .verbose = false, + }, + ) catch return false; return true; } diff --git a/src/build/GhosttyResources.zig b/src/build/GhosttyResources.zig index 6f857655b47..f6c50605088 100644 --- a/src/build/GhosttyResources.zig +++ b/src/build/GhosttyResources.zig @@ -21,9 +21,9 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty .strip = false, .omit_frame_pointer = false, .unwind_tables = .sync, + .link_libc = true, }), }); - build_data_exe.linkLibC(); deps.help_strings.addImport(build_data_exe); @@ -38,8 +38,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty // Encode our terminfo const run = b.addRunArtifact(build_data_exe); run.addArg("+terminfo"); - const wf = b.addWriteFiles(); - const source = wf.addCopyFile(run.captureStdOut(), "ghostty.terminfo"); + const source = run.captureStdOut(.{}); if (cfg.emit_terminfo) { const source_install = b.addInstallFile( @@ -63,8 +62,8 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty const run_step = RunStep.create(b, "infotocap"); run_step.addArg("infotocap"); run_step.addFileArg(source); - const out_source = run_step.captureStdOut(); - _ = run_step.captureStdErr(); // so we don't see stderr + const out_source = run_step.captureStdOut(.{}); + _ = run_step.captureStdErr(.{}); // so we don't see stderr const cap_install = b.addInstallFile( out_source, @@ -84,7 +83,7 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty const path = run_step.addOutputFileArg(terminfo_share_dir); run_step.addFileArg(source); - _ = run_step.captureStdErr(); // so we don't see stderr + _ = run_step.captureStdErr(.{}); // so we don't see stderr // Ensure that `share/terminfo` is a directory, otherwise the `cp // -R` will create a file named `share/terminfo` @@ -142,14 +141,12 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty { const run = b.addRunArtifact(build_data_exe); run.addArg("+fish"); - const wf = b.addWriteFiles(); - _ = wf.addCopyFile(run.captureStdOut(), "ghostty.fish"); + const source = run.captureStdOut(.{}); - const install_step = b.addInstallDirectory(.{ - .source_dir = wf.getDirectory(), - .install_dir = .prefix, - .install_subdir = "share/fish/vendor_completions.d", - }); + const install_step = b.addInstallFile( + source, + "share/fish/vendor_completions.d/ghostty.fish", + ); try steps.append(b.allocator, &install_step.step); } @@ -157,14 +154,12 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty { const run = b.addRunArtifact(build_data_exe); run.addArg("+zsh"); - const wf = b.addWriteFiles(); - _ = wf.addCopyFile(run.captureStdOut(), "_ghostty"); + const source = run.captureStdOut(.{}); - const install_step = b.addInstallDirectory(.{ - .source_dir = wf.getDirectory(), - .install_dir = .prefix, - .install_subdir = "share/zsh/site-functions", - }); + const install_step = b.addInstallFile( + source, + "share/zsh/site-functions/_ghostty", + ); try steps.append(b.allocator, &install_step.step); } @@ -172,55 +167,44 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty { const run = b.addRunArtifact(build_data_exe); run.addArg("+bash"); - const wf = b.addWriteFiles(); - _ = wf.addCopyFile(run.captureStdOut(), "ghostty.bash"); + const source = run.captureStdOut(.{}); - const install_step = b.addInstallDirectory(.{ - .source_dir = wf.getDirectory(), - .install_dir = .prefix, - .install_subdir = "share/bash-completion/completions", - }); + const install_step = b.addInstallFile( + source, + "share/bash-completion/completions/ghostty.bash", + ); try steps.append(b.allocator, &install_step.step); } // Vim and Neovim plugin { - const wf = b.addWriteFiles(); + const syntax_run = b.addRunArtifact(build_data_exe); + syntax_run.addArg("+vim-syntax"); + const syntax_source = syntax_run.captureStdOut(.{}); - { - const run = b.addRunArtifact(build_data_exe); - run.addArg("+vim-syntax"); - _ = wf.addCopyFile(run.captureStdOut(), "syntax/ghostty.vim"); - } - { - const run = b.addRunArtifact(build_data_exe); - run.addArg("+vim-ftdetect"); - _ = wf.addCopyFile(run.captureStdOut(), "ftdetect/ghostty.vim"); - } - { - const run = b.addRunArtifact(build_data_exe); - run.addArg("+vim-ftplugin"); - _ = wf.addCopyFile(run.captureStdOut(), "ftplugin/ghostty.vim"); - } - { - const run = b.addRunArtifact(build_data_exe); - run.addArg("+vim-compiler"); - _ = wf.addCopyFile(run.captureStdOut(), "compiler/ghostty.vim"); - } + const ftdetect_run = b.addRunArtifact(build_data_exe); + ftdetect_run.addArg("+vim-ftdetect"); + const ftdetect_source = ftdetect_run.captureStdOut(.{}); - const vim_step = b.addInstallDirectory(.{ - .source_dir = wf.getDirectory(), - .install_dir = .prefix, - .install_subdir = "share/vim/vimfiles", - }); + const ftplugin_run = b.addRunArtifact(build_data_exe); + ftplugin_run.addArg("+vim-ftplugin"); + const ftplugin_source = ftplugin_run.captureStdOut(.{}); + + const compiler_run = b.addRunArtifact(build_data_exe); + compiler_run.addArg("+vim-compiler"); + const compiler_source = compiler_run.captureStdOut(.{}); + + const vim_step = b.addInstallFile(syntax_source, "share/vim/vimfiles/syntax/ghostty.vim"); try steps.append(b.allocator, &vim_step.step); + _ = b.addInstallFile(ftdetect_source, "share/vim/vimfiles/ftdetect/ghostty.vim"); + _ = b.addInstallFile(ftplugin_source, "share/vim/vimfiles/ftplugin/ghostty.vim"); + _ = b.addInstallFile(compiler_source, "share/vim/vimfiles/compiler/ghostty.vim"); - const neovim_step = b.addInstallDirectory(.{ - .source_dir = wf.getDirectory(), - .install_dir = .prefix, - .install_subdir = "share/nvim/site", - }); + const neovim_step = b.addInstallFile(syntax_source, "share/nvim/site/syntax/ghostty.vim"); try steps.append(b.allocator, &neovim_step.step); + _ = b.addInstallFile(ftdetect_source, "share/nvim/site/ftdetect/ghostty.vim"); + _ = b.addInstallFile(ftplugin_source, "share/nvim/site/ftplugin/ghostty.vim"); + _ = b.addInstallFile(compiler_source, "share/nvim/site/compiler/ghostty.vim"); } // Sublime syntax highlighting for bat cli tool @@ -232,14 +216,12 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty { const run = b.addRunArtifact(build_data_exe); run.addArg("+sublime"); - const wf = b.addWriteFiles(); - _ = wf.addCopyFile(run.captureStdOut(), "ghostty.sublime-syntax"); + const source = run.captureStdOut(.{}); - const install_step = b.addInstallDirectory(.{ - .source_dir = wf.getDirectory(), - .install_dir = .prefix, - .install_subdir = "share/bat/syntaxes", - }); + const install_step = b.addInstallFile( + source, + "share/bat/syntaxes/ghostty.sublime-syntax", + ); try steps.append(b.allocator, &install_step.step); } @@ -358,10 +340,10 @@ fn addLinuxAppResources( // Template output has a single header line we want to remove. // We use `tail` to do it since its part of the POSIX standard. const tail = b.addSystemCommand(&.{ "tail", "-n", "+2" }); - tail.setStdIn(.{ .lazy_path = tpl.getOutput() }); + tail.setStdIn(.{ .lazy_path = tpl.getOutputFile() }); const copy = b.addInstallFile( - tail.captureStdOut(), + tail.captureStdOut(.{}), template[1], ); diff --git a/src/build/GhosttyWebdata.zig b/src/build/GhosttyWebdata.zig index e29b20c2504..7057ad034ff 100644 --- a/src/build/GhosttyWebdata.zig +++ b/src/build/GhosttyWebdata.zig @@ -40,7 +40,7 @@ pub fn init( } const webgen_config_step = b.addRunArtifact(webgen_config); - const webgen_config_out = webgen_config_step.captureStdOut(); + const webgen_config_out = webgen_config_step.captureStdOut(.{}); try steps.append(b.allocator, &b.addInstallFile( webgen_config_out, @@ -71,7 +71,7 @@ pub fn init( } const webgen_actions_step = b.addRunArtifact(webgen_actions); - const webgen_actions_out = webgen_actions_step.captureStdOut(); + const webgen_actions_out = webgen_actions_step.captureStdOut(.{}); try steps.append(b.allocator, &b.addInstallFile( webgen_actions_out, @@ -102,7 +102,7 @@ pub fn init( } const webgen_commands_step = b.addRunArtifact(webgen_commands); - const webgen_commands_out = webgen_commands_step.captureStdOut(); + const webgen_commands_out = webgen_commands_step.captureStdOut(.{}); try steps.append(b.allocator, &b.addInstallFile( webgen_commands_out, diff --git a/src/build/GhosttyXcodebuild.zig b/src/build/GhosttyXcodebuild.zig index 81af994ca51..31d357db5b7 100644 --- a/src/build/GhosttyXcodebuild.zig +++ b/src/build/GhosttyXcodebuild.zig @@ -48,21 +48,20 @@ pub fn init( }, }; - const env = try std.process.getEnvMap(b.allocator); const app_path = b.fmt("macos/build/{s}/Ghostty.app", .{xc_config}); // Our step to build the Ghostty macOS app. const build = build: { - // External environment variables can mess up xcodebuild, so - // we create a new empty environment. - const env_map = try b.allocator.create(std.process.EnvMap); - env_map.* = .init(b.allocator); - if (env.get("PATH")) |v| try env_map.put("PATH", v); - const step = RunStep.create(b, "xcodebuild"); step.has_side_effects = true; step.cwd = b.path("macos"); - step.env_map = env_map; + + // External environment variables can mess up xcodebuild, so + // we create a new empty environment. + const path = step.getEnvMap().get("PATH"); + step.clearEnvironment(); + if (path) |p| step.addPathDir(p); + step.addArgs(&.{ "xcodebuild", "-target", @@ -91,14 +90,15 @@ pub fn init( }; const xctest = xctest: { - const env_map = try b.allocator.create(std.process.EnvMap); - env_map.* = .init(b.allocator); - if (env.get("PATH")) |v| try env_map.put("PATH", v); - const step = RunStep.create(b, "xcodebuild test"); step.has_side_effects = true; step.cwd = b.path("macos"); - step.env_map = env_map; + + // External environment variables can mess up xcodebuild, so + // we create a new empty environment. + const path = step.getEnvMap().get("PATH"); + step.clearEnvironment(); + if (path) |p| step.addPathDir(p); step.addArgs(&.{ "xcodebuild", "test", diff --git a/src/build/GitVersion.zig b/src/build/GitVersion.zig index 41cc7f84f6a..2365a110c88 100644 --- a/src/build/GitVersion.zig +++ b/src/build/GitVersion.zig @@ -23,7 +23,7 @@ pub fn detect(b: *std.Build) !Version { const tmp: []u8 = b.runAllowFail( &[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "rev-parse", "--abbrev-ref", "HEAD" }, &code, - .Ignore, + .ignore, ) catch |err| switch (err) { error.FileNotFound => return error.GitNotFound, error.ExitCodeFailure => return error.GitNotRepository, @@ -44,19 +44,19 @@ pub fn detect(b: *std.Build) !Version { const output = b.runAllowFail( &[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "-c", "log.showSignature=false", "log", "--pretty=format:%h", "-n", "1" }, &code, - .Ignore, + .ignore, ) catch |err| switch (err) { error.FileNotFound => return error.GitNotFound, else => return err, }; - break :short_hash std.mem.trimRight(u8, output, "\r\n "); + break :short_hash std.mem.trimEnd(u8, output, "\r\n "); }; const tag = b.runAllowFail( &[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "describe", "--exact-match", "--tags" }, &code, - .Ignore, + .ignore, ) catch |err| switch (err) { error.FileNotFound => return error.GitNotFound, error.ExitCodeFailure => "", // expected @@ -70,7 +70,7 @@ pub fn detect(b: *std.Build) !Version { "diff", "--quiet", "--exit-code", - }, &code, .Ignore) catch |err| switch (err) { + }, &code, .ignore) catch |err| switch (err) { error.FileNotFound => return error.GitNotFound, error.ExitCodeFailure => {}, // expected else => return err, @@ -80,7 +80,7 @@ pub fn detect(b: *std.Build) !Version { return .{ .short_hash = short_hash, .changes = changes, - .tag = if (tag.len > 0) std.mem.trimRight(u8, tag, "\r\n ") else null, - .branch = std.mem.trimRight(u8, branch, "\r\n "), + .tag = if (tag.len > 0) std.mem.trimEnd(u8, tag, "\r\n ") else null, + .branch = std.mem.trimEnd(u8, branch, "\r\n "), }; } diff --git a/src/build/HelpStrings.zig b/src/build/HelpStrings.zig index 96505cabacb..a85a99c366a 100644 --- a/src/build/HelpStrings.zig +++ b/src/build/HelpStrings.zig @@ -33,8 +33,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !HelpStrings { const help_run = b.addRunArtifact(exe); // Generated Zig files have to end with .zig - const wf = b.addWriteFiles(); - const output = wf.addCopyFile(help_run.captureStdOut(), "helpgen.zig"); + const output = help_run.captureStdOut(.{ .basename = "helpgen.zig" }); return .{ .exe = exe, diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index 5ca5368d7bf..ebef601e0cf 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -143,9 +143,9 @@ pub fn add( .optimize = optimize, }); if (target.result.os.tag.isDarwin()) { - const libc = try std.zig.LibCInstallation.findNative(.{ - .allocator = b.allocator, + const libc = try std.zig.LibCInstallation.findNative(b.allocator, b.graph.io, .{ .target = &target.result, + .environ_map = &b.graph.environ_map, .verbose = false, }); c.addSystemIncludePath(.{ .cwd_relative = libc.sys_include_dir.? }); @@ -166,9 +166,9 @@ pub fn add( }); switch (target.result.os.tag) { .macos => { - const libc = try std.zig.LibCInstallation.findNative(.{ - .allocator = b.allocator, + const libc = try std.zig.LibCInstallation.findNative(b.allocator, b.graph.io, .{ .target = &target.result, + .environ_map = &b.graph.environ_map, .verbose = false, }); c.addSystemIncludePath(.{ .cwd_relative = libc.sys_include_dir.? }); @@ -194,10 +194,10 @@ pub fn add( ); if (b.systemIntegrationOption("freetype", .{})) { - step.linkSystemLibrary2("bzip2", dynamic_link_opts); - step.linkSystemLibrary2("freetype2", dynamic_link_opts); + step.root_module.linkSystemLibrary("bzip2", dynamic_link_opts); + step.root_module.linkSystemLibrary("freetype2", dynamic_link_opts); } else { - step.linkLibrary(freetype_dep.artifact("freetype")); + step.root_module.linkLibrary(freetype_dep.artifact("freetype")); try static_libs.append( b.allocator, freetype_dep.artifact("freetype").getEmittedBin(), @@ -219,9 +219,9 @@ pub fn add( harfbuzz_dep.module("harfbuzz"), ); if (b.systemIntegrationOption("harfbuzz", .{})) { - step.linkSystemLibrary2("harfbuzz", dynamic_link_opts); + step.root_module.linkSystemLibrary("harfbuzz", dynamic_link_opts); } else { - step.linkLibrary(harfbuzz_dep.artifact("harfbuzz")); + step.root_module.linkLibrary(harfbuzz_dep.artifact("harfbuzz")); try static_libs.append( b.allocator, harfbuzz_dep.artifact("harfbuzz").getEmittedBin(), @@ -243,9 +243,9 @@ pub fn add( ); if (b.systemIntegrationOption("fontconfig", .{})) { - step.linkSystemLibrary2("fontconfig", dynamic_link_opts); + step.root_module.linkSystemLibrary("fontconfig", dynamic_link_opts); } else { - step.linkLibrary(fontconfig_dep.artifact("fontconfig")); + step.root_module.linkLibrary(fontconfig_dep.artifact("fontconfig")); try static_libs.append( b.allocator, fontconfig_dep.artifact("fontconfig").getEmittedBin(), @@ -263,7 +263,7 @@ pub fn add( .target = target, .optimize = optimize, })) |libpng_dep| { - step.linkLibrary(libpng_dep.artifact("png")); + step.root_module.linkLibrary(libpng_dep.artifact("png")); try static_libs.append( b.allocator, libpng_dep.artifact("png").getEmittedBin(), @@ -277,7 +277,7 @@ pub fn add( .target = target, .optimize = optimize, })) |zlib_dep| { - step.linkLibrary(zlib_dep.artifact("z")); + step.root_module.linkLibrary(zlib_dep.artifact("z")); try static_libs.append( b.allocator, zlib_dep.artifact("z").getEmittedBin(), @@ -295,9 +295,9 @@ pub fn add( oniguruma_dep.module("oniguruma"), ); if (b.systemIntegrationOption("oniguruma", .{})) { - step.linkSystemLibrary2("oniguruma", dynamic_link_opts); + step.root_module.linkSystemLibrary("oniguruma", dynamic_link_opts); } else { - step.linkLibrary(oniguruma_dep.artifact("oniguruma")); + step.root_module.linkLibrary(oniguruma_dep.artifact("oniguruma")); try static_libs.append( b.allocator, oniguruma_dep.artifact("oniguruma").getEmittedBin(), @@ -312,13 +312,13 @@ pub fn add( })) |glslang_dep| { step.root_module.addImport("glslang", glslang_dep.module("glslang")); if (b.systemIntegrationOption("glslang", .{})) { - step.linkSystemLibrary2("glslang", dynamic_link_opts); - step.linkSystemLibrary2( + step.root_module.linkSystemLibrary("glslang", dynamic_link_opts); + step.root_module.linkSystemLibrary( "glslang-default-resource-limits", dynamic_link_opts, ); } else { - step.linkLibrary(glslang_dep.artifact("glslang")); + step.root_module.linkLibrary(glslang_dep.artifact("glslang")); try static_libs.append( b.allocator, glslang_dep.artifact("glslang").getEmittedBin(), @@ -336,9 +336,9 @@ pub fn add( spirv_cross_dep.module("spirv_cross"), ); if (b.systemIntegrationOption("spirv-cross", .{})) { - step.linkSystemLibrary2("spirv-cross-c-shared", dynamic_link_opts); + step.root_module.linkSystemLibrary("spirv-cross-c-shared", dynamic_link_opts); } else { - step.linkLibrary(spirv_cross_dep.artifact("spirv_cross")); + step.root_module.linkLibrary(spirv_cross_dep.artifact("spirv_cross")); try static_libs.append( b.allocator, spirv_cross_dep.artifact("spirv_cross").getEmittedBin(), @@ -357,7 +357,7 @@ pub fn add( "sentry", sentry_dep.module("sentry"), ); - step.linkLibrary(sentry_dep.artifact("sentry")); + step.root_module.linkLibrary(sentry_dep.artifact("sentry")); try static_libs.append( b.allocator, sentry_dep.artifact("sentry").getEmittedBin(), @@ -404,17 +404,17 @@ pub fn add( if (step.rootModuleTarget().os.tag == .linux) { const triple = try step.rootModuleTarget().linuxTriple(b.allocator); const path = b.fmt("/usr/lib/{s}", .{triple}); - if (std.fs.accessAbsolute(path, .{})) { - step.addLibraryPath(.{ .cwd_relative = path }); + if (std.Io.Dir.cwd().access(b.graph.io, path, .{})) { + step.root_module.addLibraryPath(.{ .cwd_relative = path }); } else |_| {} } // C files - step.linkLibC(); - step.addIncludePath(b.path("src/stb")); - step.addCSourceFiles(.{ .files = &.{"src/stb/stb.c"} }); + step.root_module.link_libc = true; + step.root_module.addIncludePath(b.path("src/stb")); + step.root_module.addCSourceFiles(.{ .files = &.{"src/stb/stb.c"} }); if (step.rootModuleTarget().os.tag == .linux) { - step.addIncludePath(b.path("src/apprt/gtk")); + step.root_module.addIncludePath(b.path("src/apprt/gtk")); } // libcpp is required for various dependencies. On MSVC, we must @@ -424,7 +424,7 @@ pub fn add( // include directories (already added via linkLibC above) contain // both C and C++ headers, so linkLibCpp is not needed. if (step.rootModuleTarget().abi != .msvc) { - step.linkLibCpp(); + step.root_module.link_libcpp = true; } // We always require the system SDK so that our system headers are available. @@ -443,8 +443,10 @@ pub fn add( if (b.lazyDependency("opengl", .{})) |dep| { step.root_module.addImport("opengl", dep.module("opengl")); } - if (b.lazyDependency("vaxis", .{})) |dep| { - step.root_module.addImport("vaxis", dep.module("vaxis")); + if (self.config.vaxis) { + if (b.lazyDependency("vaxis", .{})) |dep| { + step.root_module.addImport("vaxis", dep.module("vaxis")); + } } if (b.lazyDependency("wuffs", .{ .target = target, @@ -493,7 +495,7 @@ pub fn add( "macos", macos_dep.module("macos"), ); - step.linkLibrary( + step.root_module.linkLibrary( macos_dep.artifact("macos"), ); try static_libs.append( @@ -503,7 +505,7 @@ pub fn add( } if (self.config.renderer == .opengl) { - step.linkFramework("OpenGL"); + step.root_module.linkFramework("OpenGL", .{}); } // Apple platforms do not include libc libintl so we bundle it. @@ -514,7 +516,7 @@ pub fn add( .target = target, .optimize = optimize, })) |libintl_dep| { - step.linkLibrary(libintl_dep.artifact("intl")); + step.root_module.linkLibrary(libintl_dep.artifact("intl")); try static_libs.append( b.allocator, libintl_dep.artifact("intl").getEmittedBin(), @@ -534,7 +536,7 @@ pub fn add( .@"backend-opengl3" = !target.result.os.tag.isDarwin(), })) |dep| { step.root_module.addImport("dcimgui", dep.module("dcimgui")); - step.linkLibrary(dep.artifact("dcimgui")); + step.root_module.linkLibrary(dep.artifact("dcimgui")); try static_libs.append( b.allocator, dep.artifact("dcimgui").getEmittedBin(), @@ -583,15 +585,15 @@ pub fn add( // If we're building an exe then we have additional dependencies. if (step.kind != .lib) { // We always statically compile glad - step.addIncludePath(b.path("vendor/glad/include/")); - step.addCSourceFile(.{ + step.root_module.addIncludePath(b.path("vendor/glad/include/")); + step.root_module.addCSourceFile(.{ .file = b.path("vendor/glad/src/gl.c"), .flags = &.{}, }); // When we're targeting flatpak we ALWAYS link GTK so we // get access to glib for dbus. - if (self.config.flatpak) step.linkSystemLibrary2("gtk4", dynamic_link_opts); + if (self.config.flatpak) step.root_module.linkSystemLibrary("gtk4", dynamic_link_opts); switch (self.config.app_runtime) { .none => {}, @@ -635,11 +637,11 @@ fn addGtkNg( } } - step.linkSystemLibrary2("gtk4", dynamic_link_opts); - step.linkSystemLibrary2("libadwaita-1", dynamic_link_opts); + step.root_module.linkSystemLibrary("gtk4", dynamic_link_opts); + step.root_module.linkSystemLibrary("libadwaita-1", dynamic_link_opts); if (self.config.x11) { - step.linkSystemLibrary2("X11", dynamic_link_opts); + step.root_module.linkSystemLibrary("X11", dynamic_link_opts); if (gobject_) |gobject| { step.root_module.addImport( "gdk_x11", @@ -723,24 +725,24 @@ fn addGtkNg( // IMPORTANT: gtk4-layer-shell must be linked BEFORE // wayland-client, as it relies on shimming libwayland's APIs. if (b.systemIntegrationOption("gtk4-layer-shell", .{})) { - step.linkSystemLibrary2("gtk4-layer-shell-0", dynamic_link_opts); + step.root_module.linkSystemLibrary("gtk4-layer-shell-0", dynamic_link_opts); } else { // gtk4-layer-shell *must* be dynamically linked, // so we don't add it as a static library const shared_lib = gtk4_layer_shell.artifact("gtk4-layer-shell"); b.installArtifact(shared_lib); - step.linkLibrary(shared_lib); + step.root_module.linkLibrary(shared_lib); } } - step.linkSystemLibrary2("wayland-client", dynamic_link_opts); + step.root_module.linkSystemLibrary("wayland-client", dynamic_link_opts); } { // Get our gresource c/h files and add them to our build. const dist = gtkNgDistResources(b); - step.addCSourceFile(.{ .file = dist.resources_c.path(b), .flags = &.{} }); - step.addIncludePath(dist.resources_h.path(b).dirname()); + step.root_module.addCSourceFile(.{ .file = dist.resources_c.path(b), .flags = &.{} }); + step.root_module.addIncludePath(dist.resources_h.path(b).dirname()); } } @@ -798,7 +800,7 @@ pub fn addSimd( const HWY_AVX3_DL: c_int = 1 << 7; const HWY_AVX3: c_int = 1 << 8; - var flags: std.ArrayListUnmanaged([]const u8) = .empty; + var flags: std.ArrayList([]const u8) = .empty; // Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414 // To workaround this we just disable AVX512 support completely. @@ -881,11 +883,13 @@ pub fn gtkNgDistResources( .root_module = b.createModule(.{ .root_source_file = b.path("src/apprt/gtk/build/blueprint.zig"), .target = b.graph.host, + .link_libc = true, }), }); - blueprint_exe.linkLibC(); - blueprint_exe.linkSystemLibrary2("gtk4", dynamic_link_opts); - blueprint_exe.linkSystemLibrary2("libadwaita-1", dynamic_link_opts); + if (b.lazyDependency("gobject", .{})) |gobject| + blueprint_exe.root_module.addImport("adw", gobject.module("adw1")); + blueprint_exe.root_module.linkSystemLibrary("gtk4", dynamic_link_opts); + blueprint_exe.root_module.linkSystemLibrary("libadwaita-1", dynamic_link_opts); for (gresource.blueprints) |bp| { const blueprint_run = b.addRunArtifact(blueprint_exe); @@ -914,7 +918,7 @@ pub fn gtkNgDistResources( xml_run.addFileArg(ui_file); } - break :gresource_xml xml_run.captureStdOut(); + break :gresource_xml xml_run.captureStdOut(.{}); }; const generate_c = b.addSystemCommand(&.{ diff --git a/src/build/UnicodeTables.zig b/src/build/UnicodeTables.zig index 17a839eaf1f..9193562dcd0 100644 --- a/src/build/UnicodeTables.zig +++ b/src/build/UnicodeTables.zig @@ -53,9 +53,8 @@ pub fn init(b: *std.Build, uucode_tables: std.Build.LazyPath) !UnicodeTables { const symbols_run = b.addRunArtifact(symbols_exe); // Generated Zig files have to end with .zig - const wf = b.addWriteFiles(); - const props_output = wf.addCopyFile(props_run.captureStdOut(), "props.zig"); - const symbols_output = wf.addCopyFile(symbols_run.captureStdOut(), "symbols.zig"); + const props_output = props_run.captureStdOut(.{ .basename = "props.zig" }); + const symbols_output = symbols_run.captureStdOut(.{ .basename = "symbols.zig" }); return .{ .props_exe = props_exe, diff --git a/src/build/XCFrameworkStep.zig b/src/build/XCFrameworkStep.zig index 39f0f9bacca..5f1b676fb73 100644 --- a/src/build/XCFrameworkStep.zig +++ b/src/build/XCFrameworkStep.zig @@ -63,8 +63,8 @@ pub fn create(b: *std.Build, opts: Options) *XCFrameworkStep { run.addArg("-output"); run.addArg(opts.out_path); run.expectExitCode(0); - _ = run.captureStdOut(); - _ = run.captureStdErr(); + _ = run.captureStdOut(.{}); + _ = run.captureStdErr(.{}); break :run run; }; run_create.step.dependOn(&run_delete.step); diff --git a/src/build/combine_archives.zig b/src/build/combine_archives.zig index 04ec8e97804..dadb6e23f26 100644 --- a/src/build/combine_archives.zig +++ b/src/build/combine_archives.zig @@ -11,11 +11,10 @@ const std = @import("std"); -pub fn main() !void { - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - const alloc = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const alloc = init.arena.allocator(); + const args = try init.minimal.args.toSlice(alloc); - const args = try std.process.argsAlloc(alloc); if (args.len < 4) { std.log.err("usage: combine_archives ", .{}); std.process.exit(1); @@ -26,30 +25,27 @@ pub fn main() !void { const inputs = args[3..]; // Build the MRI script. - var script: std.ArrayListUnmanaged(u8) = .empty; - try script.appendSlice(alloc, "CREATE "); - try script.appendSlice(alloc, output_path); - try script.append(alloc, '\n'); + var script: std.Io.Writer.Allocating = .init(alloc); + try script.writer.print("CREATE {s}\n", .{output_path}); for (inputs) |input| { - try script.appendSlice(alloc, "ADDLIB "); - try script.appendSlice(alloc, input); - try script.append(alloc, '\n'); + try script.writer.print("ADDLIB {s}\n", .{input}); } - try script.appendSlice(alloc, "SAVE\nEND\n"); + try script.writer.writeAll("SAVE\nEND\n"); - var child: std.process.Child = .init(&.{ zig_exe, "ar", "-M" }, alloc); - child.stdin_behavior = .Pipe; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + var child = try std.process.spawn(init.io, .{ + .argv = &.{ zig_exe, "ar", "-M" }, + .stdin = .pipe, + .stdout = .inherit, + .stderr = .inherit, + }); - try child.spawn(); - try child.stdin.?.writeAll(script.items); - child.stdin.?.close(); + try child.stdin.?.writeStreamingAll(init.io, script.written()); + child.stdin.?.close(init.io); child.stdin = null; - const term = try child.wait(); - if (term.Exited != 0) { - std.log.err("zig ar -M exited with code {d}", .{term.Exited}); + const term = try child.wait(init.io); + if (term.exited != 0) { + std.log.err("zig ar -M exited with code {d}", .{term.exited}); std.process.exit(1); } } diff --git a/src/build/gtk.zig b/src/build/gtk.zig index 7adb3cdb702..1035367e122 100644 --- a/src/build/gtk.zig +++ b/src/build/gtk.zig @@ -14,7 +14,7 @@ pub fn targets(b: *std.Build) Targets { const output = b.runAllowFail( &.{ "pkg-config", "--variable=targets", "gtk4" }, &code, - .Ignore, + .ignore, ) catch return .{}; const x11 = std.mem.indexOf(u8, output, "x11") != null; diff --git a/src/build/mdgen/main_ghostty_1.zig b/src/build/mdgen/main_ghostty_1.zig index 2bb413d9386..e511768c916 100644 --- a/src/build/mdgen/main_ghostty_1.zig +++ b/src/build/mdgen/main_ghostty_1.zig @@ -1,12 +1,11 @@ const std = @import("std"); const gen = @import("mdgen.zig"); -pub fn main() !void { - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - const alloc = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const alloc = init.arena.allocator(); var buffer: [1024]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&buffer); + var stdout_writer = std.Io.File.stdout().writer(init.io, &buffer); const writer = &stdout_writer.interface; try gen.substitute(alloc, @embedFile("ghostty_1_header.md"), writer); try gen.genActions(writer); diff --git a/src/build/mdgen/main_ghostty_5.zig b/src/build/mdgen/main_ghostty_5.zig index 2123b0bce57..4f7c4882a4f 100644 --- a/src/build/mdgen/main_ghostty_5.zig +++ b/src/build/mdgen/main_ghostty_5.zig @@ -1,12 +1,11 @@ const std = @import("std"); const gen = @import("mdgen.zig"); -pub fn main() !void { - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - const alloc = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const alloc = init.arena.allocator(); var buffer: [1024]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&buffer); + var stdout_writer = std.Io.File.stdout().writer(init.io, &buffer); const writer = &stdout_writer.interface; try gen.substitute(alloc, @embedFile("ghostty_5_header.md"), writer); try gen.genConfig(writer, false); diff --git a/src/build/uucode_config.zig b/src/build/uucode_config.zig index 2bb0d4508b0..b8362852099 100644 --- a/src/build/uucode_config.zig +++ b/src/build/uucode_config.zig @@ -1,109 +1,128 @@ const std = @import("std"); const assert = std.debug.assert; const config = @import("config.zig"); -const config_x = @import("config.x.zig"); -const d = config.default; -const wcwidth = config_x.wcwidth; -const grapheme_break_no_control = config_x.grapheme_break_no_control; const Allocator = std.mem.Allocator; -fn computeWidth( - alloc: std.mem.Allocator, - cp: u21, - data: anytype, - backing: anytype, - tracking: anytype, -) Allocator.Error!void { - _ = alloc; - _ = cp; - _ = backing; - _ = tracking; - - // This condition is needed as Ghostty currently has a singular concept for - // the `width` of a code point, while `uucode` splits the concept into - // `wcwidth_standalone` and `wcwidth_zero_in_grapheme`. The two cases where - // we want to use the `wcwidth_standalone` despite the code point occupying - // zero width in a grapheme (`wcwidth_zero_in_grapheme`) are emoji - // modifiers and prepend code points. For emoji modifiers we want to - // support displaying them in isolation as color patches, and if prepend - // characters were to be width 0 they would disappear from the output with - // Ghostty's current width 0 handling. Future work will take advantage of - // the new uucode `wcwidth_standalone` vs `wcwidth_zero_in_grapheme` split. - if (data.wcwidth_zero_in_grapheme and !data.is_emoji_modifier and data.grapheme_break_no_control != .prepend) { - data.width = 0; - } else { - data.width = @min(2, data.wcwidth_standalone); - } -} - -const width = config.Extension{ - .inputs = &.{ - "wcwidth_standalone", - "wcwidth_zero_in_grapheme", - "is_emoji_modifier", - "grapheme_break_no_control", +pub const fields = &config.mergeFields(config.fields, &.{ + .{ .name = "width", .type = u2 }, + .{ .name = "is_symbol", .type = bool }, +}); +pub const build_components = &config.mergeComponents(config.build_components, &.{ + .{ + .Impl = WidthComponent, + .inputs = &.{ + "wcwidth_standalone", + "wcwidth_zero_in_grapheme", + "is_emoji_modifier", + "grapheme_break_no_control", + }, + .fields = &.{"width"}, }, - .compute = &computeWidth, - .fields = &.{ - .{ .name = "width", .type = u2 }, + .{ + .Impl = IsSymbolComponent, + .inputs = &.{ "block", "general_category" }, + .fields = &.{"is_symbol"}, }, -}; +}); -fn computeIsSymbol( - alloc: Allocator, - cp: u21, - data: anytype, - backing: anytype, - tracking: anytype, -) Allocator.Error!void { - _ = alloc; - _ = cp; - _ = backing; - _ = tracking; - const block = data.block; - data.is_symbol = data.general_category == .other_private_use or - block == .arrows or - block == .dingbats or - block == .emoticons or - block == .miscellaneous_symbols or - block == .enclosed_alphanumerics or - block == .enclosed_alphanumeric_supplement or - block == .miscellaneous_symbols_and_pictographs or - block == .transport_and_map_symbols; -} - -const is_symbol = config.Extension{ - .inputs = &.{ "block", "general_category" }, - .compute = &computeIsSymbol, - .fields = &.{ - .{ .name = "is_symbol", .type = bool }, - }, -}; +pub const get_components: []const config.Component = &.{}; pub const tables = [_]config.Table{ .{ .name = "runtime", - .extensions = &.{}, .fields = &.{ - d.field("is_emoji_presentation"), - d.field("case_folding_full"), + "is_emoji_presentation", + "case_folding_full", }, }, .{ .name = "buildtime", - .extensions = &.{ - wcwidth, - grapheme_break_no_control, - width, - is_symbol, - }, .fields = &.{ - width.field("width"), - wcwidth.field("wcwidth_zero_in_grapheme"), - grapheme_break_no_control.field("grapheme_break_no_control"), - is_symbol.field("is_symbol"), - d.field("is_emoji_vs_base"), + "width", + "wcwidth_zero_in_grapheme", + "grapheme_break_no_control", + "is_symbol", + "is_emoji_vs_base", }, }, }; + +const WidthComponent = struct { + pub fn build( + comptime InputRow: type, + comptime Row: type, + allocator: std.mem.Allocator, + io: std.Io, + inputs: config.MultiSlice(InputRow), + rows: *config.MultiSlice(Row), + backing: anytype, + tracking: anytype, + ) !void { + _ = allocator; + _ = io; + _ = backing; + _ = tracking; + + rows.len = config.num_code_points; + const items = rows.items(.width); + const standalone = inputs.items(.wcwidth_standalone); + const zero_in_grapheme = inputs.items(.wcwidth_zero_in_grapheme); + const is_emoji_modifier = inputs.items(.is_emoji_modifier); + const grapheme_break_no_control = inputs.items(.grapheme_break_no_control); + + // This condition is needed as Ghostty currently has a singular concept for + // the `width` of a code point, while `uucode` splits the concept into + // `wcwidth_standalone` and `wcwidth_zero_in_grapheme`. The two cases where + // we want to use the `wcwidth_standalone` despite the code point occupying + // zero width in a grapheme (`wcwidth_zero_in_grapheme`) are emoji + // modifiers and prepend code points. For emoji modifiers we want to + // support displaying them in isolation as color patches, and if prepend + // characters were to be width 0 they would disappear from the output with + // Ghostty's current width 0 handling. Future work will take advantage of + // the new uucode `wcwidth_standalone` vs `wcwidth_zero_in_grapheme` split. + for (0..config.num_code_points) |i| { + if (zero_in_grapheme[i] and !is_emoji_modifier[i] and grapheme_break_no_control[i] != .prepend) { + items[i] = 0; + } else { + items[i] = @min(2, standalone[i]); + } + } + } +}; + +const IsSymbolComponent = struct { + pub fn build( + comptime InputRow: type, + comptime Row: type, + allocator: std.mem.Allocator, + io: std.Io, + inputs: config.MultiSlice(InputRow), + rows: *config.MultiSlice(Row), + backing: anytype, + tracking: anytype, + ) !void { + _ = allocator; + _ = io; + _ = backing; + _ = tracking; + + rows.len = config.num_code_points; + const items = rows.items(.is_symbol); + const block = inputs.items(.block); + const general_category = inputs.items(.general_category); + + for (0..config.num_code_points) |i| { + items[i] = + general_category[i] == .other_private_use or + block[i] == .arrows or + block[i] == .dingbats or + block[i] == .emoticons or + block[i] == .miscellaneous_symbols or + block[i] == .enclosed_alphanumerics or + block[i] == .enclosed_alphanumeric_supplement or + block[i] == .miscellaneous_symbols_and_pictographs or + block[i] == .transport_and_map_symbols; + } + } +}; diff --git a/src/build/wasm_patch_growable_table.zig b/src/build/wasm_patch_growable_table.zig index c40c0a9c823..e842622e03b 100644 --- a/src/build/wasm_patch_growable_table.zig +++ b/src/build/wasm_patch_growable_table.zig @@ -12,15 +12,11 @@ const std = @import("std"); const testing = std.testing; const Allocator = std.mem.Allocator; -pub fn main() !void { - // This is a one-off patcher, so we leak all our memory on purpose - // and let the OS clean it up when we exit. - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - const alloc = gpa.allocator(); - +pub fn main(init: std.process.Init) !void { // Parse args: program input output - const args = try std.process.argsAlloc(alloc); - defer std.process.argsFree(alloc, args); + const alloc = init.arena.allocator(); + const args = try init.minimal.args.toSlice(alloc); + if (args.len != 3) { std.log.err("usage: wasm_growable_table ", .{}); std.process.exit(1); @@ -30,15 +26,15 @@ pub fn main() !void { // Patch the file. const output: []const u8 = try patchTableGrowable( alloc, - try std.fs.cwd().readFileAlloc( - alloc, + try std.Io.Dir.cwd().readFileAlloc( args[1], - std.math.maxInt(usize), + alloc, + .none, ), ); // Write our output - const out_file = try std.fs.cwd().createFile(args[2], .{}); + const out_file = try std.Io.Dir.cwd().createFile(args[2], .{}); defer out_file.close(); try out_file.writeAll(output); } diff --git a/src/build/webgen/main_actions.zig b/src/build/webgen/main_actions.zig index b0de6537dd3..38cda3f7a2c 100644 --- a/src/build/webgen/main_actions.zig +++ b/src/build/webgen/main_actions.zig @@ -1,9 +1,9 @@ const std = @import("std"); const helpgen_actions = @import("../../input/helpgen_actions.zig"); -pub fn main() !void { +pub fn main(init: std.process.Init) !void { var buffer: [2048]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&buffer); + var stdout_writer = std.Io.File.stdout().writer(init.io, &buffer); const stdout = &stdout_writer.interface; try helpgen_actions.generate(stdout, .markdown, true, std.heap.page_allocator); } diff --git a/src/build/webgen/main_commands.zig b/src/build/webgen/main_commands.zig index 65f1445220d..c895948d48c 100644 --- a/src/build/webgen/main_commands.zig +++ b/src/build/webgen/main_commands.zig @@ -2,9 +2,9 @@ const std = @import("std"); const Action = @import("../../cli/ghostty.zig").Action; const help_strings = @import("help_strings"); -pub fn main() !void { +pub fn main(init: std.process.Init) !void { var buffer: [2048]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&buffer); + var stdout_writer = std.Io.File.stdout().writer(init.io, &buffer); const stdout = &stdout_writer.interface; try genActions(stdout); } diff --git a/src/build/webgen/main_config.zig b/src/build/webgen/main_config.zig index 1363fadc417..82ecc950fce 100644 --- a/src/build/webgen/main_config.zig +++ b/src/build/webgen/main_config.zig @@ -2,9 +2,9 @@ const std = @import("std"); const Config = @import("../../config/Config.zig"); const help_strings = @import("help_strings"); -pub fn main() !void { +pub fn main(init: std.process.Init) !void { var buffer: [2048]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&buffer); + var stdout_writer = std.Io.File.stdout().writer(init.io, &buffer); const stdout = &stdout_writer.interface; try genConfig(stdout); } diff --git a/src/build_config.zig b/src/build_config.zig index c19f7372b11..c9c6da676ce 100644 --- a/src/build_config.zig +++ b/src/build_config.zig @@ -42,6 +42,7 @@ pub const app_runtime: apprt.Runtime = config.app_runtime; pub const font_backend: font.Backend = config.font_backend; pub const renderer: rendererpkg.Backend = config.renderer; pub const i18n: bool = config.i18n; +pub const vaxis: bool = config.vaxis; /// The bundle ID for the app. This is used in many places and is currently /// hardcoded here. We could make this configurable in the future if there diff --git a/src/cli/Pager.zig b/src/cli/Pager.zig index 247c998e10e..276cb7b9039 100644 --- a/src/cli/Pager.zig +++ b/src/cli/Pager.zig @@ -15,70 +15,73 @@ child: ?std.process.Child = null, /// The buffered file writer used for both the pager pipe and direct /// stdout paths. -file_writer: std.fs.File.Writer = undefined, +file_writer: std.Io.File.Writer = undefined, /// Initialize the pager. If stdout is a TTY, this spawns the pager /// process. Otherwise, output goes directly to stdout. -pub fn init(alloc: Allocator) Pager { - return .{ .child = initPager(alloc) }; +pub fn init(io: std.Io, env: *const std.process.Environ.Map) Pager { + return .{ + .child = initPager(io, env), + }; } /// Writes to the pager process if available; otherwise, stdout. -pub fn writer(self: *Pager, buffer: []u8) *std.Io.Writer { +pub fn writer(self: *Pager, io: std.Io, buffer: []u8) *std.Io.Writer { if (self.child) |child| { - self.file_writer = child.stdin.?.writer(buffer); + self.file_writer = child.stdin.?.writer(io, buffer); } else { - self.file_writer = std.fs.File.stdout().writer(buffer); + self.file_writer = std.Io.File.stdout().writer(io, buffer); } return &self.file_writer.interface; } /// Deinitialize the pager. Waits for the spawned process to exit. -pub fn deinit(self: *Pager) void { +pub fn deinit(self: *Pager, io: std.Io) void { if (self.child) |*child| { // Flush any remaining buffered data, close the pipe so the // pager sees EOF, then wait for it to exit. self.file_writer.interface.flush() catch {}; if (child.stdin) |stdin| { - stdin.close(); + stdin.close(io); child.stdin = null; } - _ = child.wait() catch {}; + _ = child.wait(io) catch {}; } self.* = undefined; } -fn initPager(alloc: Allocator) ?std.process.Child { - const stdout_file: std.fs.File = .stdout(); - if (!stdout_file.isTty()) return null; +fn initPager(io: std.Io, env: *const std.process.Environ.Map) ?std.process.Child { + const stdout_file: std.Io.File = .stdout(); + const is_tty = stdout_file.isTty(io) catch return null; + if (!is_tty) return null; // Resolve the pager command: $GHOSTTY_PAGER > $PAGER > `less`. // An empty value for either env var disables paging. - const ghostty_var = internal_os.getenv(alloc, "GHOSTTY_PAGER") catch null; - defer if (ghostty_var) |v| v.deinit(alloc); - const pager_var = internal_os.getenv(alloc, "PAGER") catch null; - defer if (pager_var) |v| v.deinit(alloc); - - const cmd: ?[]const u8 = cmd: { - if (ghostty_var) |v| break :cmd if (v.value.len > 0) v.value else null; - if (pager_var) |v| break :cmd if (v.value.len > 0) v.value else null; + const cmd: []const u8 = cmd: { + if (env.get("GHOSTTY_PAGER")) |v| { + if (v.len > 0) break :cmd v else return null; + } + if (env.get("PAGER")) |v| { + if (v.len > 0) break :cmd v else return null; + } break :cmd "less"; }; - if (cmd == null) return null; - - var child: std.process.Child = .init(&.{cmd.?}, alloc); - child.stdin_behavior = .Pipe; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; - - child.spawn() catch return null; - return child; + return std.process.spawn(io, .{ + .argv = &.{cmd}, + .stdin = .pipe, + .stdout = .inherit, + .stderr = .inherit, + }) catch null; } test "pager: non-tty" { - var pager: Pager = .init(std.testing.allocator); + var pager: Pager = .init( + std.testing.allocator, + std.testing.io, + std.testing.environ, + ); defer pager.deinit(); try std.testing.expect(pager.child == null); } diff --git a/src/cli/action.zig b/src/cli/action.zig index 41173a9f1f1..94215715c7d 100644 --- a/src/cli/action.zig +++ b/src/cli/action.zig @@ -11,8 +11,8 @@ pub const DetectError = error{ }; /// Detect the action from CLI args. -pub fn detectArgs(comptime E: type, alloc: Allocator) !?E { - var iter = try std.process.argsWithAllocator(alloc); +pub fn detectArgs(comptime E: type, alloc: Allocator, args: std.process.Args) !?E { + var iter = try args.iterateAllocator(alloc); defer iter.deinit(); return try detectIter(E, &iter); } @@ -29,7 +29,7 @@ pub fn detectArgs(comptime E: type, alloc: Allocator) !?E { /// pub fn detectIter( comptime E: type, - iter: anytype, + iter: *std.process.Args.Iterator, ) DetectError!?E { var fallback: ?E = null; var pending: ?E = null; @@ -83,7 +83,7 @@ test "detect direct match" { const alloc = testing.allocator; const Enum = enum { foo, bar, baz }; - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "+foo", ); @@ -97,7 +97,7 @@ test "detect invalid match" { const alloc = testing.allocator; const Enum = enum { foo, bar, baz }; - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "+invalid", ); @@ -113,7 +113,7 @@ test "detect multiple actions" { const alloc = testing.allocator; const Enum = enum { foo, bar, baz }; - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "+foo +bar", ); @@ -129,7 +129,7 @@ test "detect no match" { const alloc = testing.allocator; const Enum = enum { foo, bar, baz }; - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "--some-flag", ); @@ -154,7 +154,7 @@ test "detect special case action" { }; { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "--special +bar", ); @@ -164,7 +164,7 @@ test "detect special case action" { } { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "+bar --special", ); @@ -174,7 +174,7 @@ test "detect special case action" { } { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "+bar", ); @@ -200,7 +200,7 @@ test "detect special case fallback" { }; { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "--special", ); @@ -210,7 +210,7 @@ test "detect special case fallback" { } { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "+bar --special", ); @@ -220,7 +220,7 @@ test "detect special case fallback" { } { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "--special +bar", ); @@ -246,7 +246,7 @@ test "detect special case abort_if_no_action" { }; { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "-e", ); @@ -256,7 +256,7 @@ test "detect special case abort_if_no_action" { } { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "+foo -e", ); @@ -266,7 +266,7 @@ test "detect special case abort_if_no_action" { } { - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( alloc, "-e +bar", ); diff --git a/src/cli/args.zig b/src/cli/args.zig index bd5060d69bd..b351ed3dc66 100644 --- a/src/cli/args.zig +++ b/src/cli/args.zig @@ -55,6 +55,8 @@ pub const Error = error{ pub fn parse( comptime T: type, alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, dst: *T, iter: anytype, ) !void { @@ -90,6 +92,8 @@ pub fn parse( if (@hasDecl(T, "parseManuallyHook")) { if (!try dst.parseManuallyHook( arena_alloc, + io, + env, arg, iter, )) return; @@ -489,18 +493,13 @@ pub fn parseTaggedUnion(comptime T: type, alloc: Allocator, v: []const u8) !T { // We need to create a struct that looks like this union field. // This lets us use parseIntoField as if its a dedicated struct. - const Target = @Type(.{ .@"struct" = .{ - .layout = .auto, - .fields = &.{.{ - .name = field.name, - .type = field.type, - .default_value_ptr = null, - .is_comptime = false, - .alignment = @alignOf(field.type), - }}, - .decls = &.{}, - .is_tuple = false, - } }); + const Target = @Struct( + .auto, + null, + &.{field.name}, + &.{field.type}, + &.{.{ .@"align" = @alignOf(field.type) }}, + ); // Parse the value into the struct var t: Target = undefined; @@ -677,7 +676,7 @@ test "parse: simple" { } = .{}; defer if (data._arena) |arena| arena.deinit(); - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( testing.allocator, "--a=42 --b --b-f=false", ); @@ -689,7 +688,7 @@ test "parse: simple" { try testing.expect(!data.@"b-f"); // Reparsing works - var iter2 = try std.process.ArgIteratorGeneral(.{}).init( + var iter2 = try std.process.Args.IteratorGeneral(.{}).init( testing.allocator, "--a=84", ); @@ -711,7 +710,7 @@ test "parse: quoted value" { } = .{}; defer if (data._arena) |arena| arena.deinit(); - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( testing.allocator, "--a=\"42\" --b=\"hello!\"", ); @@ -731,7 +730,7 @@ test "parse: empty value resets to default" { } = .{}; defer if (data._arena) |arena| arena.deinit(); - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( testing.allocator, "--a= --b=", ); @@ -750,7 +749,7 @@ test "parse: positional arguments are invalid" { } = .{}; defer if (data._arena) |arena| arena.deinit(); - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( testing.allocator, "--a=84 what", ); @@ -774,7 +773,7 @@ test "parse: diagnostic tracking" { } = .{}; defer if (data._arena) |arena| arena.deinit(); - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( testing.allocator, "--what --a=42", ); @@ -858,7 +857,7 @@ test "parse: compatibility handler" { } = .{}; defer if (data._arena) |arena| arena.deinit(); - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( testing.allocator, "--a=yuh", ); @@ -884,7 +883,7 @@ test "parse: compatibility renamed" { } = .{}; defer if (data._arena) |arena| arena.deinit(); - var iter = try std.process.ArgIteratorGeneral(.{}).init( + var iter = try std.process.Args.IteratorGeneral(.{}).init( testing.allocator, "--old=true --b=true", ); @@ -1363,8 +1362,8 @@ pub fn ArgsIterator(comptime Iterator: type) type { } /// Create an args iterator for the process args. This will skip argv0. -pub fn argsIterator(alloc_gpa: Allocator) internal_os.args.ArgIterator.InitError!ArgsIterator(internal_os.args.ArgIterator) { - var iter = try internal_os.args.iterator(alloc_gpa); +pub fn argsIterator(args: std.process.Args, alloc: Allocator) internal_os.args.ArgIterator.InitError!ArgsIterator(internal_os.args.ArgIterator) { + var iter = try internal_os.args.iterator(args, alloc); errdefer iter.deinit(); _ = iter.next(); // skip argv0 return .{ .iterator = iter }; @@ -1373,7 +1372,7 @@ pub fn argsIterator(alloc_gpa: Allocator) internal_os.args.ArgIterator.InitError test "ArgsIterator" { const testing = std.testing; - const child = try std.process.ArgIteratorGeneral(.{}).init( + const child = try std.process.Args.IteratorGeneral(.{}).init( testing.allocator, "--what +list-things --a=42", ); diff --git a/src/cli/boo.zig b/src/cli/boo.zig index 2834eadbd3d..496f7ac64fe 100644 --- a/src/cli/boo.zig +++ b/src/cli/boo.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const build_config = @import("../build_config.zig"); const builtin = @import("builtin"); const args = @import("args.zig"); const Action = @import("ghostty.zig").Action; @@ -173,6 +174,8 @@ const Boo = struct { /// The `boo` command is used to display the animation from the Ghostty website in the terminal pub fn run(gpa: Allocator) !u8 { + if (comptime !build_config.vaxis) return 1; + // Disable on non-desktop systems. switch (builtin.os.tag) { .windows, .macos, .linux, .freebsd => {}, diff --git a/src/cli/crash_report.zig b/src/cli/crash_report.zig index f0940fdabb1..0aa3d2fe10a 100644 --- a/src/cli/crash_report.zig +++ b/src/cli/crash_report.zig @@ -23,7 +23,11 @@ pub const Options = struct { /// /// This command currently only supports listing crash reports. Viewing /// and sending crash reports is unimplemented and will be added in the future. -pub fn run(alloc_gpa: Allocator) !u8 { +pub fn run( + alloc_gpa: Allocator, + io: std.Io, + proc_args: std.process.Args, +) !u8 { // Use an arena for the whole command to avoid manual memory management. var arena = std.heap.ArenaAllocator.init(alloc_gpa); defer arena.deinit(); @@ -33,27 +37,28 @@ pub fn run(alloc_gpa: Allocator) !u8 { defer opts.deinit(); { - var iter = try args.argsIterator(alloc_gpa); + var iter = try args.argsIterator(proc_args, alloc_gpa); defer iter.deinit(); try args.parse(Options, alloc_gpa, &opts, &iter); } var buffer: [1024]u8 = undefined; - var stdout_file: std.fs.File = .stdout(); - var stdout_writer = stdout_file.writer(&buffer); + var stdout_file: std.Io.File = .stdout(); + var stdout_writer = stdout_file.writer(io, &buffer); const stdout = &stdout_writer.interface; - const result = runInner(alloc, &stdout_file, stdout); + const result = runInner(io, alloc, &stdout_file, stdout); stdout.flush() catch {}; return result; } fn runInner( + io: std.Io, alloc: Allocator, - stdout_file: *std.fs.File, + stdout_file: *std.Io.File, stdout: *std.Io.Writer, ) !u8 { - const crash_dir = try crash.defaultDir(alloc); + const crash_dir = try crash.defaultDir(io, alloc); var reports: std.ArrayList(crash.Report) = .empty; errdefer reports.deinit(alloc); @@ -66,7 +71,7 @@ fn runInner( // If we have no reports, then we're done. If we have a tty then we // print a message, otherwise we do nothing. if (reports.items.len == 0) { - if (std.posix.isatty(stdout_file.handle)) { + if (stdout_file.isTty(io) catch false) { try stdout.writeAll("No crash reports! 👻\n"); } return 0; @@ -76,7 +81,7 @@ fn runInner( for (reports.items) |report| { var buf: [128]u8 = undefined; - const now = std.time.nanoTimestamp(); + const now = std.Io.Timestamp.now(io, .real).toNanoseconds(); const diff = now - report.mtime; const since = if (diff <= 0) "now" else s: { const d = Config.Duration{ .duration = @intCast(diff) }; diff --git a/src/cli/diagnostics.zig b/src/cli/diagnostics.zig index 7f4dcc45e0a..0e4048a0120 100644 --- a/src/cli/diagnostics.zig +++ b/src/cli/diagnostics.zig @@ -93,7 +93,7 @@ pub const Location = union(enum) { /// and potentially in the future structure them differently. pub const DiagnosticList = struct { /// The list of diagnostics. - list: std.ArrayListUnmanaged(Diagnostic) = .{}, + list: std.ArrayList(Diagnostic) = .empty, /// Precomputed data for diagnostics. This is used specifically /// when we build libghostty so that we can precompute the messages @@ -111,7 +111,7 @@ pub const DiagnosticList = struct { }; const Precompute = if (precompute_enabled) struct { - messages: std.ArrayListUnmanaged([:0]const u8) = .{}, + messages: std.ArrayList([:0]const u8) = .empty, pub fn clone( self: *const Precompute, diff --git a/src/cli/edit_config.zig b/src/cli/edit_config.zig index c08651a06cf..aea8bae79ac 100644 --- a/src/cli/edit_config.zig +++ b/src/cli/edit_config.zig @@ -40,7 +40,12 @@ pub const Options = struct { /// This command prefers the `$VISUAL` environment variable over `$EDITOR`, /// if both are set. If neither are set, it will print an error /// and exit. -pub fn run(alloc: Allocator) !u8 { +pub fn run( + alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, + proc_args: std.process.Args, +) !u8 { // Implementation note (by @mitchellh): I do proper memory cleanup // throughout this command, even though we plan on doing `exec`. // I do this out of good hygiene in case we ever change this to @@ -48,32 +53,52 @@ pub fn run(alloc: Allocator) !u8 { // critical where setting up the defer cleanup is a problem. var buffer: [1024]u8 = undefined; - var stderr_writer = std.fs.File.stderr().writer(&buffer); + var stderr_writer = std.Io.File.stderr().writer(io, &buffer); const stderr = &stderr_writer.interface; var opts: Options = .{}; defer opts.deinit(); { - var iter = try args.argsIterator(alloc); + var iter = try args.argsIterator( + proc_args, + alloc, + ); defer iter.deinit(); try args.parse(Options, alloc, &opts, &iter); } - const result = runInner(alloc, stderr); + const result = runInner( + alloc, + io, + env, + proc_args, + stderr, + ); // Flushing *shouldn't* fail but... stderr.flush() catch {}; return result; } -fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 { +fn runInner( + alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, + proc_args: std.process.Args, + stderr: *std.Io.Writer, +) !u8 { // We load the configuration once because that will write our // default configuration files to disk. We don't use the config. - var config = try Config.load(alloc); + var config = try Config.load( + alloc, + io, + proc_args, + env, + ); defer config.deinit(); // Find the preferred path. - const path = try configpkg.preferredDefaultFilePath(alloc); + const path = try configpkg.preferredDefaultFilePath(alloc, io, env); defer alloc.free(path); // We don't currently support Windows because we use the exec syscall. @@ -91,22 +116,17 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 { } // Get our editor - const get_env_: ?internal_os.GetEnvResult = env: { + const get_env_: ?[]const u8 = env: { // VISUAL vs. EDITOR: https://unix.stackexchange.com/questions/4859/visual-vs-editor-what-s-the-difference - if (try internal_os.getenv(alloc, "VISUAL")) |v| { - if (v.value.len > 0) break :env v; - v.deinit(alloc); + if (env.get("VISUAL")) |v| { + if (v.len > 0) break :env v; } - - if (try internal_os.getenv(alloc, "EDITOR")) |v| { - if (v.value.len > 0) break :env v; - v.deinit(alloc); + if (env.get("EDITOR")) |v| { + if (v.len > 0) break :env v; } - break :env null; }; - defer if (get_env_) |v| v.deinit(alloc); - const editor: []const u8 = if (get_env_) |v| v.value else ""; + const editor: []const u8 = get_env_ orelse ""; // If we don't have `$EDITOR` set then we can't do anything // but we can still print a helpful message. @@ -137,7 +157,7 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 { } const command = command: { - var buffer: std.io.Writer.Allocating = .init(alloc); + var buffer: std.Io.Writer.Allocating = .init(alloc); defer buffer.deinit(); const writer = &buffer.writer; try writer.writeAll(editor); @@ -158,11 +178,10 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 { // so this is not a big deal. comptime assert(builtin.link_libc); - const err = std.posix.execvpeZ( - "/bin/sh", - &.{ "/bin/sh", "-c", command }, - std.c.environ, - ); + const err = std.process.replace(io, .{ + .argv = &.{ "/bin/sh", "-c", command }, + .environ_map = env, + }); // If we reached this point then exec failed. try stderr.print( diff --git a/src/cli/explain_config.zig b/src/cli/explain_config.zig index 4f034afef6b..b611cc229e5 100644 --- a/src/cli/explain_config.zig +++ b/src/cli/explain_config.zig @@ -45,13 +45,18 @@ pub const Options = struct { /// * `--option`: The name of the configuration option to explain. /// * `--keybind`: The name of the keybind action to explain. /// * `--no-pager`: Disable automatic paging of output. -pub fn run(alloc: Allocator) !u8 { +pub fn run( + alloc: Allocator, + io: std.Io, + env: *const std.process.Environ.Map, + proc_args: std.process.Args, +) !u8 { var option_name: ?[]const u8 = null; var keybind_name: ?[]const u8 = null; var positional: ?[]const u8 = null; var no_pager: bool = false; - var iter = try args.argsIterator(alloc); + var iter = try args.argsIterator(proc_args, alloc); defer iter.deinit(); defer if (option_name) |s| alloc.free(s); defer if (keybind_name) |s| alloc.free(s); @@ -75,9 +80,9 @@ pub fn run(alloc: Allocator) !u8 { // respective lookup. A bare positional argument tries config // options first, then keybind actions as a fallback. const name = keybind_name orelse option_name orelse positional orelse { - var stderr: std.fs.File = .stderr(); + var stderr: std.Io.File = .stderr(); var buffer: [4096]u8 = undefined; - var stderr_writer = stderr.writer(&buffer); + var stderr_writer = stderr.writer(io, &buffer); try stderr_writer.interface.writeAll("Usage: ghostty +explain-config