From f8e201c5c4b1ee078219207c3c464c821194b685 Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Wed, 20 Nov 2024 23:58:45 -0500 Subject: [PATCH 01/10] wasm shaping and atlas rendering again --- build.zig | 152 +++++++++++---------- example/app.ts | 169 ++++++++++++------------ example/package-lock.json | 35 ++--- example/package.json | 2 +- pkg/sentry/build.zig | 2 +- src/App.zig | 2 +- src/Surface.zig | 8 +- src/build_config.zig | 4 +- src/cli/args.zig | 2 +- src/config/Config.zig | 24 ++-- src/config/theme.zig | 2 +- src/font/Collection.zig | 21 +++ src/font/DeferredFace.zig | 4 +- src/font/SharedGrid.zig | 48 +++++++ src/font/face/web_canvas.zig | 19 ++- src/font/main.zig | 2 + src/font/shaper/web_canvas.zig | 81 ++++++------ src/main_wasm.zig | 15 ++- src/os/desktop.zig | 2 + src/os/homedir.zig | 2 + src/os/main.zig | 7 + src/terminal/kitty/graphics_image.zig | 4 +- src/terminal/kitty/graphics_storage.zig | 3 +- src/terminal/page.zig | 2 +- src/termio/Exec.zig | 6 +- src/termio/Termio.zig | 4 +- 26 files changed, 358 insertions(+), 264 deletions(-) diff --git a/build.zig b/build.zig index 15fed7ed6..5410b793c 100644 --- a/build.zig +++ b/build.zig @@ -748,7 +748,7 @@ pub fn build(b: *std.Build) !void { break :config copy; }; - const wasm = b.addSharedLibrary(.{ + const wasm = b.addExecutable(.{ .name = "ghostty-wasm", .root_source_file = b.path("src/main_wasm.zig"), .target = b.resolveTargetQuery(wasm_crosstarget), @@ -757,7 +757,10 @@ pub fn build(b: *std.Build) !void { // So that we can use web workers with our wasm binary wasm.import_memory = true; - wasm.initial_memory = 65536 * 25; + wasm.initial_memory = 65536 * 512; + wasm.entry = .disabled; + // wasm.wasi_exec_model = .reactor; + wasm.rdynamic = true; wasm.max_memory = 65536 * 65536; // Maximum number of pages in wasm32 wasm.shared_memory = wasm_shared; @@ -769,10 +772,11 @@ pub fn build(b: *std.Build) !void { // Install const wasm_install = b.addInstallArtifact(wasm, .{}); - wasm_install.dest_dir = .{ .prefix = {} }; + const install = b.addInstallFile(wasm.getEmittedBin(), "../example/ghostty-wasm.wasm"); + wasm_install.step.dependOn(&install.step); const step = b.step("wasm", "Build the wasm library"); - step.dependOn(&wasm_install.step); + step.dependOn(&install.step); // We support tests via wasmtime. wasmtime uses WASI so this // isn't an exact match to our freestanding target above but @@ -1073,81 +1077,82 @@ fn addDeps( try static_libs.append(fontconfig_dep.artifact("fontconfig").getEmittedBin()); } } + if (step.rootModuleTarget().cpu.arch != .wasm32) { + // Libpng - Ghostty doesn't actually use this directly, its only used + // through dependencies, so we only need to add it to our static + // libs list if we're not using system integration. The dependencies + // will handle linking it. + if (!b.systemIntegrationOption("libpng", .{})) { + const libpng_dep = b.dependency("libpng", .{ + .target = target, + .optimize = optimize, + }); + step.linkLibrary(libpng_dep.artifact("png")); + try static_libs.append(libpng_dep.artifact("png").getEmittedBin()); + } - // Libpng - Ghostty doesn't actually use this directly, its only used - // through dependencies, so we only need to add it to our static - // libs list if we're not using system integration. The dependencies - // will handle linking it. - if (!b.systemIntegrationOption("libpng", .{})) { - const libpng_dep = b.dependency("libpng", .{ + // Zlib - same as libpng, only used through dependencies. + if (!b.systemIntegrationOption("zlib", .{})) { + const zlib_dep = b.dependency("zlib", .{ + .target = target, + .optimize = optimize, + }); + step.linkLibrary(zlib_dep.artifact("z")); + try static_libs.append(zlib_dep.artifact("z").getEmittedBin()); + } + + // Oniguruma + const oniguruma_dep = b.dependency("oniguruma", .{ .target = target, .optimize = optimize, }); - step.linkLibrary(libpng_dep.artifact("png")); - try static_libs.append(libpng_dep.artifact("png").getEmittedBin()); - } + step.root_module.addImport("oniguruma", oniguruma_dep.module("oniguruma")); + if (b.systemIntegrationOption("oniguruma", .{})) { + // Oniguruma is compiled and distributed as libonig.so + step.linkSystemLibrary2("onig", dynamic_link_opts); + } else { + step.linkLibrary(oniguruma_dep.artifact("oniguruma")); + try static_libs.append(oniguruma_dep.artifact("oniguruma").getEmittedBin()); + } - // Zlib - same as libpng, only used through dependencies. - if (!b.systemIntegrationOption("zlib", .{})) { - const zlib_dep = b.dependency("zlib", .{ + // Glslang + const glslang_dep = b.dependency("glslang", .{ .target = target, .optimize = optimize, }); - step.linkLibrary(zlib_dep.artifact("z")); - try static_libs.append(zlib_dep.artifact("z").getEmittedBin()); - } + step.root_module.addImport("glslang", glslang_dep.module("glslang")); + if (b.systemIntegrationOption("glslang", .{})) { + step.linkSystemLibrary2("glslang", dynamic_link_opts); + step.linkSystemLibrary2("glslang-default-resource-limits", dynamic_link_opts); + } else { + step.linkLibrary(glslang_dep.artifact("glslang")); + try static_libs.append(glslang_dep.artifact("glslang").getEmittedBin()); + } - // Oniguruma - const oniguruma_dep = b.dependency("oniguruma", .{ - .target = target, - .optimize = optimize, - }); - step.root_module.addImport("oniguruma", oniguruma_dep.module("oniguruma")); - if (b.systemIntegrationOption("oniguruma", .{})) { - // Oniguruma is compiled and distributed as libonig.so - step.linkSystemLibrary2("onig", dynamic_link_opts); - } else { - step.linkLibrary(oniguruma_dep.artifact("oniguruma")); - try static_libs.append(oniguruma_dep.artifact("oniguruma").getEmittedBin()); - } - - // Glslang - const glslang_dep = b.dependency("glslang", .{ - .target = target, - .optimize = optimize, - }); - step.root_module.addImport("glslang", glslang_dep.module("glslang")); - if (b.systemIntegrationOption("glslang", .{})) { - step.linkSystemLibrary2("glslang", dynamic_link_opts); - step.linkSystemLibrary2("glslang-default-resource-limits", dynamic_link_opts); - } else { - step.linkLibrary(glslang_dep.artifact("glslang")); - try static_libs.append(glslang_dep.artifact("glslang").getEmittedBin()); - } - - // Spirv-cross - const spirv_cross_dep = b.dependency("spirv_cross", .{ - .target = target, - .optimize = optimize, - }); - step.root_module.addImport("spirv_cross", spirv_cross_dep.module("spirv_cross")); - if (b.systemIntegrationOption("spirv-cross", .{})) { - step.linkSystemLibrary2("spirv-cross", dynamic_link_opts); - } else { - step.linkLibrary(spirv_cross_dep.artifact("spirv_cross")); - try static_libs.append(spirv_cross_dep.artifact("spirv_cross").getEmittedBin()); - } - - // Simdutf - if (b.systemIntegrationOption("simdutf", .{})) { - step.linkSystemLibrary2("simdutf", dynamic_link_opts); - } else { - const simdutf_dep = b.dependency("simdutf", .{ + // Spirv-cross + const spirv_cross_dep = b.dependency("spirv_cross", .{ .target = target, .optimize = optimize, }); - step.linkLibrary(simdutf_dep.artifact("simdutf")); - try static_libs.append(simdutf_dep.artifact("simdutf").getEmittedBin()); + step.root_module.addImport("spirv_cross", spirv_cross_dep.module("spirv_cross")); + if (b.systemIntegrationOption("spirv-cross", .{})) { + step.linkSystemLibrary2("spirv-cross", dynamic_link_opts); + } else { + step.linkLibrary(spirv_cross_dep.artifact("spirv_cross")); + try static_libs.append(spirv_cross_dep.artifact("spirv_cross").getEmittedBin()); + } + + // Simdutf + if (b.systemIntegrationOption("simdutf", .{})) { + step.linkSystemLibrary2("simdutf", dynamic_link_opts); + } else { + const simdutf_dep = b.dependency("simdutf", .{ + .target = target, + .optimize = optimize, + }); + step.linkLibrary(simdutf_dep.artifact("simdutf")); + try static_libs.append(simdutf_dep.artifact("simdutf").getEmittedBin()); + } } // Sentry @@ -1157,7 +1162,7 @@ fn addDeps( .backend = .breakpad, }); step.root_module.addImport("sentry", sentry_dep.module("sentry")); - if (target.result.os.tag != .windows) { + if (target.result.os.tag != .windows and target.result.cpu.arch != .wasm32) { // Sentry step.linkLibrary(sentry_dep.artifact("sentry")); try static_libs.append(sentry_dep.artifact("sentry").getEmittedBin()); @@ -1177,6 +1182,17 @@ fn addDeps( .optimize = optimize, }); step.root_module.addImport("zig-js", js_dep.module("zig-js")); + step.root_module.addImport("ziglyph", b.dependency("ziglyph", .{ + .target = target, + .optimize = optimize, + }).module("ziglyph")); + step.root_module.addImport("z2d", b.addModule("z2d", .{ + .root_source_file = b.dependency("z2d", .{}).path("src/z2d.zig"), + .target = target, + .optimize = optimize, + })); + // step.linkLibC(); + try addUnicodeTables(b, step); return static_libs; } diff --git a/example/app.ts b/example/app.ts index 5b426a333..0168e030d 100644 --- a/example/app.ts +++ b/example/app.ts @@ -1,11 +1,11 @@ -import { ZigJS } from "zig-js"; +import { ZigJS } from "zig-js/src/index.ts"; const zjs = new ZigJS(); const importObject = { module: {}, env: { memory: new WebAssembly.Memory({ - initial: 25, + initial: 512, maximum: 65536, shared: true, }), @@ -27,38 +27,36 @@ fetch(url.href) .then((results) => { const memory = importObject.env.memory; const { - malloc, - free, - config_new, - config_free, - config_load_string, - config_finalize, - face_new, - face_free, - face_render_glyph, - face_debug_canvas, - deferred_face_new, - deferred_face_free, - deferred_face_load, - deferred_face_face, - group_new, - group_free, - group_add_face, - group_init_sprite_face, - group_index_for_codepoint, - group_render_glyph, - group_cache_new, - group_cache_free, - group_cache_index_for_codepoint, - group_cache_render_glyph, - group_cache_atlas_grayscale, - group_cache_atlas_color, - atlas_new, - atlas_free, - atlas_debug_canvas, - shaper_new, - shaper_free, - shaper_test, + atlas_clear, + atlas_debug_canvas, + atlas_free, + atlas_grow, + atlas_new, + atlas_reserve, + atlas_set, + config_finalize, + config_free, + config_load_string, + config_new, + deferred_face_free, + deferred_face_load, + deferred_face_new, + face_debug_canvas, + face_free, + face_new, + face_render_glyph, + free, + malloc, + shaper_free, + shaper_new, + shaper_test, + collection_new, + collection_add_deferred_face, + shared_grid_new, + shared_grid_atlas_grayscale, + shared_grid_atlas_color, + shared_grid_index_for_codepoint, + shared_grid_render_glyph, } = results.instance.exports; // Give us access to the zjs value for debugging. globalThis.zjs = zjs; @@ -81,109 +79,110 @@ fetch(url.href) config_load_string(config, config_str.ptr, config_str.len); config_finalize(config); free(config_str.ptr); - + // Create our atlas // const atlas = atlas_new(512, 0 /* grayscale */); - + // Create some memory for our string const font_name = makeStr("monospace"); - + // Initialize our deferred face // const df = deferred_face_new(font_ptr, font.byteLength, 0 /* text */); //deferred_face_load(df, 72 /* size */); //const face = deferred_face_face(df); - + // Initialize our font face //const face = face_new(font_ptr, font.byteLength, 72 /* size in px */); //free(font_ptr); - + // Create our group - const group = group_new(32 /* size */); - group_add_face( - group, + const collection = collection_new(24); + collection_add_deferred_face( + collection, 0 /* regular */, deferred_face_new(font_name.ptr, font_name.len, 0 /* text */), ); - group_add_face( - group, + collection_add_deferred_face( + collection, 0 /* regular */, deferred_face_new(font_name.ptr, font_name.len, 1 /* emoji */), ); - + const grid = shared_grid_new(collection); + // Initialize our sprite font, without this we just use the browser. - group_init_sprite_face(group); - - // Create our group cache - const group_cache = group_cache_new(group); - + // group_init_sprite_face(group); + + // // Create our group cache + // const group_cache = group_cache_new(group); + // Render a glyph - // for (let i = 33; i <= 126; i++) { - // const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1); - // group_cache_render_glyph(group_cache, font_idx, i, 0); - // //face_render_glyph(face, atlas, i); - // } + for (let i = 33; i <= 126; i++) { + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); + //face_render_glyph(face, atlas, i); + } // - // const emoji = ["🐏","🌞","🌚","🍱","πŸ’Ώ","🐈","πŸ“ƒ","πŸ“€","πŸ•‘","πŸ™ƒ"]; - // for (let i = 0; i < emoji.length; i++) { - // const cp = emoji[i].codePointAt(0); - // const font_idx = group_cache_index_for_codepoint(group_cache, cp, 0, -1 /* best choice */); - // group_cache_render_glyph(group_cache, font_idx, cp, 0); - // } - + const emoji = ["🐏","🌞","🌚","🍱","πŸ’Ώ","🐈","πŸ“ƒ","πŸ“€","πŸ•‘","πŸ™ƒ"]; + for (let i = 0; i < emoji.length; i++) { + const cp = emoji[i].codePointAt(0); + const font_idx = shared_grid_index_for_codepoint(grid, cp, 0, -1 /* best choice */); + shared_grid_render_glyph(grid, font_idx, cp, 0); + } + for (let i = 0x2500; i <= 0x257f; i++) { - const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1); - group_cache_render_glyph(group_cache, font_idx, i, 0); + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); } for (let i = 0x2580; i <= 0x259f; i++) { - const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1); - group_cache_render_glyph(group_cache, font_idx, i, 0); + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); } for (let i = 0x2800; i <= 0x28ff; i++) { - const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1); - group_cache_render_glyph(group_cache, font_idx, i, 0); + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); } for (let i = 0x1fb00; i <= 0x1fb3b; i++) { - const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1); - group_cache_render_glyph(group_cache, font_idx, i, 0); + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); } for (let i = 0x1fb3c; i <= 0x1fb6b; i++) { - const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1); - group_cache_render_glyph(group_cache, font_idx, i, 0); + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); } - + //face_render_glyph(face, atlas, "ζ©‹".codePointAt(0)); //face_render_glyph(face, atlas, "p".codePointAt(0)); - + // Debug our canvas //face_debug_canvas(face); - + // Let's try shaping const shaper = shaper_new(120); //const input = makeStr("hello🐏"); const input = makeStr("helloπŸπŸ‘πŸ½"); - shaper_test(shaper, group_cache, input.ptr, input.len); - + shaper_test(shaper, grid, input.ptr, input.len); + const cp = 1114112; - const font_idx = group_cache_index_for_codepoint( - group_cache, + const font_idx = shared_grid_index_for_codepoint( + grid, cp, 0, -1 /* best choice */, ); - group_cache_render_glyph(group_cache, font_idx, cp, -1); - + shared_grid_render_glyph(grid, font_idx, cp, -1); + // Debug our atlas canvas { - const atlas = group_cache_atlas_grayscale(group_cache); + const atlas = shared_grid_atlas_grayscale(grid); const id = atlas_debug_canvas(atlas); document.getElementById("atlas-canvas").append(zjs.deleteValue(id)); } - + { - const atlas = group_cache_atlas_color(group_cache); + const atlas = shared_grid_atlas_color(grid); const id = atlas_debug_canvas(atlas); document.getElementById("atlas-color-canvas").append(zjs.deleteValue(id)); } - + //face_free(face); }); diff --git a/example/package-lock.json b/example/package-lock.json index 3cb4de6f0..c18a6b878 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "zig-js": "file:../vendor/zig-js/js" + "zig-js": "https://gitpkg.vercel.app/mitchellh/zig-js/js?main" }, "devDependencies": { "@parcel/transformer-inline-string": "^2.8.0", @@ -20,20 +20,6 @@ "../js": { "extraneous": true }, - "../vendor/zig-js/js": { - "name": "zig-js-glue", - "version": "0.1.2", - "license": "MIT", - "devDependencies": { - "@parcel/packager-ts": "^2.8.0", - "@parcel/transformer-typescript-types": "^2.8.0", - "@types/jest": "^29.2.3", - "jest": "^29.3.1", - "parcel": "^2.8.0", - "ts-jest": "^29.0.3", - "typescript": "^4.9.3" - } - }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -2693,8 +2679,11 @@ } }, "node_modules/zig-js": { - "resolved": "../vendor/zig-js/js", - "link": true + "name": "zig-js-glue", + "version": "0.1.3", + "resolved": "https://gitpkg.vercel.app/mitchellh/zig-js/js?main", + "integrity": "sha512-CdM4TmAINU1fsZMm0S3dH4XzQgCIC4AWfztA2eGRD9Tfk/2LfCZ7RgOED7i26P1Jf2+BCQIBMbxXocV7oxR3Ig==", + "license": "MIT" } }, "dependencies": { @@ -4421,16 +4410,8 @@ "dev": true }, "zig-js": { - "version": "file:../vendor/zig-js/js", - "requires": { - "@parcel/packager-ts": "^2.8.0", - "@parcel/transformer-typescript-types": "^2.8.0", - "@types/jest": "^29.2.3", - "jest": "^29.3.1", - "parcel": "^2.8.0", - "ts-jest": "^29.0.3", - "typescript": "^4.9.3" - } + "version": "https://gitpkg.vercel.app/mitchellh/zig-js/js?main", + "integrity": "sha512-CdM4TmAINU1fsZMm0S3dH4XzQgCIC4AWfztA2eGRD9Tfk/2LfCZ7RgOED7i26P1Jf2+BCQIBMbxXocV7oxR3Ig==" } } } diff --git a/example/package.json b/example/package.json index e884e5c6c..b031a6cc9 100644 --- a/example/package.json +++ b/example/package.json @@ -17,6 +17,6 @@ "typescript": "^4.9.3" }, "dependencies": { - "zig-js": "file:../vendor/zig-js/js" + "zig-js": "https://gitpkg.vercel.app/mitchellh/zig-js/js?main" } } diff --git a/pkg/sentry/build.zig b/pkg/sentry/build.zig index 855490c0a..c3e360b48 100644 --- a/pkg/sentry/build.zig +++ b/pkg/sentry/build.zig @@ -116,7 +116,7 @@ pub fn build(b: *std.Build) !void { .flags = flags.items, }), - .freestanding => {}, + .freestanding, .wasi => {}, else => { std.log.warn("target={} not supported", .{target.result.os.tag}); diff --git a/src/App.zig b/src/App.zig index 279c4e497..24d9018bc 100644 --- a/src/App.zig +++ b/src/App.zig @@ -64,7 +64,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: ?internal_os.Instant = null, last_notification_digest: u64 = 0, /// The conditional state of the configuration. See the equivalent field diff --git a/src/Surface.zig b/src/Surface.zig index b2530936a..4d2cff165 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -174,7 +174,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: internal_os.Instant = undefined, /// The last x/y sent for mouse reports. event_point: ?terminal.point.Coordinate = null, @@ -2724,7 +2724,7 @@ 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| { + if (internal_os.Instant.now()) |now| { const since = now.since(self.mouse.left_click_time); if (since <= self.config.mouse_interval) { // Click interval very short, we may be increasing @@ -2870,7 +2870,7 @@ pub fn mouseButtonCallback( self.mouse.left_click_ypos = pos.y; // Setup our click counter and timer - if (std.time.Instant.now()) |now| { + if (internal_os.Instant.now()) |now| { // 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) { @@ -4478,7 +4478,7 @@ 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 = try internal_os.Instant.now(); // Set a limit of one desktop notification per second so that the OS // doesn't kill us when we run out of resources. diff --git a/src/build_config.zig b/src/build_config.zig index 715552e03..8f9df129c 100644 --- a/src/build_config.zig +++ b/src/build_config.zig @@ -128,8 +128,8 @@ pub const Artifact = enum { pub fn detect() Artifact { if (builtin.target.isWasm()) { - assert(builtin.output_mode == .Obj); - assert(builtin.link_mode == .Static); + // assert(builtin.output_mode == .Obj); + // assert(builtin.link_mode == .Static); return .wasm_module; } diff --git a/src/cli/args.zig b/src/cli/args.zig index 3e378f347..466404500 100644 --- a/src/cli/args.zig +++ b/src/cli/args.zig @@ -1275,7 +1275,7 @@ pub fn LineIterator(comptime ReaderType: type) type { } // Constructs a LineIterator (see docs for that). -fn lineIterator(reader: anytype) LineIterator(@TypeOf(reader)) { +pub fn lineIterator(reader: anytype) LineIterator(@TypeOf(reader)) { return .{ .r = reader }; } diff --git a/src/config/Config.zig b/src/config/Config.zig index 66e9e9ff9..319504088 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -2752,19 +2752,21 @@ fn loadTheme(self: *Config, theme: Theme) !void { pub fn finalize(self: *Config) !void { // We always load the theme first because it may set other fields // in our config. - if (self.theme) |theme| { - const different = !std.mem.eql(u8, theme.light, theme.dark); + if (builtin.cpu.arch != .wasm32) { + if (self.theme) |theme| { + const different = !std.mem.eql(u8, theme.light, theme.dark); - // Warning: loadTheme will deinit our existing config and replace - // it so all memory from self prior to this point will be freed. - try self.loadTheme(theme); + // Warning: loadTheme will deinit our existing config and replace + // it so all memory from self prior to this point will be freed. + try self.loadTheme(theme); - // If we have different light vs dark mode themes, disable - // window-theme = auto since that breaks it. - if (different) { - // This setting doesn't make sense with different light/dark themes - // because it'll force the theme based on the Ghostty theme. - if (self.@"window-theme" == .auto) self.@"window-theme" = .system; + // If we have different light vs dark mode themes, disable + // window-theme = auto since that breaks it. + if (different) { + // This setting doesn't make sense with different light/dark themes + // because it'll force the theme based on the Ghostty theme. + if (self.@"window-theme" == .auto) self.@"window-theme" = .system; + } } } diff --git a/src/config/theme.zig b/src/config/theme.zig index b851ec3d4..2ad5cd76d 100644 --- a/src/config/theme.zig +++ b/src/config/theme.zig @@ -39,7 +39,7 @@ pub const Location = enum { // error set since some platforms don't support some // error types. const Error = @TypeOf(err) || switch (builtin.os.tag) { - .ios => error{BufferTooSmall}, + .ios, .wasi => error{BufferTooSmall}, else => error{}, }; diff --git a/src/font/Collection.zig b/src/font/Collection.zig index f79c80936..5168b2dc8 100644 --- a/src/font/Collection.zig +++ b/src/font/Collection.zig @@ -659,6 +659,27 @@ pub const Index = packed struct(Index.Backing) { } }; +/// The wasm-compatible API. +pub const Wasm = struct { + const wasm = @import("../os/wasm.zig"); + const alloc = wasm.alloc; + export fn collection_new(points: f32) ?*Collection { + const result = alloc.create(Collection) catch |err| { + log.warn("error creating collection err={}", .{err}); + return null; + }; + result.* = Collection.init(); + result.load_options = .{ .library = Library.init() catch unreachable, .size = .{ .points = points } }; + return result; + } + export fn collection_add_deferred_face(self: *Collection, style: u8, face: *DeferredFace) u16 { + return @bitCast(self.add(alloc, @enumFromInt(style), .{ .deferred = face.* }) catch |err| { + log.warn("error adding deferred face to collection err={}", .{err}); + return 0; + }); + } +}; + test init { const testing = std.testing; const alloc = testing.allocator; diff --git a/src/font/DeferredFace.zig b/src/font/DeferredFace.zig index 3ee104386..f47904e95 100644 --- a/src/font/DeferredFace.zig +++ b/src/font/DeferredFace.zig @@ -254,7 +254,7 @@ fn loadWebCanvas( opts: font.face.Options, ) !Face { const wc = self.wc.?; - return try Face.initNamed(wc.alloc, wc.font_str, opts, wc.presentation); + return try Face.initNamed(wc.alloc, wc.font_str, opts.size, wc.presentation); } /// Returns true if this face can satisfy the given codepoint and @@ -392,7 +392,7 @@ pub const Wasm = struct { } export fn deferred_face_load(self: *DeferredFace, pts: f32) void { - self.load(.{}, .{ .points = pts }) catch |err| { + _ = self.load(.{}, .{ .size = .{ .points = pts } }) catch |err| { log.warn("error loading deferred face err={}", .{err}); return; }; diff --git a/src/font/SharedGrid.zig b/src/font/SharedGrid.zig index 8af385b84..40a4f877b 100644 --- a/src/font/SharedGrid.zig +++ b/src/font/SharedGrid.zig @@ -322,6 +322,54 @@ const GlyphKey = struct { const TestMode = enum { normal }; +/// The wasm-compatible API. +pub const Wasm = struct { + const wasm = @import("../os/wasm.zig"); + const alloc = wasm.alloc; + export fn shared_grid_new(c: *Collection) ?*SharedGrid { + const result = alloc.create(SharedGrid) catch |err| { + log.warn("error creating SharedGrid err={}", .{err}); + return null; + }; + result.* = SharedGrid.init(wasm.alloc, .{ .collection = c.* }) catch |err| { + log.warn("error initializing SharedGrid err={}", .{err}); + return null; + }; + return result; + } + export fn shared_grid_atlas_grayscale(self: *SharedGrid) ?*Atlas { + return &self.atlas_grayscale; + } + export fn shared_grid_atlas_color(self: *SharedGrid) ?*Atlas { + return &self.atlas_color; + } + export fn shared_grid_index_for_codepoint(self: *SharedGrid, code: u32, style: u8, presentation: i8) ?*Collection.Index { + const get = self.getIndex(wasm.alloc, code, @enumFromInt(style), if (presentation < 0) null else @enumFromInt(presentation)) catch |err| { + log.warn("error getting SharedGrid index for codepoint err={}", .{err}); + return null; + }; + if (get) |thing| { + const index = wasm.alloc.create(Collection.Index) catch unreachable; + index.* = thing; + return index; + } + return null; + } + + export fn shared_grid_render_glyph(self: *SharedGrid, font_idx: *Collection.Index, code: u32, _: u8) void { + const glyph_index = glyph_index: { + if (font_idx.special()) |special| break :glyph_index switch (special) { + .sprite => code, + }; + self.lock.lockShared(); + defer self.lock.unlockShared(); + const face = self.resolver.collection.getFace(font_idx.*) catch unreachable; + break :glyph_index face.glyphIndex(code) orelse return; + }; + _ = self.renderGlyph(wasm.alloc, font_idx.*, glyph_index, .{}) catch unreachable; + } +}; + fn testGrid(mode: TestMode, alloc: Allocator, lib: Library) !SharedGrid { const testFont = font.embedded.regular; diff --git a/src/font/face/web_canvas.zig b/src/font/face/web_canvas.zig index 60846f350..51c76712a 100644 --- a/src/font/face/web_canvas.zig +++ b/src/font/face/web_canvas.zig @@ -325,6 +325,23 @@ pub const Face = struct { return ctx; } + pub fn isColorGlyph(self: *const Face, cp: u32) bool { + // Render the glyph + var render = self.renderGlyphInternal(self.alloc, cp) catch unreachable; + defer render.deinit(); + + // Inspect the image data for any non-zeros in the RGB value. + // NOTE(perf): this is an easy candidate for SIMD. + var i: usize = 0; + while (i < render.bitmap.len) : (i += 4) { + if (render.bitmap[i] > 0 or + render.bitmap[i + 1] > 0 or + render.bitmap[i + 2] > 0) return true; + } + + return false; + } + /// An internal (web-canvas-only) format for rendered glyphs /// since we do render passes in multiple different situations. const RenderedGlyph = struct { @@ -494,7 +511,7 @@ pub const Wasm = struct { const alloc = wasm.alloc; export fn face_new(ptr: [*]const u8, len: usize, pts: u16, p: u16) ?*Face { - return face_new_(ptr, len, pts, p) catch null; + return face_new_(ptr, len, @floatFromInt(pts), p) catch null; } fn face_new_(ptr: [*]const u8, len: usize, pts: f32, presentation: u16) !*Face { diff --git a/src/font/main.zig b/src/font/main.zig index 60e7593cb..413788c31 100644 --- a/src/font/main.zig +++ b/src/font/main.zig @@ -33,6 +33,8 @@ comptime { if (builtin.target.isWasm()) { _ = Atlas.Wasm; _ = DeferredFace.Wasm; + _ = SharedGrid.Wasm; + _ = @import("Collection.zig").Wasm; _ = face.web_canvas.Wasm; _ = shape.web_canvas.Wasm; } diff --git a/src/font/shaper/web_canvas.zig b/src/font/shaper/web_canvas.zig index f38ab885a..37bfe70b2 100644 --- a/src/font/shaper/web_canvas.zig +++ b/src/font/shaper/web_canvas.zig @@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator; const ziglyph = @import("ziglyph"); const font = @import("../main.zig"); const terminal = @import("../../terminal/main.zig"); +const SharedGrid = font.SharedGrid; const log = std.log.scoped(.font_shaper); @@ -30,19 +31,19 @@ pub const Shaper = struct { alloc: Allocator, /// The shared memory used for shaping results. - cell_buf: []font.shape.Cell, + cell_buf: std.ArrayListUnmanaged(font.shape.Cell), /// The shared memory used for storing information about a run. run_buf: RunBuf, /// The cell_buf argument is the buffer to use for storing shaped results. /// This should be at least the number of columns in the terminal. - pub fn init(alloc: Allocator, opts: font.shape.Options) !Shaper { + pub fn init(alloc: Allocator, _: font.shape.Options) !Shaper { // Note: we do not support opts.font_features return Shaper{ .alloc = alloc, - .cell_buf = opts.cell_buf, + .cell_buf = .{}, .run_buf = .{}, }; } @@ -61,14 +62,16 @@ pub const Shaper = struct { /// for a Shaper struct since they share state. pub fn runIterator( self: *Shaper, - group: *font.GroupCache, - row: terminal.Screen.Row, + grid: *SharedGrid, + screen: *const terminal.Screen, + row: terminal.Pin, selection: ?terminal.Selection, cursor_x: ?usize, ) font.shape.RunIterator { return .{ .hooks = .{ .shaper = self }, - .group = group, + .grid = grid, + .screen = screen, .row = row, .selection = selection, .cursor_x = cursor_x, @@ -90,21 +93,22 @@ pub const Shaper = struct { const clusters = self.run_buf.items(.cluster); assert(codepoints.len == clusters.len); + self.cell_buf.clearRetainingCapacity(); switch (codepoints.len) { // Special cases: if we have no codepoints (is this possible?) // then our result is also an empty cell run. - 0 => return self.cell_buf[0..0], + 0 => return self.cell_buf.items[0..0], // If we have only 1 codepoint, then we assume that it is // a single grapheme and just let it through. At this point, // we can't have any more information to do anything else. 1 => { - self.cell_buf[0] = .{ + try self.cell_buf.append(self.alloc, .{ .x = @intCast(clusters[0]), .glyph_index = codepoints[0], - }; + }); - return self.cell_buf[0..1]; + return self.cell_buf.items[0..1]; }, else => {}, @@ -151,10 +155,10 @@ pub const Shaper = struct { switch (len) { // If we have only a single codepoint then just render it // as-is. - 1 => self.cell_buf[cur] = .{ + 1 => try self.cell_buf.append(self.alloc, .{ .x = @intCast(clusters[start]), .glyph_index = codepoints[start], - }, + }), // We must have multiple codepoints (see assert above). In // this case we UTF-8 encode the codepoints and send them @@ -190,13 +194,13 @@ pub const Shaper = struct { }; defer self.alloc.free(cluster); - var face = try run.group.group.faceFromIndex(run.font_index); + var face = try run.grid.resolver.collection.getFace(run.font_index); const index = try face.graphemeGlyphIndex(cluster); - self.cell_buf[cur] = .{ + try self.cell_buf.append(self.alloc, .{ .x = @intCast(clusters[start]), .glyph_index = index, - }; + }); }, } @@ -204,7 +208,7 @@ pub const Shaper = struct { cur += 1; } - return self.cell_buf[0..cur]; + return self.cell_buf.items[0..cur]; } /// The hooks for RunIterator. @@ -238,15 +242,12 @@ pub const Wasm = struct { const wasm = @import("../../os/wasm.zig"); const alloc = wasm.alloc; - export fn shaper_new(cap: usize) ?*Shaper { - return shaper_new_(cap) catch null; + export fn shaper_new() ?*Shaper { + return shaper_new_() catch null; } - fn shaper_new_(cap: usize) !*Shaper { - const cell_buf = try alloc.alloc(font.shape.Cell, cap); - errdefer alloc.free(cell_buf); - - var shaper = try Shaper.init(alloc, .{ .cell_buf = cell_buf }); + fn shaper_new_() !*Shaper { + var shaper = try Shaper.init(alloc, .{}); errdefer shaper.deinit(); const result = try alloc.create(Shaper); @@ -257,7 +258,6 @@ pub const Wasm = struct { export fn shaper_free(ptr: ?*Shaper) void { if (ptr) |v| { - alloc.free(v.cell_buf); v.deinit(); alloc.destroy(v); } @@ -266,38 +266,33 @@ pub const Wasm = struct { /// Runs a test to verify shaping works properly. export fn shaper_test( self: *Shaper, - group: *font.GroupCache, + grid: *SharedGrid, str: [*]const u8, len: usize, ) void { - shaper_test_(self, group, str[0..len]) catch |err| { + shaper_test_(self, grid, str[0..len]) catch |err| { log.warn("error during shaper test err={}", .{err}); }; } - fn shaper_test_(self: *Shaper, group: *font.GroupCache, str: []const u8) !void { - // Create a terminal and print all our characters into it. - var term = try terminal.Terminal.init(alloc, self.cell_buf.len, 80); + fn shaper_test_(self: *Shaper, grid: *SharedGrid, str: []const u8) !void { + // Make a screen with some data + var term = try terminal.Terminal.init(alloc, .{ .cols = 6, .rows = 5 }); defer term.deinit(alloc); + try term.printString(str); - // Iterate over unicode codepoints and add to terminal - { - const view = try std.unicode.Utf8View.init(str); - var iter = view.iterator(); - while (iter.nextCodepoint()) |c| { - try term.print(c); - } - } + // Get our run iterator - // Iterate over the rows and print out all the runs we get. - var rowIter = term.screen.rowIterator(.viewport); + var row_it = term.screen.pages.rowIterator(.right_down, .{ .viewport = .{} }, null); var y: usize = 0; - while (rowIter.next()) |row| { + while (row_it.next()) |row| { defer y += 1; - - var iter = self.runIterator(group, row, null, null); - while (try iter.next(alloc)) |run| { + var it = self.runIterator(grid, &term.screen, row, null, null); + while (try it.next(alloc)) |run| { const cells = try self.shape(run); + for (cells) |cell| { + _ = try grid.renderGlyph(wasm.alloc, run.font_index, cell.glyph_index, .{}); + } log.info("y={} run={d} shape={any} idx={}", .{ y, run.cells, diff --git a/src/main_wasm.zig b/src/main_wasm.zig index bffe5e4b7..d4b7d63e5 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -11,16 +11,17 @@ comptime { _ = @import("App.zig").Wasm; } -pub const std_options = struct { +pub const std_options: std.Options = .{ // Set our log level. We try to get as much logging as possible but in // ReleaseSmall mode where we're optimizing for space, we elevate the // log level. - pub const log_level: std.log.Level = switch (builtin.mode) { - .Debug => .debug, - .ReleaseSmall => .warn, - else => .info, - }; + // .log_level = switch (builtin.mode) { + // .Debug => .debug, + // .ReleaseSmall => .warn, + // else => .info, + // }, + .log_level = .info, // Set our log function - pub const logFn = @import("os/wasm/log.zig").log; + .logFn = @import("os/wasm/log.zig").log, }; diff --git a/src/os/desktop.zig b/src/os/desktop.zig index 103127dfa..eb4ac1a59 100644 --- a/src/os/desktop.zig +++ b/src/os/desktop.zig @@ -56,6 +56,8 @@ pub fn launchedFromDesktop() bool { // iPhone/iPad is always launched from the "desktop" .ios => true, + .freestanding => false, + else => @compileError("unsupported platform"), }; } diff --git a/src/os/homedir.zig b/src/os/homedir.zig index cf6931f22..b299563fb 100644 --- a/src/os/homedir.zig +++ b/src/os/homedir.zig @@ -20,6 +20,8 @@ pub inline fn home(buf: []u8) !?[]u8 { // iOS doesn't have a user-writable home directory .ios => null, + .wasi => null, + else => @compileError("unimplemented"), }; } diff --git a/src/os/main.zig b/src/os/main.zig index 22765f546..d545f3475 100644 --- a/src/os/main.zig +++ b/src/os/main.zig @@ -13,6 +13,8 @@ const mouse = @import("mouse.zig"); const openpkg = @import("open.zig"); const pipepkg = @import("pipe.zig"); const resourcesdir = @import("resourcesdir.zig"); +const builtin = @import("builtin"); +const std = @import("std"); // Namespaces pub const args = @import("args.zig"); @@ -42,3 +44,8 @@ pub const clickInterval = mouse.clickInterval; pub const open = openpkg.open; pub const pipe = pipepkg.pipe; pub const resourcesDir = resourcesdir.resourcesDir; +pub const Instant = if (builtin.cpu.arch != .wasm32) std.time.Instant else struct { + fn now() !@This() { + return .{}; + } +}; diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index 931d068f9..f461757f3 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -346,7 +346,7 @@ pub const LoadingImage = struct { } // Set our time - self.image.transmit_time = std.time.Instant.now() catch |err| { + self.image.transmit_time = internal_os.Instant.now() catch |err| { log.warn("failed to get time: {}", .{err}); return error.InternalError; }; @@ -453,7 +453,7 @@ pub const Image = struct { format: command.Transmission.Format = .rgb, compression: command.Transmission.Compression = .none, data: []const u8 = "", - transmit_time: std.time.Instant = undefined, + transmit_time: internal_os.Instant = undefined, pub const Error = error{ InternalError, diff --git a/src/terminal/kitty/graphics_storage.zig b/src/terminal/kitty/graphics_storage.zig index bf8633c88..3a4501922 100644 --- a/src/terminal/kitty/graphics_storage.zig +++ b/src/terminal/kitty/graphics_storage.zig @@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const terminal = @import("../main.zig"); +const internal_os = @import("../../os/main.zig"); const point = @import("../point.zig"); const size = @import("../size.zig"); const command = @import("graphics_command.zig"); @@ -495,7 +496,7 @@ pub const ImageStorage = struct { // bit is fine compared to the megabytes we're looking to save. const Candidate = struct { id: u32, - time: std.time.Instant, + time: internal_os.Instant, used: bool, }; diff --git a/src/terminal/page.zig b/src/terminal/page.zig index 8c470d726..5cc24d01f 100644 --- a/src/terminal/page.zig +++ b/src/terminal/page.zig @@ -320,7 +320,7 @@ pub const Page = struct { /// when runtime safety is enabled. This is a no-op when runtime /// safety is disabled. This uses the libc allocator. pub fn assertIntegrity(self: *const Page) void { - if (comptime build_config.slow_runtime_safety) { + if (comptime build_config.slow_runtime_safety and builtin.cpu.arch != .wasm32) { self.verifyIntegrity(std.heap.c_allocator) catch |err| { log.err("page integrity violation, crashing. err={}", .{err}); @panic("page integrity violation"); diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 07aa43c42..3b4e0c6a9 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -102,7 +102,7 @@ pub fn threadEnter( }; // Track our process start time for abnormal exits - const process_start = try std.time.Instant.now(); + const process_start = try internal_os.Instant.now(); // Create our pipe that we'll use to kill our read thread. // pipe[0] is the read end, pipe[1] is the write end. @@ -345,7 +345,7 @@ fn processExit( // Determine how long the process was running for. const runtime_ms: ?u64 = runtime: { - const process_end = std.time.Instant.now() catch break :runtime null; + const process_end = internal_os.now() catch break :runtime null; const runtime_ns = process_end.since(execdata.start); const runtime_ms = runtime_ns / std.time.ns_per_ms; break :runtime runtime_ms; @@ -603,7 +603,7 @@ pub const ThreadData = struct { const WRITE_REQ_PREALLOC = std.math.pow(usize, 2, 5); /// Process start time and boolean of whether its already exited. - start: std.time.Instant, + start: internal_os.Instant, exited: bool = false, /// The number of milliseconds below which we consider a process diff --git a/src/termio/Termio.zig b/src/termio/Termio.zig index 9ed3ffc94..bdc743949 100644 --- a/src/termio/Termio.zig +++ b/src/termio/Termio.zig @@ -68,7 +68,7 @@ terminal_stream: terminal.Stream(StreamHandler), /// Last time the cursor was reset. This is used to prevent message /// flooding with cursor resets. -last_cursor_reset: ?std.time.Instant = null, +last_cursor_reset: ?internal_os.Instant = null, /// The configuration for this IO that is derived from the main /// configuration. This must be exported so that we don't need to @@ -552,7 +552,7 @@ fn processOutputLocked(self: *Termio, buf: []const u8) void { // non-blink state so it is rendered if visible. If we're under // HEAVY read load, we don't want to send a ton of these so we // use a timer under the covers - if (std.time.Instant.now()) |now| cursor_reset: { + if (internal_os.Instant.now()) |now| cursor_reset: { if (self.last_cursor_reset) |last| { if (now.since(last) <= (500 * std.time.ns_per_ms)) { break :cursor_reset; From f0fd846679617795257da06a81231341bd8e4015 Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Fri, 22 Nov 2024 16:33:38 -0500 Subject: [PATCH 02/10] opengl in browser --- build.zig | 75 +- build.zig.zon | 5 +- example/.proxyrc.js | 10 + example/app.ts | 199 +--- example/imports.ts | 650 +++++++++++++ example/index.html | 2 + example/old.ts | 158 ++++ example/package-lock.json | 1237 ++++++++++++++++++++++++- example/package.json | 12 +- example/worker.ts | 11 + pkg/opengl/c.zig | 285 +++++- pkg/simdutf/build.zig | 1 + pkg/simdutf/vendor/simdutf.h | 34 +- src/Command.zig | 64 +- src/Surface.zig | 7 +- src/apprt/browser.zig | 100 +- src/build_config.zig | 24 +- src/font/DeferredFace.zig | 1 + src/font/SharedGridSet.zig | 99 +- src/font/face/web_canvas.zig | 44 +- src/font/shaper/web_canvas.zig | 106 ++- src/input/Link.zig | 3 + src/inspector/main.zig | 3 +- src/main_wasm.zig | 53 ++ src/os/desktop.zig | 2 +- src/os/hostname.zig | 21 +- src/os/main.zig | 12 +- src/pty.zig | 3 +- src/renderer.zig | 2 +- src/renderer/OpenGL.zig | 17 +- src/renderer/link.zig | 5 +- src/renderer/opengl/custom.zig | 11 +- src/renderer/opengl/image.zig | 3 +- src/renderer/shaders/cell.f.glsl | 18 +- src/renderer/shaders/cell.v.glsl | 34 +- src/renderer/shaders/custom.v.glsl | 2 +- src/renderer/shaders/image.f.glsl | 8 +- src/renderer/shaders/image.v.glsl | 12 +- src/terminal/Parser.zig | 10 +- src/terminal/kitty/graphics_image.zig | 2 +- src/terminal/osc.zig | 7 +- src/terminal/stream.zig | 10 +- src/termio/Exec.zig | 25 +- src/termio/Termio.zig | 25 +- src/termio/stream_handler.zig | 2 +- 45 files changed, 3058 insertions(+), 356 deletions(-) create mode 100644 example/.proxyrc.js create mode 100644 example/imports.ts create mode 100644 example/old.ts create mode 100644 example/worker.ts diff --git a/build.zig b/build.zig index 5410b793c..b798308ce 100644 --- a/build.zig +++ b/build.zig @@ -716,7 +716,8 @@ pub fn build(b: *std.Build) !void { // Build our Wasm target. const wasm_crosstarget: std.Target.Query = .{ .cpu_arch = .wasm32, - .os_tag = .freestanding, + .os_tag = .wasi, + .abi = .musl, .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, .cpu_features_add = std.Target.wasm.featureSet(&.{ // We use this to explicitly request shared memory. @@ -759,10 +760,12 @@ pub fn build(b: *std.Build) !void { wasm.import_memory = true; wasm.initial_memory = 65536 * 512; wasm.entry = .disabled; - // wasm.wasi_exec_model = .reactor; + wasm.wasi_exec_model = .reactor; wasm.rdynamic = true; + wasm.import_symbols = true; wasm.max_memory = 65536 * 65536; // Maximum number of pages in wasm32 wasm.shared_memory = wasm_shared; + wasm.root_module.single_threaded = false; // Stack protector adds extern requirements that we don't satisfy. wasm.root_module.stack_protector = false; @@ -1191,7 +1194,73 @@ fn addDeps( .target = target, .optimize = optimize, })); - // step.linkLibC(); + step.root_module.addImport("opengl", b.dependency( + "opengl", + .{}, + ).module("opengl")); + step.root_module.addImport("xev", b.dependency("libxev", .{ + .target = target, + .optimize = optimize, + }).module("xev")); + step.root_module.addImport("wuffs", b.dependency("wuffs", .{ + .target = target, + .optimize = optimize, + }).module("wuffs")); + const oniguruma_dep = b.dependency("oniguruma", .{ + .target = target, + .optimize = optimize, + }); + step.root_module.addImport("oniguruma", oniguruma_dep.module("oniguruma")); + step.linkLibrary(oniguruma_dep.artifact("oniguruma")); + try static_libs.append(oniguruma_dep.artifact("oniguruma").getEmittedBin()); + const simdutf_dep = b.dependency("simdutf", .{ + .target = target, + .optimize = optimize, + }); + const utfcpp_dep = b.dependency("utfcpp", .{ + .target = target, + .optimize = optimize, + }); + step.linkLibrary(utfcpp_dep.artifact("utfcpp")); + try static_libs.append(utfcpp_dep.artifact("utfcpp").getEmittedBin()); + step.linkLibrary(simdutf_dep.artifact("simdutf")); + try static_libs.append(simdutf_dep.artifact("simdutf").getEmittedBin()); + step.linkLibC(); + step.linkLibCpp(); + step.addIncludePath(b.path("src")); + { + // From hwy/detect_targets.h + const HWY_AVX3_SPR: c_int = 1 << 4; + const HWY_AVX3_ZEN4: c_int = 1 << 6; + const HWY_AVX3_DL: c_int = 1 << 7; + const HWY_AVX3: c_int = 1 << 8; + + // Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414 + // To workaround this we just disable AVX512 support completely. + // The performance difference between AVX2 and AVX512 is not + // significant for our use case and AVX512 is very rare on consumer + // hardware anyways. + const HWY_DISABLED_TARGETS: c_int = HWY_AVX3_SPR | HWY_AVX3_ZEN4 | HWY_AVX3_DL | HWY_AVX3; + + step.addCSourceFiles(.{ + .files = &.{ + "src/simd/base64.cpp", + "src/simd/codepoint_width.cpp", + "src/simd/index_of.cpp", + "src/simd/vt.cpp", + }, + .flags = if (step.rootModuleTarget().cpu.arch == .x86_64) &.{ + b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}), + } else &.{"-DSIMDUTF_NO_THREADS"}, + }); + } + const highway_dep = b.dependency("highway", .{ + .target = target, + .optimize = optimize, + }); + step.linkLibrary(highway_dep.artifact("highway")); + try static_libs.append(highway_dep.artifact("highway").getEmittedBin()); + try addUnicodeTables(b, step); return static_libs; diff --git a/build.zig.zon b/build.zig.zon index c5e2d8f70..b9a6773ec 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -4,10 +4,7 @@ .paths = .{""}, .dependencies = .{ // Zig libs - .libxev = .{ - .url = "https://github.com/mitchellh/libxev/archive/b8d1d93e5c899b27abbaa7df23b496c3e6a178c7.tar.gz", - .hash = "1220612bc023c21d75234882ec9a8c6a1cbd9d642da3dfb899297f14bb5bd7b6cd78", - }, + .libxev = .{ .path = "../xev" }, .mach_glfw = .{ .url = "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz", .hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62", diff --git a/example/.proxyrc.js b/example/.proxyrc.js new file mode 100644 index 000000000..8e231b5df --- /dev/null +++ b/example/.proxyrc.js @@ -0,0 +1,10 @@ +module.exports = function (app) { + app.use( + (req, res, next) => { + res.setHeader('Cross-Origin-Opener-Policy', 'same-origin'); + res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp'); + res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin'); + next(); + } + ); +}; diff --git a/example/app.ts b/example/app.ts index 0168e030d..2199e858f 100644 --- a/example/app.ts +++ b/example/app.ts @@ -1,24 +1,5 @@ -import { ZigJS } from "zig-js/src/index.ts"; - -const zjs = new ZigJS(); -const importObject = { - module: {}, - env: { - memory: new WebAssembly.Memory({ - initial: 512, - maximum: 65536, - shared: true, - }), - log: (ptr: number, len: number) => { - const arr = new Uint8ClampedArray(zjs.memory.buffer, ptr, len); - const data = arr.slice(); - const str = new TextDecoder("utf-8").decode(data); - console.log(str); - }, - }, - - ...zjs.importObject(), -}; +import { importObject, zjs } from "./imports"; +import { old } from "./old"; const url = new URL("ghostty-wasm.wasm", import.meta.url); fetch(url.href) @@ -27,37 +8,39 @@ fetch(url.href) .then((results) => { const memory = importObject.env.memory; const { - atlas_clear, - atlas_debug_canvas, - atlas_free, - atlas_grow, - atlas_new, - atlas_reserve, - atlas_set, - config_finalize, - config_free, - config_load_string, - config_new, - deferred_face_free, - deferred_face_load, - deferred_face_new, - face_debug_canvas, - face_free, - face_new, - face_render_glyph, - free, - malloc, - shaper_free, - shaper_new, - shaper_test, - collection_new, - collection_add_deferred_face, - shared_grid_new, - shared_grid_atlas_grayscale, - shared_grid_atlas_color, - shared_grid_index_for_codepoint, - shared_grid_render_glyph, + atlas_clear, + atlas_debug_canvas, + atlas_free, + atlas_grow, + atlas_new, + atlas_reserve, + atlas_set, + config_finalize, + config_free, + config_load_string, + config_new, + deferred_face_free, + deferred_face_load, + deferred_face_new, + face_debug_canvas, + face_free, + face_new, + face_render_glyph, + free, + malloc, + shaper_free, + shaper_new, + shaper_test, + collection_new, + collection_add_deferred_face, + shared_grid_new, + shared_grid_atlas_grayscale, + shared_grid_atlas_color, + shared_grid_index_for_codepoint, + shared_grid_render_glyph, + run, } = results.instance.exports; + // Give us access to the zjs value for debugging. globalThis.zjs = zjs; console.log(zjs); @@ -74,115 +57,7 @@ fetch(url.href) }; // Create our config - const config = config_new(); const config_str = makeStr("font-family = monospace"); - config_load_string(config, config_str.ptr, config_str.len); - config_finalize(config); - free(config_str.ptr); - - // Create our atlas - // const atlas = atlas_new(512, 0 /* grayscale */); - - // Create some memory for our string - const font_name = makeStr("monospace"); - - // Initialize our deferred face - // const df = deferred_face_new(font_ptr, font.byteLength, 0 /* text */); - //deferred_face_load(df, 72 /* size */); - //const face = deferred_face_face(df); - - // Initialize our font face - //const face = face_new(font_ptr, font.byteLength, 72 /* size in px */); - //free(font_ptr); - - // Create our group - const collection = collection_new(24); - collection_add_deferred_face( - collection, - 0 /* regular */, - deferred_face_new(font_name.ptr, font_name.len, 0 /* text */), - ); - collection_add_deferred_face( - collection, - 0 /* regular */, - deferred_face_new(font_name.ptr, font_name.len, 1 /* emoji */), - ); - const grid = shared_grid_new(collection); - - // Initialize our sprite font, without this we just use the browser. - // group_init_sprite_face(group); - - // // Create our group cache - // const group_cache = group_cache_new(group); - - // Render a glyph - for (let i = 33; i <= 126; i++) { - const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); - shared_grid_render_glyph(grid, font_idx, i, 0); - //face_render_glyph(face, atlas, i); - } - // - const emoji = ["🐏","🌞","🌚","🍱","πŸ’Ώ","🐈","πŸ“ƒ","πŸ“€","πŸ•‘","πŸ™ƒ"]; - for (let i = 0; i < emoji.length; i++) { - const cp = emoji[i].codePointAt(0); - const font_idx = shared_grid_index_for_codepoint(grid, cp, 0, -1 /* best choice */); - shared_grid_render_glyph(grid, font_idx, cp, 0); - } - - for (let i = 0x2500; i <= 0x257f; i++) { - const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); - shared_grid_render_glyph(grid, font_idx, i, 0); - } - for (let i = 0x2580; i <= 0x259f; i++) { - const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); - shared_grid_render_glyph(grid, font_idx, i, 0); - } - for (let i = 0x2800; i <= 0x28ff; i++) { - const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); - shared_grid_render_glyph(grid, font_idx, i, 0); - } - for (let i = 0x1fb00; i <= 0x1fb3b; i++) { - const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); - shared_grid_render_glyph(grid, font_idx, i, 0); - } - for (let i = 0x1fb3c; i <= 0x1fb6b; i++) { - const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); - shared_grid_render_glyph(grid, font_idx, i, 0); - } - - //face_render_glyph(face, atlas, "ζ©‹".codePointAt(0)); - //face_render_glyph(face, atlas, "p".codePointAt(0)); - - // Debug our canvas - //face_debug_canvas(face); - - // Let's try shaping - const shaper = shaper_new(120); - //const input = makeStr("hello🐏"); - const input = makeStr("helloπŸπŸ‘πŸ½"); - shaper_test(shaper, grid, input.ptr, input.len); - - const cp = 1114112; - const font_idx = shared_grid_index_for_codepoint( - grid, - cp, - 0, - -1 /* best choice */, - ); - shared_grid_render_glyph(grid, font_idx, cp, -1); - - // Debug our atlas canvas - { - const atlas = shared_grid_atlas_grayscale(grid); - const id = atlas_debug_canvas(atlas); - document.getElementById("atlas-canvas").append(zjs.deleteValue(id)); - } - - { - const atlas = shared_grid_atlas_color(grid); - const id = atlas_debug_canvas(atlas); - document.getElementById("atlas-color-canvas").append(zjs.deleteValue(id)); - } - - //face_free(face); - }); + old(results); + run(config_str.ptr, config_str.len); + }) diff --git a/example/imports.ts b/example/imports.ts new file mode 100644 index 000000000..78745dd8e --- /dev/null +++ b/example/imports.ts @@ -0,0 +1,650 @@ +import { ZigJS } from "zig-js/src/index.ts"; + +const textDecoder = new TextDecoder("utf-8"); + +let gl: WebGL2RenderingContext; +export function setGl(l) { + gl = l; +} + +export const zjs = new ZigJS(); +try { + const $webgl = document.getElementById("main-canvas"); + let webgl2Supported = typeof WebGL2RenderingContext !== "undefined"; + + let webglOptions = { + alpha: false, + antialias: true, + depth: 32, + failIfMajorPerformanceCaveat: false, + powerPreference: "default", + premultipliedAlpha: true, + preserveDrawingBuffer: true, + }; + + if (webgl2Supported) { + gl = $webgl.getContext("webgl2", webglOptions); + if (!gl) { + throw new Error("The browser supports WebGL2, but initialization failed."); + } + } +} catch (e){console.error(e) } + +// OpenGL operates on numeric IDs while WebGL on objects. The following is a +// hack made to allow keeping current API on the native side while resolving IDs +// to objects in JS. Because the values of those IDs don't really matter, there +// is a shared counter. +let id = 1; +const getId = () => { + id += 1; + return id; +}; + +const glShaders = new Map(); +const glPrograms = new Map(); +const glVertexArrays = new Map(); +const glBuffers = new Map(); +const glFrameBuffers = new Map(); +const glTextures = new Map(); +const glUniformLocations = new Map(); + +const glViewport = (x, y, width, height) => { + gl.viewport(x, y, width, height); +}; + +const glClearColor = (r, g, b, a) => { + gl.clearColor(r, g, b, a); +}; + +const glEnable = (value) => { + gl.enable(value); +}; + +const glDisable = (value) => { + gl.disable(value); +}; + +const glDepthFunc = (value) => { + gl.depthFunc(value); +}; + +const glBlendFunc = (sFactor, dFactor) => { + gl.blendFunc(sFactor, dFactor); +}; + +const glClear = (value) => { + gl.clear(value); +}; + +const glGetAttribLocation = (programId, pointer, length) => { + const name = zjs.loadString(pointer, length); + return gl.getAttribLocation(glPrograms.get(programId), name); +}; + +const glGetUniformLocation = (programId, pointer) => { + const str = new Uint8Array(zjs.memory.buffer, pointer); + let i = 0; + while (str[i] !== 0) i++; + const name = textDecoder.decode(str.slice(0, i)); + const value = gl.getUniformLocation(glPrograms.get(programId), name); + const id = getId(); + glUniformLocations.set(id, value); + return id; +}; + +const glUniform4fv = (locationId, x, y, z, w) => { + gl.uniform4fv(glUniformLocations.get(locationId), [x, y, z, w]); +}; + +const glUniform2f = (locationId, x, y) => { + gl.uniform2f(glUniformLocations.get(locationId), x, y); +}; + +const glUniformMatrix4fv = (locationId, length, transpose, pointer) => { + const floats = new Float32Array(zjs.memory.buffer, pointer, length * 16); + gl.uniformMatrix4fv(glUniformLocations.get(locationId), transpose, floats); +}; + +const glUniform1i = (locationId, value) => { + gl.uniform1i(glUniformLocations.get(locationId), value); +}; + +const glUniform1f = (locationId, value) => { + gl.uniform1f(glUniformLocations.get(locationId), value); +}; + +const glCreateBuffer = () => { + const id = getId(); + glBuffers.set(id, gl.createBuffer()); + return id; +}; + +const glGenBuffers = (number, pointer) => { + const buffers = new Uint32Array(zjs.memory.buffer, pointer, number); + for (let n = 0; n < number; n++) { + const b = glCreateBuffer(); + buffers[n] = b; + } +}; + +const glAttachShader = (program, shader) => { + gl.attachShader(glPrograms.get(program), glShaders.get(shader)); +}; + +const glDetachShader = (program, shader) => { + gl.detachShader(glPrograms.get(program), glShaders.get(shader)); +}; + +const glDeleteProgram = (id) => { + gl.deleteProgram(glPrograms.get(id)); + glPrograms.delete(id); +}; + +const glDeleteBuffer = (id) => { + gl.deleteBuffer(glBuffers.get(id)); + glBuffers.delete(id); +}; + +const glDeleteBuffers = (number, pointer) => { + const buffers = new Uint32Array(zjs.memory.buffer, pointer, number); + for (let n = 0; n < number; n++) { + gl.deleteBuffer(glBuffers.get(buffers[n])); + glBuffers.delete(buffers[n]); + } +}; + +const glDeleteShader = (id) => { + console.error("shader deleted"); + gl.deleteShader(glShaders.get(id)); + glShaders.delete(id); +}; + +const glCreateShader = (type) => { + const shader = gl.createShader(type); + const id = getId(); + glShaders.set(id, shader); + return id; +}; + +const glCompileShader = (id) => { + gl.compileShader(glShaders.get(id)); +}; + +// This differs from OpenGL version due to problems with reading strings till +// null termination. +const glShaderSource = (shader, amount, pointer) => { + const addr = new Uint32Array(zjs.memory.buffer, pointer)[0]; + const str = new Uint8Array(zjs.memory.buffer, addr); + let i = 0; + while (str[i] !== 0) i++; + const source = textDecoder.decode(str.slice(0, i)); + gl.shaderSource(glShaders.get(shader), source); +}; + +const glCreateProgram = () => { + const id = getId(); + const program = gl.createProgram(); + glPrograms.set(id, program); + return id; +}; + +const glGetShaderiv = (id, parameter, ptr) => { + const ret = gl.getShaderParameter(glShaders.get(id), parameter); + const data = new Int32Array(zjs.memory.buffer, ptr, 1); + data[0] = ret; +}; + +const glGetProgramiv = (id, parameter, ptr) => { + const ret = gl.getProgramParameter(glPrograms.get(id), parameter); + const data = new Int32Array(zjs.memory.buffer, ptr, 1); + data[0] = ret; +}; + +const glGetShaderInfoLog = (id, length, lengthPointer, messagePointer) => { + const message = new Uint8Array(zjs.memory.buffer, messagePointer, length); + const info = gl.getShaderInfoLog(glShaders.get(id)); + + for (let i = 0; i < info.length; i++) { + message[i] = info.charCodeAt(i); + } +}; + +const glGetProgramInfoLog = (id, length, lengthPointer, messagePointer) => { + const message = new Uint8Array(zjs.memory.buffer, messagePointer, length); + const info = gl.getProgramInfoLog(glPrograms.get(id)); + + for (let i = 0; i < info.length; i++) { + message[i] = info.charCodeAt(i); + } +}; + +const glLinkProgram = (id) => { + gl.linkProgram(glPrograms.get(id)); +}; + +const glBindBuffer = (type, bufferId) => { + gl.bindBuffer(type, glBuffers.get(bufferId)); +}; + +const glBufferData = (type, count, pointer, drawType) => { + // The Float32Array multiplies by size of float which is 4, and the call to + // this method, due to OpenGL compatibility, also receives already + // pre-multiplied value. + gl.bufferData(type, zjs.memory.buffer.slice(pointer, pointer+count), drawType); +}; + +const glBufferSubData = (target, offset, size, data) => { + // The Float32Array multiplies by size of float which is 4, and the call to + // this method, due to OpenGL compatibility, also receives already + // pre-multiplied value. + gl.bufferSubData(target, offset, zjs.memory.buffer.slice(data, data + size)); +}; + +const glUseProgram = (programId) => { + gl.useProgram(glPrograms.get(programId)); +}; + +const glEnableVertexAttribArray = (value) => { + gl.enableVertexAttribArray(value); +}; + +const glVertexAttribPointer = ( + attribLocation, + size, + type, + normalize, + stride, + offset +) => { + gl.vertexAttribPointer(attribLocation, size, type, normalize, stride, offset); +}; + +const glVertexAttribIPointer = ( + attribLocation, + size, + type, + stride, + offset +) => { + gl.vertexAttribIPointer(attribLocation, size, type, stride, offset); +}; + +const glVertexAttribDivisor = (index, divisor) => { + gl.vertexAttribDivisor(index, divisor); +} + +const glGenFramebuffers = (n, ptr) => { + const buffers = new Uint32Array(zjs.memory.buffer, ptr, n); + for (let i = 0; i < n; i++) { + const id = getId(); + const b = gl.createFramebuffer(); + glFrameBuffers.set(id, b); + buffers[i] = id; + } +} + +const glDeleteFramebuffers = (n, ptr) => { + const buffers = new Uint32Array(zjs.memory.buffer, ptr, n); + for (let i = 0; i < n; i++) { + const b = glFrameBuffers.get(buffers[i]); + gl.deleteFramebuffer(b); + } +} + +const glBindFramebuffer = (target, fb) => { + const b = glFrameBuffers.get(fb); + gl.bindFramebuffer(target, b); +} + +const glTexSubImage2D = (target, level, xoffset, yoffset, width, height, format, type, pixels) => { + let size = 1; + if (format === gl.RGBA) { + size = 4; + } else if (format === gl.RED) { + size = 1; + } else if (format == 32993) { + size = 4; + format = gl.RGBA + } else { + throw new Error("Add pixel count for this format."); + } + + const data = new Uint8Array(zjs.memory.buffer, pixels, width * height * size); + gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, data) +} +const glGetIntegerv = (pname, ptr) => { + const data = new Uint32Array(zjs.memory?.buffer, ptr, 1); + data[0] = gl.getParameter(pname); +} + +const glUniform4f = (locationId, x, y, z, w) => { + gl.uniform4f(glUniformLocations.get(locationId), x, y, z, w); +}; +const glDrawElementsInstanced = (mode, count, type, indices, instancecount) => { + gl.drawElementsInstanced(mode, count, type, indices, instancecount); +}; +const glBindBufferBase = (target, index, buffer) => { + gl.bindBufferBase(target, index, glBuffers.get(buffer)) +} +const glDrawArrays = (type, offset, count) => { + gl.drawArrays(type, offset, count); +}; + +const glCreateTexture = () => { + const id = getId(); + glTextures.set(id, gl.createTexture()); + return id; +}; + +const glGenTextures = (number, pointer) => { + const textures = new Uint32Array(zjs.memory.buffer, pointer, number); + for (let n = 0; n < number; n++) { + const texture = glCreateTexture(); + textures[n] = texture; + } +}; + +const glDeleteTextures = (number, pointer) => { + const textures = new Uint32Array(zjs.memory.buffer, pointer, number); + for (let n = 0; n < number; n++) { + gl.deleteTexture(glBuffers[n]); + glTextures.delete(textures[n]); + } +}; + +const glDeleteTexture = (id) => { + gl.deleteTexture(glTextures.get(id)); + glTextures.delete(id); +}; + +const glBindTexture = (target, id) => { + return gl.bindTexture(target, glTextures.get(id)); +}; + +const glTexImage2D = ( + target, + level, + internalFormat, + width, + height, + border, + format, + type, + pointer +) => { + let size = 1; + if (format === gl.RGBA) { + size = 4; + } else if (format == gl.RED) { + size = 1; + internalFormat = gl.R8 + } else if (format == 32993) { + size = 4; + format = gl.RGBA + } else { + throw new Error("Add pixel count for this format."); + } + + const data = new Uint8Array(zjs.memory.buffer, pointer, width * height * size); + + gl.texImage2D( + target, + level, + internalFormat, + width, + height, + border, + format, + type, + data + ); +}; + +const glTexParameteri = (target, name, parameter) => { + gl.texParameteri(target, name, parameter); +}; + +const glActiveTexture = (target) => { + return gl.activeTexture(target); +}; + +const glGenerateMipmap = (value) => { + gl.generateMipmap(value); +}; + +const glCreateVertexArray = () => { + const id = getId(); + glVertexArrays.set(id, gl.createVertexArray()); + return id; +}; + +const glGenVertexArrays = (number, pointer) => { + const vaos = new Uint32Array(zjs.memory.buffer, pointer, number); + for (let n = 0; n < number; n++) { + const b = glCreateVertexArray(); + vaos[n] = b; + } +}; + +const glDeleteVertexArrays = (number, pointer) => { + const vaos = new Uint32Array(zjs.memory.buffer, pointer, number); + for (let n = 0; n < number; n++) { + glVertexArrays.delete(vaos[n]); + } +}; + +const glBindVertexArray = (id) => gl.bindVertexArray(glVertexArrays.get(id)); + +const glPixelStorei = (type, alignment) => gl.pixelStorei(type, alignment); + +const glGetError = () => { + return gl.getError(); +} + +const webgl = { + glViewport, + glClearColor, + glEnable, + glDisable, + glDepthFunc, + glBlendFunc, + glClear, + glGetAttribLocation, + glGetUniformLocation, + glUniform4fv, + glUniform2f, + glUniform1i, + glUniform1f, + glUniformMatrix4fv, + glCreateVertexArray, + glGenVertexArrays, + glDeleteVertexArrays, + glBindVertexArray, + glCreateBuffer, + glGenBuffers, + glDeleteBuffers, + glDeleteBuffer, + glBindBuffer, + glBufferData, + glBufferSubData, + glPixelStorei, + glAttachShader, + glDetachShader, + glDeleteShader, + glCreateShader, + glCompileShader, + glShaderSource, + glCreateProgram, + glGetShaderInfoLog, + glGetProgramInfoLog, + glLinkProgram, + glUseProgram, + glDeleteProgram, + glEnableVertexAttribArray, + glVertexAttribPointer, + glVertexAttribIPointer, + glDrawArrays, + glCreateTexture, + glGenTextures, + glDeleteTextures, + glDeleteTexture, + glBindTexture, + glTexImage2D, + glTexParameteri, + glActiveTexture, + glGenerateMipmap, + glGetError, + glGetShaderiv, + glGetProgramiv, + glVertexAttribDivisor, + glGenFramebuffers, + glDeleteFramebuffers, + glBindFramebuffer, + glTexSubImage2D, + glGetIntegerv, + glUniform4f, + glDrawElementsInstanced, + glBindBufferBase, +} +export const importObject = { + module: {}, + env: { + memory: new WebAssembly.Memory({ + initial: 512, + maximum: 65536, + shared: true, + }), + ...webgl, + log: (ptr: number, len: number) => { + const arr = new Uint8ClampedArray(zjs.memory.buffer, ptr, len); + const data = arr.slice(); + const str = textDecoder.decode(data); + console.error(str); + }, + fork: (...params) => { + console.error("fork", params); + }, + execve: (...params) => { + console.error("execve", params); + }, + pipe: (...params) => { + console.error("pipe", params); + }, + realpath: (...params) => { + console.error("realpath", params); + }, + pthread_mutex_lock: (...params) => { + console.error("pthread_mutex_lock", params); + }, + pthread_cond_wait: (...params) => { + console.error("pthread_cond_wait", params); + }, + pthread_mutex_unlock: (...params) => { + console.error("pthread_mutex_unlock", params); + }, + __cxa_allocate_exception: (...params) => { + console.error("__cxa_allocate_exception", params); + }, + pthread_cond_broadcast: (...params) => { + console.error("pthread_cond_broadcast", params); + }, + __cxa_throw: (...params) => { + console.error("__cxa_throw", params); + }, + + }, + wasi_snapshot_preview1: { + fd_write: (fd, iovs, iovs_len, nwritten_ptr) => { + const memory = new DataView(zjs.memory.buffer); + let buf = ""; + let nwritten = 0; + for (let offset = iovs; offset < iovs + iovs_len * 8; offset += 8) { + const iov_base = memory.getUint32(offset, true); + const iov_len = memory.getUint32(offset + 4, true); + buf += textDecoder.decode(new Uint8ClampedArray(memory.buffer.slice(iov_base, iov_base + iov_len)).slice()); + nwritten += iov_len; + } + memory.setUint32(nwritten_ptr, nwritten, true); + console.error(buf); + }, + fd_close: (...params) => { + console.error("fd_close", params); + }, + fd_fdstat_get: (...params) => { + console.error("fd_fdstat_get", params); + }, + fd_fdstat_set_flags: (...params) => { + console.error("fd_fdstat_set_flags", params); + }, + fd_filestat_get: (...params) => { + console.error("fd_filestat_get", params); + }, + fd_pread: (...params) => { + console.error("fd_pread", params); + }, + fd_prestat_get: (...params) => { + console.error("fd_prestat_get", params); + }, + fd_prestat_dir_name: (...params) => { + console.error("fd_prestat_dir_name", params); + }, + fd_pwrite: (...params) => { + console.error("fd_pwrite", params); + }, + fd_read: (...params) => { + console.error("fd_read", params); + }, + fd_seek: (...params) => { + console.error("fd_seek", params); + }, + path_filestat_get: (...params) => { + console.error("path_filestat_get", params); + }, + path_open: (...params) => { + console.error("path_open", params); + }, + path_unlink_file: (...params) => { + console.error("path_unlink_file", params); + }, + poll_oneoff: (...params) => { + }, + proc_exit: (...params) => { + console.error("proc_exit", params); + }, + sock_shutdown: (...params) => { + console.error("sock_shutdown", params); + }, + sock_accept: (...params) => { + console.error("sock_accept", params); + }, + sock_recv: (...params) => { + console.error("sock_recv", params); + }, + sock_send: (...params) => { + console.error("sock_send", params); + }, + random_get: (ptr, len) => { + const memory = new Uint8Array(zjs.memory.buffer, ptr, len).slice(); + crypto.getRandomValues(memory); + }, + environ_get: (...params) => { + console.error("environ_get", params); + }, + clock_time_get: (_clock, _precision, ptr) => { + const data = new DataView(zjs.memory.buffer); + data.setBigUint64(ptr, BigInt(Date.now()) * 1000000n, true) + }, + environ_sizes_get: (...params) => { + console.error("environ_sizes_get", params); + }, + }, + wasi: { + "thread-spawn": (instance) => { + const worker = new Worker(new URL("worker.ts", import.meta.url), { type: "module" }); + worker.postMessage([zjs.memory, instance]); + + } + }, + + ...zjs.importObject(), +}; diff --git a/example/index.html b/example/index.html index 2e66f92d1..e74e55717 100644 --- a/example/index.html +++ b/example/index.html @@ -7,6 +7,8 @@

Open your console, we are just debugging here.

+
+

The current grayscale font atlas is rendered below.

The current color font atlas is rendered below.

diff --git a/example/old.ts b/example/old.ts new file mode 100644 index 000000000..d1976753b --- /dev/null +++ b/example/old.ts @@ -0,0 +1,158 @@ +import { zjs } from "./imports"; + +export function old(results) { + const { + atlas_clear, + atlas_debug_canvas, + atlas_free, + atlas_grow, + atlas_new, + atlas_reserve, + atlas_set, + config_finalize, + config_free, + config_load_string, + config_new, + deferred_face_free, + deferred_face_load, + deferred_face_new, + face_debug_canvas, + face_free, + face_new, + face_render_glyph, + free, + malloc, + shaper_free, + shaper_new, + shaper_test, + collection_new, + collection_add_deferred_face, + shared_grid_new, + shared_grid_atlas_grayscale, + shared_grid_atlas_color, + shared_grid_index_for_codepoint, + shared_grid_render_glyph, + run, + } = results.instance.exports; + + + // Helpers + const makeStr = (str) => { + const utf8 = new TextEncoder().encode(str); + const ptr = malloc(utf8.byteLength); + new Uint8Array(zjs.memory.buffer, ptr).set(utf8); + return { ptr: ptr, len: utf8.byteLength }; + }; + // Create our config + const config_str = makeStr("font-family = monospace"); + + const config = config_new(); + config_load_string(config, config_str); + config_finalize(config); + free(config_str.ptr); + // Create our atlas + // const atlas = atlas_new(512, 0 /* grayscale */); + + // Create some memory for our string + const font_name = makeStr("monospace"); + + // Initialize our deferred face + // const df = deferred_face_new(font_ptr, font.byteLength, 0 /* text */); + //deferred_face_load(df, 72 /* size */); + //const face = deferred_face_face(df); + + // Initialize our font face + //const face = face_new(font_ptr, font.byteLength, 72 /* size in px */); + //free(font_ptr); + + // Create our group + const collection = collection_new(32); + collection_add_deferred_face( + collection, + 0 /* regular */, + deferred_face_new(font_name.ptr, font_name.len, 0 /* text */), + ); + collection_add_deferred_face( + collection, + 0 /* regular */, + deferred_face_new(font_name.ptr, font_name.len, 1 /* emoji */), + ); + const grid = shared_grid_new(collection); + + // Initialize our sprite font, without this we just use the browser. + // group_init_sprite_face(group); + + // // Create our group cache + // const group_cache = group_cache_new(group); + + // Render a glyph + for (let i = 33; i <= 126; i++) { + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); + //face_render_glyph(face, atlas, i); + } + // + const emoji = ["🐏", "🌞", "🌚", "🍱", "πŸ’Ώ", "🐈", "πŸ“ƒ", "πŸ“€", "πŸ•‘", "πŸ™ƒ"]; + for (let i = 0; i < emoji.length; i++) { + const cp = emoji[i].codePointAt(0); + const font_idx = shared_grid_index_for_codepoint(grid, cp, 0, -1 /* best choice */); + shared_grid_render_glyph(grid, font_idx, cp, 0); + } + + for (let i = 0x2500; i <= 0x257f; i++) { + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); + } + for (let i = 0x2580; i <= 0x259f; i++) { + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); + } + for (let i = 0x2800; i <= 0x28ff; i++) { + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); + } + for (let i = 0x1fb00; i <= 0x1fb3b; i++) { + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); + } + for (let i = 0x1fb3c; i <= 0x1fb6b; i++) { + const font_idx = shared_grid_index_for_codepoint(grid, i, 0, -1); + shared_grid_render_glyph(grid, font_idx, i, 0); + } + + //face_render_glyph(face, atlas, "ζ©‹".codePointAt(0)); + //face_render_glyph(face, atlas, "p".codePointAt(0)); + + // Debug our canvas + //face_debug_canvas(face); + + // Let's try shaping + const shaper = shaper_new(120); + //const input = makeStr("hello🐏"); + const input = makeStr("M_yhelloaaaaaaaaa\n🐏\nπŸ‘πŸ½\nM_ghostty"); + shaper_test(shaper, grid, input.ptr, input.len); + + const cp = 1114112; + const font_idx = shared_grid_index_for_codepoint( + grid, + cp, + 0, + -1 /* best choice */, + ); + shared_grid_render_glyph(grid, font_idx, cp, -1); + + // Debug our atlas canvas + { + const atlas = shared_grid_atlas_grayscale(grid); + const id = atlas_debug_canvas(atlas); + document.getElementById("atlas-canvas").append(zjs.deleteValue(id)); + } + + { + const atlas = shared_grid_atlas_color(grid); + const id = atlas_debug_canvas(atlas); + document.getElementById("atlas-color-canvas").append(zjs.deleteValue(id)); + } + + //face_free(face); +} diff --git a/example/package-lock.json b/example/package-lock.json index c18a6b878..bfeb345be 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -9,12 +9,22 @@ "version": "0.1.0", "license": "MIT", "dependencies": { + "glslog": "^0.0.10", "zig-js": "https://gitpkg.vercel.app/mitchellh/zig-js/js?main" }, "devDependencies": { "@parcel/transformer-inline-string": "^2.8.0", + "buffer": "^5.7.1", + "crypto-browserify": "^3.12.1", + "events": "^3.3.0", + "os-browserify": "^0.3.0", "parcel": "^2.8.0", - "typescript": "^4.9.3" + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "stream-browserify": "^3.0.0", + "string_decoder": "^1.3.0", + "typescript": "^4.9.3", + "vm-browserify": "^1.1.2" } }, "../js": { @@ -1487,6 +1497,25 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, "node_modules/base-x": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", @@ -1496,12 +1525,124 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true, + "license": "MIT" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dev": true, + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/browserslist": { "version": "4.21.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", @@ -1530,12 +1671,44 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true, + "license": "MIT" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1586,6 +1759,20 @@ "node": ">=6.0" } }, + "node_modules/cipher-base": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.5.tgz", + "integrity": "sha512-xq7ICKB4TMHUx7Tz1L9O2SGKOhYMOTR32oir45Bq28/AQTpHogKgHcoYFSdRbMtddl+ozNXfXY9jWcgYKmde0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -1622,6 +1809,13 @@ "node": ">= 10" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -1638,6 +1832,80 @@ "node": ">=10" } }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/css-select": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", @@ -1691,6 +1959,17 @@ "node": ">=8.0.0" } }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -1703,6 +1982,25 @@ "node": ">=0.10" } }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -1788,6 +2086,29 @@ "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, "node_modules/entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", @@ -1827,6 +2148,27 @@ "node": ">=0.8.0" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "node_modules/get-port": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", @@ -1851,6 +2193,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/glslog": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/glslog/-/glslog-0.0.10.tgz", + "integrity": "sha512-RJn15AQAH0qYvGZNtRdAN4KCsGNUC6ArIn5EJIbWMo96vjf+ZEHk7jStNa12mWNQIPZ6S7kPKsK/pBqt9GDChA==", + "license": "MIT" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1860,6 +2208,43 @@ "node": ">=8" } }, + "node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/htmlnano": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.3.tgz", @@ -1926,6 +2311,27 @@ "entities": "^3.0.1" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -1942,6 +2348,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -1954,6 +2367,13 @@ "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", "dev": true }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2198,12 +2618,59 @@ "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", "dev": true }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, "node_modules/msgpackr": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.8.1.tgz", @@ -2293,6 +2760,13 @@ "integrity": "sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ==", "dev": true }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true, + "license": "MIT" + }, "node_modules/parcel": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.8.0.tgz", @@ -2337,6 +2811,24 @@ "node": ">=6" } }, + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dev": true, + "license": "ISC", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -2355,6 +2847,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -2364,6 +2863,23 @@ "node": ">=8" } }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -2425,6 +2941,66 @@ "node": ">=12" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, "node_modules/react-error-overlay": { "version": "6.0.9", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", @@ -2440,6 +3016,39 @@ "node": ">=0.10.0" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -2455,6 +3064,17 @@ "node": ">=4" } }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2484,6 +3104,20 @@ "semver": "bin/semver" } }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -2510,6 +3144,42 @@ "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", "dev": true }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2642,6 +3312,13 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/utility-types": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", @@ -2657,6 +3334,13 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/weak-lru-cache": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", @@ -3636,6 +4320,25 @@ "color-convert": "^2.0.1" } }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true + } + } + }, "base-x": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", @@ -3645,12 +4348,96 @@ "safe-buffer": "^5.0.1" } }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "dev": true, + "requires": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + } + }, + "browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dev": true, + "requires": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + } + }, "browserslist": { "version": "4.21.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", @@ -3663,12 +4450,28 @@ "update-browserslist-db": "^1.0.9" } }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3697,6 +4500,16 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, + "cipher-base": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.5.tgz", + "integrity": "sha512-xq7ICKB4TMHUx7Tz1L9O2SGKOhYMOTR32oir45Bq28/AQTpHogKgHcoYFSdRbMtddl+ozNXfXY9jWcgYKmde0w==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + } + }, "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -3724,6 +4537,12 @@ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -3737,6 +4556,71 @@ "yaml": "^1.10.0" } }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "crypto-browserify": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + } + }, "css-select": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", @@ -3775,12 +4659,41 @@ "css-tree": "^1.1.2" } }, + "des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true + } + } + }, "dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -3844,6 +4757,29 @@ "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, + "elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true + } + } + }, "entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", @@ -3871,6 +4807,22 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "get-port": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", @@ -3886,12 +4838,48 @@ "type-fest": "^0.20.2" } }, + "glslog": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/glslog/-/glslog-0.0.10.tgz", + "integrity": "sha512-RJn15AQAH0qYvGZNtRdAN4KCsGNUC6ArIn5EJIbWMo96vjf+ZEHk7jStNa12mWNQIPZ6S7kPKsK/pBqt9GDChA==" + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "htmlnano": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.3.tgz", @@ -3915,6 +4903,12 @@ "entities": "^3.0.1" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3925,6 +4919,12 @@ "resolve-from": "^4.0.0" } }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -3937,6 +4937,12 @@ "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", "dev": true }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4061,12 +5067,53 @@ } } }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, "msgpackr": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.8.1.tgz", @@ -4137,6 +5184,12 @@ "integrity": "sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ==", "dev": true }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, "parcel": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.8.0.tgz", @@ -4168,6 +5221,20 @@ "callsites": "^3.0.0" } }, + "parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dev": true, + "requires": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + } + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -4180,12 +5247,31 @@ "lines-and-columns": "^1.1.6" } }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -4237,6 +5323,59 @@ "is-json": "^2.0.1" } }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true + } + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, "react-error-overlay": { "version": "6.0.9", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", @@ -4249,6 +5388,38 @@ "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", "dev": true }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -4261,6 +5432,16 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4273,6 +5454,16 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4295,6 +5486,38 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, + "stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "requires": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4379,6 +5602,12 @@ "picocolors": "^1.0.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "utility-types": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", @@ -4391,6 +5620,12 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, "weak-lru-cache": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", diff --git a/example/package.json b/example/package.json index b031a6cc9..2619b96a6 100644 --- a/example/package.json +++ b/example/package.json @@ -13,10 +13,20 @@ "license": "MIT", "devDependencies": { "@parcel/transformer-inline-string": "^2.8.0", + "buffer": "^5.7.1", + "crypto-browserify": "^3.12.1", + "events": "^3.3.0", + "os-browserify": "^0.3.0", "parcel": "^2.8.0", - "typescript": "^4.9.3" + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "stream-browserify": "^3.0.0", + "string_decoder": "^1.3.0", + "typescript": "^4.9.3", + "vm-browserify": "^1.1.2" }, "dependencies": { + "glslog": "^0.0.10", "zig-js": "https://gitpkg.vercel.app/mitchellh/zig-js/js?main" } } diff --git a/example/worker.ts b/example/worker.ts new file mode 100644 index 000000000..5ca06d8f3 --- /dev/null +++ b/example/worker.ts @@ -0,0 +1,11 @@ +import { importObject, zjs } from "./imports"; + +onmessage = async (e) => { + console.log("module received from main thread"); + const [memory, instance] = e.data; + importObject.env.memory = memory; +const url = new URL("ghostty-wasm.wasm", import.meta.url); + const results = await WebAssembly.instantiateStreaming(fetch(url), importObject) + zjs.memory = memory; + results.instance.exports.wasi_thread_start(100, instance); +}; diff --git a/pkg/opengl/c.zig b/pkg/opengl/c.zig index fc95ee94c..60c13cf6b 100644 --- a/pkg/opengl/c.zig +++ b/pkg/opengl/c.zig @@ -1,3 +1,284 @@ -pub const c = @cImport({ +const builtin = @import("builtin"); +pub const c = if (builtin.cpu.arch != .wasm32) @cImport({ @cInclude("glad/gl.h"); -}); +}) else struct { + pub extern fn glBindBufferBase(_: c_uint, _: c_uint, _: c_uint) void; + pub extern fn glDrawElementsInstanced(_: GLenum, _: GLsizei, _: GLenum, _: ?*const anyopaque, _: GLsizei) void; + pub extern fn glUniform4f(_: c_int, _: f32, _: f32, _: f32, _: f32) void; + pub extern fn glUniform4fv(_: c_int, _: f32, _: f32, _: f32, _: f32) void; + pub extern fn glBindFramebuffer(_: c_uint, _: c_uint) void; + pub extern fn glGetIntegerv(_: GLenum, _: *GLint) void; + pub extern fn glTexSubImage2D(_: c_uint, _: c_int, _: c_int, _: isize, _: isize, _: c_int, _: c_uint, _: c_uint, _: ?*const anyopaque) void; + pub extern fn glDeleteFramebuffers(_: c_int, _: [*c]const c_uint) void; + pub extern fn glGenFramebuffers(_: c_int, _: [*c]c_uint) void; + pub extern fn glVertexAttribDivisor(_: c_uint, _: c_uint) void; + pub extern fn glVertexAttribIPointer(_: c_uint, _: c_int, _: c_uint, _: isize, _: ?*const anyopaque) void; + pub extern fn glBufferSubData(_: GLenum, _: isize, _: isize, _: ?*const anyopaque) void; + pub extern fn glViewport(_: c_int, _: c_int, _: isize, _: isize) void; + pub extern fn glClearColor(_: f32, _: f32, _: f32, _: f32) void; + pub extern fn glEnable(_: c_uint) void; + pub extern fn glDisable(_: c_uint) void; + pub extern fn glDepthFunc(_: c_uint) void; + pub extern fn glBlendFunc(_: c_uint, _: c_uint) void; + pub extern fn glClear(_: c_uint) void; + pub extern fn glGetAttribLocation(_: c_uint, _: [*]const u8, _: c_uint) c_int; + pub extern fn glGetUniformLocation(_: c_uint, _: [*]const u8) c_int; + pub extern fn glUniform1i(_: c_int, _: c_int) void; + pub extern fn glUniform1f(_: c_int, _: f32) void; + pub extern fn glUniformMatrix4fv(_: c_int, _: c_int, _: c_uint, _: *const f32) void; + pub extern fn glCreateVertexArray() c_uint; + pub extern fn glGenVertexArrays(_: c_int, [*c]c_uint) void; + pub extern fn glDeleteVertexArrays(_: c_int, [*c]const c_uint) void; + pub extern fn glBindVertexArray(_: c_uint) void; + pub extern fn glCreateBuffer() c_uint; + pub extern fn glGenBuffers(_: c_int, _: [*c]c_uint) void; + pub extern fn glDeleteBuffers(_: c_int, _: [*c]const c_uint) void; + pub extern fn glDeleteBuffer(_: c_uint) void; + pub extern fn glBindBuffer(_: c_uint, _: c_uint) void; + pub extern fn glBufferData(_: c_uint, _: isize, _: ?*const anyopaque, _: c_uint) void; + pub extern fn glPixelStorei(_: c_uint, _: c_int) void; + pub extern fn glAttachShader(_: c_uint, _: c_uint) void; + pub extern fn glDetachShader(_: c_uint, _: c_uint) void; + pub extern fn glDeleteShader(_: c_uint) void; + pub extern fn glCreateShader(_: c_uint) c_uint; + pub extern fn glCompileShader(_: c_uint) void; + pub extern fn glShaderSource(_: c_uint, _: c_uint, _: *const [*c]const u8, _: ?*GLint) void; + pub extern fn glCreateProgram() c_uint; + pub extern fn glGetShaderiv(_: c_uint, _: c_uint, _: [*c]c_int) void; + pub extern fn glGetShaderInfoLog(_: c_uint, _: c_int, _: [*c]c_int, _: [*c]u8) void; + pub extern fn glGetProgramiv(_: c_uint, _: c_uint, _: [*c]c_int) void; + pub extern fn glLinkProgram(_: c_uint) void; + pub extern fn glUseProgram(_: c_uint) void; + pub extern fn glGetProgramInfoLog(_: c_uint, _: c_int, _: [*c]c_int, _: [*c]u8) void; + pub extern fn glDeleteProgram(_: c_uint) void; + pub extern fn glEnableVertexAttribArray(_: c_uint) void; + pub extern fn glVertexAttribPointer(_: c_uint, _: c_int, _: c_uint, _: c_uint, _: isize, _: ?*const anyopaque) void; + pub extern fn glDrawArrays(_: c_uint, _: c_uint, _: c_int) void; + pub extern fn glCreateTexture() c_uint; + pub extern fn glGenTextures(_: c_int, _: [*c]c_uint) void; + pub extern fn glDeleteTextures(_: c_int, _: [*c]const c_uint) void; + pub extern fn glDeleteTexture(_: c_uint) void; + pub extern fn glBindTexture(_: c_uint, _: c_uint) void; + pub extern fn glTexImage2D(_: GLenum, _: GLint, _: c_int, _: c_int, _: c_int, _: c_int, _: c_uint, _: c_uint, _: ?*const anyopaque) void; + pub extern fn glTexParameteri(_: c_uint, _: c_uint, _: c_int) void; + pub extern fn glActiveTexture(_: c_uint) void; + pub extern fn glGenerateMipmap(_: c_uint) void; + pub extern fn glGetError() c_int; + pub extern fn glGetString(_: c_int) c_int; + pub extern fn glGetShaderParameter(_: c_uint, _: c_uint) c_int; + pub extern fn glUniform2f(_: c_int, _: f32, _: f32) void; + + // Types. + pub const GLuint = c_uint; + pub const GLenum = c_uint; + pub const GLbitfield = c_uint; + pub const GLint = c_int; + pub const GLsizei = isize; + pub const GLfloat = f32; + pub const GLboolean = u8; + + // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants + pub const GL_FALSE = 0; + pub const GL_TRUE = 1; + pub const GL_TRIANGLES = 4; + pub const GL_DEPTH_BUFFER_BIT = 256; + pub const GL_SRC_ALPHA = 770; + pub const GL_ONE_MINUS_SRC_ALPHA = 771; + pub const GL_FLOAT = 5126; + pub const GL_CULL_FACE = 2884; + pub const GL_DEPTH_TEST = 2929; + pub const GL_BLEND = 3042; + pub const GL_TEXTURE_2D = 3553; + pub const GL_UNSIGNED_BYTE = 5121; + pub const GL_RED = 6403; + pub const GL_RGBA = 6408; + pub const GL_VERSION = 7938; + pub const GL_LINEAR: GLint = 9729; + pub const GL_TEXTURE_MAG_FILTER = 10240; + pub const GL_TEXTURE_MIN_FILTER = 10241; + pub const GL_TEXTURE_WRAP_S = 10242; + pub const GL_TEXTURE_WRAP_T = 10243; + pub const GL_COLOR_BUFFER_BIT = 16384; + pub const GL_CLAMP_TO_EDGE: GLint = 33071; + pub const GL_RG = 33319; + pub const GL_RG32F = 33327; + pub const GL_TEXTURE0 = 33984; + pub const GL_TEXTURE1 = 33985; + pub const GL_TEXTURE2 = 33986; + pub const GL_TEXTURE3 = 33987; + pub const GL_TEXTURE4 = 33988; + pub const GL_TEXTURE5 = 33989; + pub const GL_RGBA32F = 34836; + pub const GL_ARRAY_BUFFER = 34962; + pub const GL_STATIC_DRAW = 35044; + pub const GL_FRAGMENT_SHADER = 35632; + pub const GL_VERTEX_SHADER = 35633; + pub const GL_COMPILE_STATUS = 35713; + pub const GL_LINK_STATUS = 35714; + pub const GL_FRAMEBUFFER_COMPLETE = 36053; + pub const GL_COLOR_ATTACHMENT0 = 36064; + pub const GL_COLOR_ATTACHMENT1 = 36065; + pub const GL_COLOR_ATTACHMENT2 = 36066; + pub const GL_DEPTH_ATTACHMENT = 36096; + pub const GL_FRAMEBUFFER = 36160; + pub const GL_RENDERBUFFER = 36161; + + pub const GL_NO_ERROR = 0; + pub const GL_INVALID_ENUM = 0x0500; + pub const GL_INVALID_FRAMEBUFFER_OPERATION = 0x0506; + pub const GL_INVALID_OPERATION = 0x0502; + pub const GL_INVALID_VALUE = 0x0501; + pub const GL_OUT_OF_MEMORY = 0x0505; + pub const GL_ONE = 1; + + pub const GL_TEXTURE_1D = 0x0DE0; + pub const GL_TEXTURE_2D_ARRAY = 0x8C1A; + pub const GL_TEXTURE_1D_ARRAY = 0x8C18; + pub const GL_TEXTURE_3D = 0x806F; + pub const GL_TEXTURE_RECTANGLE = 0x84F5; + pub const GL_TEXTURE_CUBE_MAP = 0x8513; + pub const GL_TEXTURE_BUFFER = 0x8C2A; + pub const GL_TEXTURE_2D_MULTISAMPLE = 0x9100; + pub const GL_TEXTURE_2D_MULTISAMPLE_ARRAY = 0x9102; + pub const GL_TEXTURE_BASE_LEVEL = 0x813C; + pub const GL_TEXTURE_SWIZZLE_A = 0x8E45; + pub const GL_TEXTURE_SWIZZLE_B = 0x8E44; + pub const GL_TEXTURE_SWIZZLE_G = 0x8E43; + pub const GL_TEXTURE_SWIZZLE_R = 0x8E42; + pub const GL_TEXTURE_COMPARE_FUNC = 0x884D; + pub const GL_TEXTURE_COMPARE_MODE = 0x884C; + pub const GL_TEXTURE_LOD_BIAS = 0x8501; + pub const GL_TEXTURE_MIN_LOD = 0x813A; + pub const GL_TEXTURE_MAX_LOD = 0x813B; + pub const GL_TEXTURE_MAX_LEVEL = 0x813D; + pub const GL_TEXTURE_WRAP_R = 0x8072; + pub const GL_RGB = 0x1907; + pub const GL_BGRA = 0x80E1; + pub const GL_ELEMENT_ARRAY_BUFFER = 0x8893; + pub const GL_UNIFORM_BUFFER = 0x8A11; + pub const GL_STREAM_COPY = 0x88E2; + pub const GL_STREAM_DRAW = 0x88E0; + pub const GL_STREAM_READ = 0x88E1; + pub const GL_STATIC_COPY = 0x88E6; + pub const GL_STATIC_READ = 0x88E5; + pub const GL_DYNAMIC_COPY = 0x88EA; + pub const GL_DYNAMIC_DRAW = 0x88E8; + pub const GL_DYNAMIC_READ = 0x88E9; + pub const GL_UNSIGNED_SHORT = 0x1403; + pub const GL_INT = 0x1404; + pub const GL_UNSIGNED_INT = 0x1405; + pub const GL_DRAW_FRAMEBUFFER = 0x8CA9; + pub const GL_READ_FRAMEBUFFER_BINDING = 0x8CAA; + pub const GL_READ_FRAMEBUFFER = 0x8CA8; + pub const GL_FRAMEBUFFER_BINDING = 0x8CA6; + pub const GladGLContext = struct { + pub fn init(self: *GladGLContext) void { + self.* = .{ + .Enable = glEnable, + .GetError = glGetError, + .BlendFunc = glBlendFunc, + .BindTexture = glBindTexture, + .GenTextures = glGenTextures, + .DeleteTextures = glDeleteTextures, + .TexImage2D = glTexImage2D, + .CreateShader = glCreateShader, + .CompileShader = glCompileShader, + .DeleteShader = glDeleteShader, + .AttachShader = glAttachShader, + .ShaderSource = glShaderSource, + .GetShaderiv = glGetShaderiv, + .GetShaderInfoLog = glGetShaderInfoLog, + .CreateProgram = glCreateProgram, + .LinkProgram = glLinkProgram, + .GetProgramiv = glGetProgramiv, + .GetProgramInfoLog = glGetProgramInfoLog, + .UseProgram = glUseProgram, + .DeleteProgram = glDeleteProgram, + .GetUniformLocation = glGetUniformLocation, + .Uniform4fv = glUniform4fv, + .Uniform1i = glUniform1i, + .Uniform1f = glUniform1f, + .Uniform2f = glUniform2f, + .UniformMatrix4fv = glUniformMatrix4fv, + .GenVertexArrays = glGenVertexArrays, + .DeleteVertexArrays = glDeleteVertexArrays, + .BindVertexArray = glBindVertexArray, + .GenBuffers = glGenBuffers, + .DeleteBuffers = glDeleteBuffers, + .DeleteBuffer = glDeleteBuffer, + .BindBuffer = glBindBuffer, + .BufferData = glBufferData, + .BufferSubData = glBufferSubData, + .VertexAttribPointer = glVertexAttribPointer, + .VertexAttribIPointer = glVertexAttribIPointer, + .EnableVertexAttribArray = glEnableVertexAttribArray, + .VertexAttribDivisor = glVertexAttribDivisor, + .GenFramebuffers = glGenFramebuffers, + .DeleteFramebuffers = glDeleteFramebuffers, + .TexSubImage2D = glTexSubImage2D, + .GetIntegerv = glGetIntegerv, + .BindFramebuffer = glBindFramebuffer, + .ClearColor = glClearColor, + .Clear = glClear, + .Viewport = glViewport, + .Uniform4f = glUniform4f, + .ActiveTexture = glActiveTexture, + .DrawElementsInstanced = glDrawElementsInstanced, + .BindBufferBase = glBindBufferBase, + .TexParameteri = glTexParameteri, + }; + } + Enable: ?*const fn (_: GLenum) callconv(.C) void, + GetError: ?*const fn () callconv(.C) c_int, + BlendFunc: ?*const fn (_: c_uint, _: c_uint) callconv(.C) void, + BindTexture: ?*const fn (_: c_uint, _: c_uint) callconv(.C) void, + GenTextures: ?*const fn (_: c_int, _: [*c]c_uint) callconv(.C) void, + DeleteTextures: ?*const fn (_: c_int, _: [*c]const c_uint) callconv(.C) void, + TexImage2D: ?*const fn (_: GLenum, _: GLint, _: c_int, _: c_int, _: c_int, _: c_int, _: c_uint, _: c_uint, _: ?*const anyopaque) callconv(.C) void, + CreateShader: ?*const fn (_: GLenum) callconv(.C) GLuint, + CompileShader: ?*const fn (_: c_uint) callconv(.C) void, + DeleteShader: ?*const fn (_: c_uint) callconv(.C) void, + AttachShader: ?*const fn (_: c_uint, _: c_uint) callconv(.C) void, + ShaderSource: ?*const fn (_: c_uint, _: c_uint, _: *const [*c]const u8, _: ?*GLint) callconv(.C) void, + GetShaderiv: ?*const fn (_: c_uint, _: c_uint, _: [*c]c_int) callconv(.C) void, + GetShaderInfoLog: ?*const fn (_: c_uint, _: c_int, _: [*c]c_int, _: [*c]u8) callconv(.C) void, + CreateProgram: ?*const fn () callconv(.C) c_uint, + LinkProgram: ?*const fn (_: c_uint) callconv(.C) void, + GetProgramiv: ?*const fn (_: c_uint, _: c_uint, _: [*c]c_int) callconv(.C) void, + GetProgramInfoLog: ?*const fn (_: c_uint, _: c_int, _: [*c]c_int, _: [*c]u8) callconv(.C) void, + UseProgram: ?*const fn (_: c_uint) callconv(.C) void, + DeleteProgram: ?*const fn (_: c_uint) callconv(.C) void, + GetUniformLocation: ?*const fn (_: c_uint, _: [*]const u8) callconv(.C) c_int, + Uniform4fv: ?*const fn (_: c_int, _: f32, _: f32, _: f32, _: f32) callconv(.C) void, + Uniform1i: ?*const fn (_: c_int, _: c_int) callconv(.C) void, + Uniform1f: ?*const fn (_: c_int, _: f32) callconv(.C) void, + Uniform2f: ?*const fn (_: c_int, _: f32, _: f32) callconv(.C) void, + UniformMatrix4fv: ?*const fn (_: c_int, _: c_int, _: c_uint, _: *const f32) callconv(.C) void, + GenVertexArrays: ?*const fn (_: c_int, [*c]c_uint) callconv(.C) void, + DeleteVertexArrays: ?*const fn (_: c_int, [*c]const c_uint) callconv(.C) void, + BindVertexArray: ?*const fn (_: c_uint) callconv(.C) void, + GenBuffers: ?*const fn (_: c_int, _: [*c]c_uint) callconv(.C) void, + DeleteBuffers: ?*const fn (_: c_int, _: [*c]const c_uint) callconv(.C) void, + DeleteBuffer: ?*const fn (_: c_uint) callconv(.C) void, + BindBuffer: ?*const fn (_: c_uint, _: c_uint) callconv(.C) void, + BufferData: ?*const fn (_: c_uint, _: isize, _: ?*const anyopaque, _: c_uint) callconv(.C) void, + BufferSubData: ?*const fn (_: GLenum, _: isize, _: isize, _: ?*const anyopaque) callconv(.C) void, + VertexAttribPointer: ?*const fn (_: c_uint, _: c_int, _: c_uint, _: c_uint, _: isize, _: ?*const anyopaque) callconv(.C) void, + VertexAttribIPointer: ?*const fn (_: c_uint, _: c_int, _: c_uint, _: isize, _: ?*const anyopaque) callconv(.C) void, + EnableVertexAttribArray: ?*const fn (_: c_uint) callconv(.C) void, + VertexAttribDivisor: ?*const fn (_: c_uint, _: c_uint) callconv(.C) void, + GenFramebuffers: ?*const fn (_: c_int, _: [*c]c_uint) callconv(.C) void, + DeleteFramebuffers: ?*const fn (_: c_int, _: [*c]const c_uint) callconv(.C) void, + TexSubImage2D: ?*const fn (_: c_uint, _: c_int, _: c_int, _: isize, _: isize, _: c_int, _: c_uint, _: c_uint, _: ?*const anyopaque) callconv(.C) void, + GetIntegerv: ?*const fn (_: GLenum, _: *GLint) callconv(.C) void, + BindFramebuffer: ?*const fn (_: c_uint, _: c_uint) callconv(.C) void, + ClearColor: ?*const fn (_: f32, _: f32, _: f32, _: f32) callconv(.C) void, + Clear: ?*const fn (_: c_uint) callconv(.C) void, + Viewport: ?*const fn (_: c_int, _: c_int, _: isize, _: isize) callconv(.C) void, + Uniform4f: ?*const fn (_: c_int, _: f32, _: f32, _: f32, _: f32) callconv(.C) void, + ActiveTexture: ?*const fn (_: c_uint) callconv(.C) void, + DrawElementsInstanced: ?*const fn (_: GLenum, _: GLsizei, _: GLenum, _: ?*const anyopaque, _: GLsizei) callconv(.C) void, + BindBufferBase: ?*const fn (_: c_uint, _: c_uint, _: c_uint) callconv(.C) void, + TexParameteri: ?*const fn (_: c_uint, _: c_uint, _: c_int) callconv(.C) void, + }; +}; diff --git a/pkg/simdutf/build.zig b/pkg/simdutf/build.zig index 1532e4a33..123d8f903 100644 --- a/pkg/simdutf/build.zig +++ b/pkg/simdutf/build.zig @@ -22,6 +22,7 @@ pub fn build(b: *std.Build) !void { // Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414 // (See root Ghostty build.zig on why we do this) try flags.appendSlice(&.{"-DSIMDUTF_IMPLEMENTATION_ICELAKE=0"}); + try flags.appendSlice(&.{"-DSIMDUTF_NO_THREADS"}); lib.addCSourceFiles(.{ .flags = flags.items, diff --git a/pkg/simdutf/vendor/simdutf.h b/pkg/simdutf/vendor/simdutf.h index d37bd2c7d..89c9d1ca1 100644 --- a/pkg/simdutf/vendor/simdutf.h +++ b/pkg/simdutf/vendor/simdutf.h @@ -619,9 +619,9 @@ enum { #ifndef SIMDUTF_IMPLEMENTATION_H #define SIMDUTF_IMPLEMENTATION_H #include -#if !defined(SIMDUTF_NO_THREADS) -#include -#endif +// #if !defined(SIMDUTF_NO_THREADS) +// #include +// #endif #include #include /* begin file include/simdutf/internal/isadetection.h */ @@ -3692,7 +3692,7 @@ class atomic_ptr { public: atomic_ptr(T *_ptr) : ptr{_ptr} {} -#if defined(SIMDUTF_NO_THREADS) +// #if defined(SIMDUTF_NO_THREADS) operator const T*() const { return ptr; } const T& operator*() const { return *ptr; } const T* operator->() const { return ptr; } @@ -3702,24 +3702,24 @@ public: T* operator->() { return ptr; } atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; } -#else - operator const T*() const { return ptr.load(); } - const T& operator*() const { return *ptr; } - const T* operator->() const { return ptr.load(); } +// #else +// operator const T*() const { return ptr.load(); } +// const T& operator*() const { return *ptr; } +// const T* operator->() const { return ptr.load(); } - operator T*() { return ptr.load(); } - T& operator*() { return *ptr; } - T* operator->() { return ptr.load(); } - atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; } +// operator T*() { return ptr.load(); } +// T& operator*() { return *ptr; } +// T* operator->() { return ptr.load(); } +// atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; } -#endif +// #endif private: -#if defined(SIMDUTF_NO_THREADS) +// #if defined(SIMDUTF_NO_THREADS) T* ptr; -#else - std::atomic ptr; -#endif +// #else +// std::atomic ptr; +// #endif }; class detect_best_supported_implementation_on_first_use; diff --git a/src/Command.zig b/src/Command.zig index daca54f94..9f3ce499c 100644 --- a/src/Command.zig +++ b/src/Command.zig @@ -73,7 +73,8 @@ pseudo_console: if (builtin.os.tag == .windows) ?windows.exp.HPCON else void = data: ?*anyopaque = null, /// Process ID is set after start is called. -pid: ?posix.pid_t = null, +pid: ?PidT = null, +pub const PidT = if (builtin.cpu.arch != .wasm32) posix.pid_t else i32; /// LinuxCGroup type depends on our target OS pub const LinuxCgroup = if (builtin.os.tag == .linux) ?[]const u8 else void; @@ -122,6 +123,7 @@ pub fn start(self: *Command, alloc: Allocator) !void { switch (builtin.os.tag) { .windows => try self.startWindows(arena), + .wasi => try self.startWasi(arena), else => try self.startPosix(arena), } } @@ -187,6 +189,65 @@ fn startPosix(self: *Command, arena: Allocator) !void { return error.ExecFailedInChild; } +fn startWasi(self: *Command, arena: Allocator) !void { + // Null-terminate all our arguments + const pathZ = try arena.dupeZ(u8, self.path); + const argsZ = try arena.allocSentinel(?[*:0]u8, self.args.len, null); + for (self.args, 0..) |arg, i| argsZ[i] = (try arena.dupeZ(u8, arg)).ptr; + + // Determine our env vars + const envp = if (self.env) |env_map| + (try createNullDelimitedEnvMap(arena, env_map)).ptr + else if (builtin.link_libc) + std.c.environ + else + @compileError("missing env vars"); + + // Fork. If we have a cgroup specified on Linxu then we use clone + const pid: posix.pid_t = switch (builtin.os.tag) { + .linux => if (self.linux_cgroup) |cgroup| + try internal_os.cgroup.cloneInto(cgroup) + else + try posix.fork(), + + else => try posix.fork(), + }; + + if (pid != 0) { + // Parent, return immediately. + self.pid = @intCast(pid); + return; + } + + // We are the child. + + // Setup our file descriptors for std streams. + if (self.stdin) |f| setupFd(f.handle, posix.STDIN_FILENO) catch + return error.ExecFailedInChild; + if (self.stdout) |f| setupFd(f.handle, posix.STDOUT_FILENO) catch + return error.ExecFailedInChild; + if (self.stderr) |f| setupFd(f.handle, posix.STDERR_FILENO) catch + return error.ExecFailedInChild; + + // Setup our working directory + if (self.cwd) |cwd| posix.chdir(cwd) catch { + // 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. + }; + + // If the user requested a pre exec callback, call it now. + if (self.pre_exec) |f| f(self); + std.log.err("{s} {*}", .{ pathZ, envp }); + + // If we are executing this code, the exec failed. In that scenario, + // we return a very specific error that can be detected to determine + // we're in the child. + return; +} + fn startWindows(self: *Command, arena: Allocator) !void { const application_w = try std.unicode.utf8ToUtf16LeWithNull(arena, self.path); const cwd_w = if (self.cwd) |cwd| try std.unicode.utf8ToUtf16LeWithNull(arena, cwd) else null; @@ -324,6 +385,7 @@ fn setupFd(src: File.Handle, target: i32) !void { try posix.dup2(src, target); }, + .wasi => {}, else => @compileError("unsupported platform"), } } diff --git a/src/Surface.zig b/src/Surface.zig index 4d2cff165..7f3b9af6b 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -21,7 +21,6 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const global_state = &@import("global.zig").state; -const oni = @import("oniguruma"); const crash = @import("crash/main.zig"); const unicode = @import("unicode/main.zig"); const renderer = @import("renderer.zig"); @@ -256,7 +255,7 @@ const DerivedConfig = struct { links: []Link, const Link = struct { - regex: oni.Regex, + regex: input.Link.Regex, action: input.Link.Action, highlight: input.Link.Highlight, }; @@ -586,7 +585,7 @@ pub fn init( // Start our renderer thread self.renderer_thr = try std.Thread.spawn( - .{}, + .{ .allocator = alloc }, renderer.Thread.threadMain, .{&self.renderer_thread}, ); @@ -594,7 +593,7 @@ pub fn init( // Start our IO thread self.io_thr = try std.Thread.spawn( - .{}, + .{ .allocator = alloc }, termio.Thread.threadMain, .{ &self.io_thread, &self.io }, ); diff --git a/src/apprt/browser.zig b/src/apprt/browser.zig index d60776a6a..cecea4ef4 100644 --- a/src/apprt/browser.zig +++ b/src/apprt/browser.zig @@ -1,2 +1,100 @@ -pub const App = struct {}; +const CoreSurface = @import("../Surface.zig"); +const CoreApp = @import("../App.zig"); +const apprt = @import("../apprt.zig"); +const std = @import("std"); +const log = std.log; + +pub const App = struct { + app: *CoreApp, + + pub const Options = struct {}; + pub fn init(core_app: *CoreApp, _: Options) !App { + return .{ .app = core_app }; + } + pub fn performAction( + self: *App, + target: apprt.Target, + comptime action: apprt.Action.Key, + value: apprt.Action.Value(action), + ) !void { + switch (action) { + .new_window => _ = try self.newSurface(switch (target) { + .app => null, + .surface => |v| v, + }), + + .new_tab => try self.newTab(switch (target) { + .app => null, + .surface => |v| v, + }), + + .initial_size => switch (target) { + .app => {}, + .surface => |surface| try surface.rt_surface.setInitialWindowSize( + value.width, + value.height, + ), + }, + + // Unimplemented + .size_limit, + .toggle_fullscreen, + .set_title, + .mouse_shape, + .mouse_visibility, + .open_config, + .new_split, + .goto_split, + .resize_split, + .equalize_splits, + .toggle_split_zoom, + .present_terminal, + .close_all_windows, + .toggle_tab_overview, + .toggle_window_decorations, + .toggle_quick_terminal, + .toggle_visibility, + .goto_tab, + .move_tab, + .inspector, + .render_inspector, + .quit_timer, + .secure_input, + .key_sequence, + .desktop_notification, + .mouse_over_link, + .cell_size, + .renderer_health, + .color_change, + .pwd, + .config_change_conditional_state, + => log.info("unimplemented action={}", .{action}), + } + } + pub fn wakeup(self: *const App) void { + _ = self; + } +}; pub const Window = struct {}; +pub const Surface = struct { + pub const opengl_single_threaded_draw = true; + /// The app we're part of + app: *App, + + /// A core surface + core_surface: CoreSurface, + pub fn init(self: *Surface, app: *App) !void { + self.app = app; + } + pub fn getContentScale(_: *const Surface) !apprt.ContentScale { + return apprt.ContentScale{ .x = 1, .y = 1 }; + } + pub fn getSize(_: *const Surface) !apprt.SurfaceSize { + return apprt.SurfaceSize{ .width = 500, .height = 500 }; + } + fn setInitialWindowSize(self: *const Surface, width: u32, height: u32) !void { + _ = height; // autofix + _ = self; // autofix + _ = width; // autofix + } +}; diff --git a/src/build_config.zig b/src/build_config.zig index 8f9df129c..f4360620e 100644 --- a/src/build_config.zig +++ b/src/build_config.zig @@ -127,20 +127,18 @@ pub const Artifact = enum { wasm_module, pub fn detect() Artifact { - if (builtin.target.isWasm()) { - // assert(builtin.output_mode == .Obj); - // assert(builtin.link_mode == .Static); - return .wasm_module; - } + // assert(builtin.output_mode == .Obj); + // assert(builtin.link_mode == .Static); + return .wasm_module; - return switch (builtin.output_mode) { - .Exe => .exe, - .Lib => .lib, - else => { - @compileLog(builtin.output_mode); - @compileError("unsupported artifact output mode"); - }, - }; + // return switch (builtin.output_mode) { + // .Exe => .exe, + // .Lib => .lib, + // else => { + // @compileLog(builtin.output_mode); + // @compileError("unsupported artifact output mode"); + // }, + // }; } }; diff --git a/src/font/DeferredFace.zig b/src/font/DeferredFace.zig index f47904e95..757347cdf 100644 --- a/src/font/DeferredFace.zig +++ b/src/font/DeferredFace.zig @@ -163,6 +163,7 @@ pub fn load( lib: Library, opts: font.face.Options, ) !Face { + log.err("load {}", .{opts.size}); return switch (options.backend) { .fontconfig_freetype => try self.loadFontconfig(lib, opts), .coretext, .coretext_harfbuzz, .coretext_noshape => try self.loadCoreText(lib, opts), diff --git a/src/font/SharedGridSet.zig b/src/font/SharedGridSet.zig index ac2fcbf8a..a6bc66d63 100644 --- a/src/font/SharedGridSet.zig +++ b/src/font/SharedGridSet.zig @@ -255,42 +255,67 @@ fn collection( try c.completeStyles(self.alloc, config.@"font-synthetic-style"); // Our built-in font will be used as a backup - _ = try c.add( - self.alloc, - .regular, - .{ .fallback_loaded = try Face.init( - self.font_lib, - font.embedded.regular, - load_options.faceOptions(), - ) }, - ); - _ = try c.add( - self.alloc, - .bold, - .{ .fallback_loaded = try Face.init( - self.font_lib, - font.embedded.bold, - load_options.faceOptions(), - ) }, - ); - _ = try c.add( - self.alloc, - .italic, - .{ .fallback_loaded = try Face.init( - self.font_lib, - font.embedded.italic, - load_options.faceOptions(), - ) }, - ); - _ = try c.add( - self.alloc, - .bold_italic, - .{ .fallback_loaded = try Face.init( - self.font_lib, - font.embedded.bold_italic, - load_options.faceOptions(), - ) }, - ); + if (builtin.cpu.arch != .wasm32) { + _ = try c.add( + self.alloc, + .regular, + .{ .fallback_loaded = try Face.init( + self.font_lib, + font.embedded.regular, + load_options.faceOptions(), + ) }, + ); + _ = try c.add( + self.alloc, + .bold, + .{ .fallback_loaded = try Face.init( + self.font_lib, + font.embedded.bold, + load_options.faceOptions(), + ) }, + ); + _ = try c.add( + self.alloc, + .italic, + .{ .fallback_loaded = try Face.init( + self.font_lib, + font.embedded.italic, + load_options.faceOptions(), + ) }, + ); + _ = try c.add( + self.alloc, + .bold_italic, + .{ .fallback_loaded = try Face.init( + self.font_lib, + font.embedded.bold_italic, + load_options.faceOptions(), + ) }, + ); + } else { + const family = if (config.@"font-family".list.items.len > 0) config.@"font-family".list.items[0] else "monospace"; + + _ = try c.add( + self.alloc, + .regular, + .{ .fallback_loaded = try Face.initNamed( + self.alloc, + family, + load_options.size, + .text, + ) }, + ); + _ = try c.add( + self.alloc, + .regular, + .{ .fallback_loaded = try Face.initNamed( + self.alloc, + family, + load_options.size, + .emoji, + ) }, + ); + } // On macOS, always search for and add the Apple Emoji font // as our preferred emoji font for fallback. We do this in case @@ -314,7 +339,7 @@ fn collection( // Emoji fallback. We don't include this on Mac since Mac is expected // to always have the Apple Emoji available on the system. - if (comptime !builtin.target.isDarwin() or Discover == void) { + if (builtin.cpu.arch != .wasm32 and comptime !builtin.target.isDarwin() or Discover == void) { _ = try c.add( self.alloc, .regular, diff --git a/src/font/face/web_canvas.zig b/src/font/face/web_canvas.zig index 51c76712a..8b04fe947 100644 --- a/src/font/face/web_canvas.zig +++ b/src/font/face/web_canvas.zig @@ -58,10 +58,10 @@ pub const Face = struct { const font_str = try alloc.dupe(u8, raw); errdefer alloc.free(font_str); - // Create our canvas that we're going to continue to reuse. - const doc = try js.global.get(js.Object, "document"); - defer doc.deinit(); - const canvas = try doc.call(js.Object, "createElement", .{js.string("canvas")}); + // Create our canvasxx that we're going to continue to reuse. + const OffscreenCanvas = try js.global.get(js.Object, "OffscreenCanvas"); + defer OffscreenCanvas.deinit(); + const canvas = try OffscreenCanvas.new(.{ 0, 0 }); errdefer canvas.deinit(); var result = Face{ @@ -232,7 +232,7 @@ pub const Face = struct { .height = render.height, // TODO: this can't be right .offset_x = 0, - .offset_y = 0, + .offset_y = render.y_offset, .atlas_x = region.x, .atlas_y = region.y, .advance_x = 0, @@ -262,8 +262,8 @@ pub const Face = struct { // pixel height but this is a more surefire way to get it. const height_metrics = try ctx.call(js.Object, "measureText", .{js.string("M_")}); defer height_metrics.deinit(); - const asc = try height_metrics.get(f32, "actualBoundingBoxAscent"); - const desc = try height_metrics.get(f32, "actualBoundingBoxDescent"); + const asc = try height_metrics.get(f32, "fontBoundingBoxAscent"); + const desc = try height_metrics.get(f32, "fontBoundingBoxDescent"); const cell_height = asc + desc; const cell_baseline = desc; @@ -325,6 +325,10 @@ pub const Face = struct { return ctx; } + pub fn hasColor(_: *const Face) bool { + return true; + } + pub fn isColorGlyph(self: *const Face, cp: u32) bool { // Render the glyph var render = self.renderGlyphInternal(self.alloc, cp) catch unreachable; @@ -349,6 +353,7 @@ pub const Face = struct { metrics: js.Object, width: u32, height: u32, + y_offset: i32, bitmap: []u8, pub fn deinit(self: *RenderedGlyph) void { @@ -409,6 +414,14 @@ pub const Face = struct { // Height is our ascender + descender for this char const height = if (!broken_bbox) @as(u32, @intFromFloat(@ceil(asc + desc))) + 1 else width; + const ctx_temp = try self.context(); + try ctx_temp.set("textBaseline", js.string("top")); + // Get the width and height of the render + const metrics_2 = try measure_ctx.call(js.Object, "measureText", .{glyph_str}); + const top_desc = try metrics_2.get(f32, "actualBoundingBoxDescent") + 1; + const y_offset = @as(i32, @intFromFloat(top_desc)) - @as(i32, @intCast(height)); + ctx_temp.deinit(); + // Note: width and height both get "+ 1" added to them above. This // is important so that there is a 1px border around the glyph to avoid // any clipping in the atlas. @@ -418,15 +431,15 @@ pub const Face = struct { try self.canvas.set("width", width); try self.canvas.set("height", height); - const width_str = try std.fmt.allocPrint(alloc, "{d}px", .{width}); - defer alloc.free(width_str); - const height_str = try std.fmt.allocPrint(alloc, "{d}px", .{height}); - defer alloc.free(height_str); + // const width_str = try std.fmt.allocPrint(alloc, "{d}px", .{width}); + // defer alloc.free(width_str); + // const height_str = try std.fmt.allocPrint(alloc, "{d}px", .{height}); + // defer alloc.free(height_str); - const style = try self.canvas.get(js.Object, "style"); - defer style.deinit(); - try style.set("width", js.string(width_str)); - try style.set("height", js.string(height_str)); + // const style = try self.canvas.get(js.Object, "style"); + // defer style.deinit(); + // try style.set("width", js.string(width_str)); + // try style.set("height", js.string(height_str)); } // Reload our context since we resized the canvas @@ -501,6 +514,7 @@ pub const Face = struct { .width = width, .height = height, .bitmap = bitmap, + .y_offset = y_offset, }; } }; diff --git a/src/font/shaper/web_canvas.zig b/src/font/shaper/web_canvas.zig index 37bfe70b2..323a587fa 100644 --- a/src/font/shaper/web_canvas.zig +++ b/src/font/shaper/web_canvas.zig @@ -274,10 +274,66 @@ pub const Wasm = struct { log.warn("error during shaper test err={}", .{err}); }; } + const js = @import("zig-js"); + + fn createImageData(self: *font.Atlas) !js.Object { + // We need to draw pixels so this is format dependent. + const buf: []u8 = switch (self.format) { + // RGBA is the native ImageData format + .rgba => self.data, + + .grayscale => buf: { + // Convert from A8 to RGBA so every 4th byte is set to a value. + var buf: []u8 = try alloc.alloc(u8, self.data.len * 4); + errdefer alloc.free(buf); + @memset(buf, 0); + for (self.data, 0..) |value, i| { + buf[(i * 4) + 3] = value; + } + break :buf buf; + }, + + else => return error.UnsupportedAtlasFormat, + }; + defer if (buf.ptr != self.data.ptr) alloc.free(buf); + + // Create an ImageData from our buffer and then write it to the canvas + const image_data: js.Object = data: { + // Get our runtime memory + const mem = try js.runtime.get(js.Object, "memory"); + defer mem.deinit(); + const mem_buf = try mem.get(js.Object, "buffer"); + defer mem_buf.deinit(); + + // Create an array that points to our buffer + const arr = arr: { + const Uint8ClampedArray = try js.global.get(js.Object, "Uint8ClampedArray"); + defer Uint8ClampedArray.deinit(); + const arr = try Uint8ClampedArray.new(.{ mem_buf, buf.ptr, buf.len }); + if (!wasm.shared_mem) break :arr arr; + + // If we're sharing memory then we have to copy the data since + // we can't set ImageData directly using a SharedArrayBuffer. + defer arr.deinit(); + break :arr try arr.call(js.Object, "slice", .{}); + }; + defer arr.deinit(); + + // Create the image data from our array + const ImageData = try js.global.get(js.Object, "ImageData"); + defer ImageData.deinit(); + const data = try ImageData.new(.{ arr, self.size, self.size }); + errdefer data.deinit(); + + break :data data; + }; + + return image_data; + } fn shaper_test_(self: *Shaper, grid: *SharedGrid, str: []const u8) !void { // Make a screen with some data - var term = try terminal.Terminal.init(alloc, .{ .cols = 6, .rows = 5 }); + var term = try terminal.Terminal.init(alloc, .{ .cols = 20, .rows = 5 }); defer term.deinit(alloc); try term.printString(str); @@ -285,21 +341,55 @@ pub const Wasm = struct { var row_it = term.screen.pages.rowIterator(.right_down, .{ .viewport = .{} }, null); var y: usize = 0; + const Render = struct { + render: SharedGrid.Render, + y: usize, + x: usize, + }; + var cell_list: std.ArrayListUnmanaged(Render) = .{}; + defer cell_list.deinit(wasm.alloc); while (row_it.next()) |row| { defer y += 1; var it = self.runIterator(grid, &term.screen, row, null, null); while (try it.next(alloc)) |run| { const cells = try self.shape(run); for (cells) |cell| { - _ = try grid.renderGlyph(wasm.alloc, run.font_index, cell.glyph_index, .{}); + const render = try grid.renderGlyph(wasm.alloc, run.font_index, cell.glyph_index, .{}); + try cell_list.append(wasm.alloc, .{ .render = render, .x = cell.x, .y = y }); + + log.info("y={} x={} width={} height={} ax={} ay={} base={}", .{ + y * grid.metrics.cell_height, + cell.x * grid.metrics.cell_width, + render.glyph.width, + render.glyph.height, + render.glyph.atlas_x, + render.glyph.atlas_y, + grid.metrics.cell_baseline, + }); } - log.info("y={} run={d} shape={any} idx={}", .{ - y, - run.cells, - cells, - run.font_index, - }); } } + const colour_data = try createImageData(&grid.atlas_color); + const gray_data = try createImageData(&grid.atlas_grayscale); + + const doc = try js.global.get(js.Object, "document"); + defer doc.deinit(); + const canvas = try doc.call(js.Object, "getElementById", .{js.string("shaper-canvas")}); + errdefer canvas.deinit(); + const ctx = try canvas.call(js.Object, "getContext", .{js.string("2d")}); + defer ctx.deinit(); + for (cell_list.items) |cell| { + const x_start = -@as(isize, @intCast(cell.render.glyph.atlas_x)); + const y_start = -@as(isize, @intCast(cell.render.glyph.atlas_y)); + try ctx.call(void, "putImageData", .{ + if (cell.render.presentation == .emoji) colour_data else gray_data, + x_start + @as(isize, @intCast(cell.x * grid.metrics.cell_width)) + cell.render.glyph.offset_x, + y_start + @as(isize, @intCast(cell.y * grid.metrics.cell_height)) + cell.render.glyph.offset_y, + cell.render.glyph.atlas_x, + cell.render.glyph.atlas_y, + cell.render.glyph.width, + cell.render.glyph.height, + }); + } } }; diff --git a/src/input/Link.zig b/src/input/Link.zig index adc52a270..eeec2ac71 100644 --- a/src/input/Link.zig +++ b/src/input/Link.zig @@ -6,6 +6,9 @@ const Link = @This(); const oni = @import("oniguruma"); const Mods = @import("key.zig").Mods; +const builtin = @import("builtin"); + +pub const Regex = oni.Regex; /// The regular expression that will be used to match the link. Ownership /// of this memory is up to the caller. The link will never free this memory. diff --git a/src/inspector/main.zig b/src/inspector/main.zig index ee871f200..7b1747c05 100644 --- a/src/inspector/main.zig +++ b/src/inspector/main.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); pub const cell = @import("cell.zig"); pub const cursor = @import("cursor.zig"); pub const key = @import("key.zig"); @@ -6,7 +7,7 @@ pub const page = @import("page.zig"); pub const termio = @import("termio.zig"); pub const Cell = cell.Cell; -pub const Inspector = @import("Inspector.zig"); +pub const Inspector = if (builtin.cpu.arch != .wasm32) @import("Inspector.zig") else struct {}; test { @import("std").testing.refAllDecls(@This()); diff --git a/src/main_wasm.zig b/src/main_wasm.zig index d4b7d63e5..7ff03f30b 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -11,6 +11,59 @@ comptime { _ = @import("App.zig").Wasm; } +const renderer = @import("renderer.zig"); +const Surface = @import("Surface.zig"); +const App = @import("App.zig"); +const apprt = @import("apprt.zig"); + +const wasm = @import("os/wasm.zig"); +const alloc = wasm.alloc; +const cli = @import("cli.zig"); +const Config = @import("config/Config.zig"); + +export fn run(str: [*]const u8, len: usize) void { + run_(str[0..len]) catch |err| { + std.log.err("err: {?}", .{err}); + }; +} +fn run_(str: []const u8) !void { + var config = try Config.default(alloc); + var fbs = std.io.fixedBufferStream(str); + var iter = cli.args.lineIterator(fbs.reader()); + try cli.args.parse(Config, alloc, &config, &iter); + try config.finalize(); + const app = try App.create(alloc); + // Create our runtime app + var app_runtime = try apprt.App.init(app, .{}); + const surface = try alloc.create(Surface); + const apprt_surface = try alloc.create(apprt.Surface); + try surface.init(alloc, &config, app, &app_runtime, apprt_surface); + std.log.err("{}", .{surface.size}); + try surface.renderer.setScreenSize(surface.size); + try surface.renderer_state.terminal.printString( + \\M_hello + ); + surface.renderer_state.terminal.setCursorPos(2, 2); + try surface.renderer_state.terminal.setAttribute(.{ .direct_color_bg = .{ + .r = 240, + .g = 40, + .b = 40, + } }); + try surface.renderer_state.terminal.setAttribute(.{ .direct_color_fg = .{ + .r = 255, + .g = 255, + .b = 255, + } }); + try surface.renderer_state.terminal.printString("hello"); + try surface.renderer.updateFrame(apprt_surface, &surface.renderer_state, false); + try surface.renderer.drawFrame(apprt_surface); + try surface.renderer.updateFrame(apprt_surface, &surface.renderer_state, false); + try surface.renderer.drawFrame(apprt_surface); + + // const webgl = try renderer.OpenGL.init(alloc, .{ .config = try renderer.OpenGL.DerivedConfig.init(alloc, &config) }); + // _ = webgl; +} + pub const std_options: std.Options = .{ // Set our log level. We try to get as much logging as possible but in // ReleaseSmall mode where we're optimizing for space, we elevate the diff --git a/src/os/desktop.zig b/src/os/desktop.zig index eb4ac1a59..a2e5b61c3 100644 --- a/src/os/desktop.zig +++ b/src/os/desktop.zig @@ -56,7 +56,7 @@ pub fn launchedFromDesktop() bool { // iPhone/iPad is always launched from the "desktop" .ios => true, - .freestanding => false, + .freestanding, .wasi => false, else => @compileError("unsupported platform"), }; diff --git a/src/os/hostname.zig b/src/os/hostname.zig index 22f29ceff..0a326c97a 100644 --- a/src/os/hostname.zig +++ b/src/os/hostname.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const posix = std.posix; pub const HostnameParsingError = error{ @@ -57,6 +58,7 @@ pub const LocalHostnameValidationError = error{ Unexpected, }; +const HOST_NAME_MAX = (if (builtin.cpu.arch == .wasm32) 64 else posix.HOST_NAME_MAX); /// Checks if a hostname is local to the current machine. This matches /// both "localhost" and the current hostname of the machine (as returned /// by `gethostname`). @@ -65,7 +67,8 @@ pub fn isLocalHostname(hostname: []const u8) LocalHostnameValidationError!bool { if (std.mem.eql(u8, "localhost", hostname)) return true; // If hostname is not "localhost" it must match our hostname. - var buf: [posix.HOST_NAME_MAX]u8 = undefined; + if (builtin.cpu.arch == .wasm32) return false; + var buf: [HOST_NAME_MAX]u8 = undefined; const ourHostname = try posix.gethostname(&buf); return std.mem.eql(u8, hostname, ourHostname); } @@ -73,7 +76,7 @@ pub fn isLocalHostname(hostname: []const u8) LocalHostnameValidationError!bool { test "bufPrintHostnameFromFileUri succeeds with ascii hostname" { const uri = try std.Uri.parse("file://localhost/"); - var buf: [posix.HOST_NAME_MAX]u8 = undefined; + var buf: [HOST_NAME_MAX]u8 = undefined; const actual = try bufPrintHostnameFromFileUri(&buf, uri); try std.testing.expectEqualStrings("localhost", actual); } @@ -81,7 +84,7 @@ test "bufPrintHostnameFromFileUri succeeds with ascii hostname" { test "bufPrintHostnameFromFileUri succeeds with hostname as mac address" { const uri = try std.Uri.parse("file://12:34:56:78:90:12"); - var buf: [posix.HOST_NAME_MAX]u8 = undefined; + var buf: [HOST_NAME_MAX]u8 = undefined; const actual = try bufPrintHostnameFromFileUri(&buf, uri); try std.testing.expectEqualStrings("12:34:56:78:90:12", actual); } @@ -89,7 +92,7 @@ test "bufPrintHostnameFromFileUri succeeds with hostname as mac address" { test "bufPrintHostnameFromFileUri succeeds with hostname as a mac address and the last section is < 10" { const uri = try std.Uri.parse("file://12:34:56:78:90:05"); - var buf: [posix.HOST_NAME_MAX]u8 = undefined; + var buf: [HOST_NAME_MAX]u8 = undefined; const actual = try bufPrintHostnameFromFileUri(&buf, uri); try std.testing.expectEqualStrings("12:34:56:78:90:05", actual); } @@ -98,21 +101,21 @@ test "bufPrintHostnameFromFileUri returns only hostname when there is a port com // First: try with a non-2-digit port, to test general port handling. const four_port_uri = try std.Uri.parse("file://has-a-port:1234"); - var four_port_buf: [posix.HOST_NAME_MAX]u8 = undefined; + var four_port_buf: [HOST_NAME_MAX]u8 = undefined; const four_port_actual = try bufPrintHostnameFromFileUri(&four_port_buf, four_port_uri); try std.testing.expectEqualStrings("has-a-port", four_port_actual); // Second: try with a 2-digit port to test mac-address handling. const two_port_uri = try std.Uri.parse("file://has-a-port:12"); - var two_port_buf: [posix.HOST_NAME_MAX]u8 = undefined; + var two_port_buf: [HOST_NAME_MAX]u8 = undefined; const two_port_actual = try bufPrintHostnameFromFileUri(&two_port_buf, two_port_uri); try std.testing.expectEqualStrings("has-a-port", two_port_actual); // Third: try with a mac-address that has a port-component added to it to test mac-address handling. const mac_with_port_uri = try std.Uri.parse("file://12:34:56:78:90:12:1234"); - var mac_with_port_buf: [posix.HOST_NAME_MAX]u8 = undefined; + var mac_with_port_buf: [HOST_NAME_MAX]u8 = undefined; const mac_with_port_actual = try bufPrintHostnameFromFileUri(&mac_with_port_buf, mac_with_port_uri); try std.testing.expectEqualStrings("12:34:56:78:90:12", mac_with_port_actual); } @@ -120,7 +123,7 @@ test "bufPrintHostnameFromFileUri returns only hostname when there is a port com test "bufPrintHostnameFromFileUri returns NoHostnameInUri error when hostname is missing from uri" { const uri = try std.Uri.parse("file:///"); - var buf: [posix.HOST_NAME_MAX]u8 = undefined; + var buf: [HOST_NAME_MAX]u8 = undefined; const actual = bufPrintHostnameFromFileUri(&buf, uri); try std.testing.expectError(HostnameParsingError.NoHostnameInUri, actual); } @@ -138,7 +141,7 @@ test "isLocalHostname returns true when provided hostname is localhost" { } test "isLocalHostname returns true when hostname is local" { - var buf: [posix.HOST_NAME_MAX]u8 = undefined; + var buf: [HOST_NAME_MAX]u8 = undefined; const localHostname = try posix.gethostname(&buf); try std.testing.expect(try isLocalHostname(localHostname)); } diff --git a/src/os/main.zig b/src/os/main.zig index d545f3475..afafbd145 100644 --- a/src/os/main.zig +++ b/src/os/main.zig @@ -44,8 +44,16 @@ pub const clickInterval = mouse.clickInterval; pub const open = openpkg.open; pub const pipe = pipepkg.pipe; pub const resourcesDir = resourcesdir.resourcesDir; -pub const Instant = if (builtin.cpu.arch != .wasm32) std.time.Instant else struct { - fn now() !@This() { +pub const Instant = if (true) std.time.Instant else struct { + pub fn now() !@This() { return .{}; } + pub fn order(self: *const Instant, other: Instant) std.math.Order { + _ = self; + _ = other; + return .eq; + } }; +pub fn sleep(nanosecond: u64) void { + _ = nanosecond; +} diff --git a/src/pty.zig b/src/pty.zig index c0d082411..f282073de 100644 --- a/src/pty.zig +++ b/src/pty.zig @@ -18,6 +18,7 @@ pub const winsize = extern struct { pub const Pty = switch (builtin.os.tag) { .windows => WindowsPty, .ios => NullPty, + .freestanding, .wasi => NullPty, else => PosixPty, }; @@ -41,7 +42,7 @@ pub const Mode = packed struct { // a termio that doesn't use a pty. This isn't used in any user-facing // artifacts, this is just a stopgap to get compilation to work on iOS. const NullPty = struct { - pub const Fd = posix.fd_t; + pub const Fd = i32; master: Fd, slave: Fd, diff --git a/src/renderer.zig b/src/renderer.zig index d968ab4df..9c88a0632 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -44,7 +44,7 @@ pub const Impl = enum { ) Impl { if (target.cpu.arch == .wasm32) { return switch (wasm_target) { - .browser => .webgl, + .browser => .opengl, }; } diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 481f5b0db..67a6212db 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -460,6 +460,9 @@ pub fn surfaceInit(surface: *apprt.Surface) !void { // to compile for OpenGL targets but libghostty is strictly // broken for rendering on this platforms. }, + apprt.browser => { + gl.glad.context.init(); + }, } // These are very noisy so this is commented, but easy to uncomment @@ -574,6 +577,10 @@ pub fn threadEnter(self: *const OpenGL, surface: *apprt.Surface) !void { // to compile for OpenGL targets but libghostty is strictly // broken for rendering on this platforms. }, + apprt.browser => { + log.err("context init", .{}); + gl.glad.context.init(); + }, } } @@ -597,6 +604,7 @@ pub fn threadExit(self: *const OpenGL) void { apprt.embedded => { // TODO: see threadEnter }, + apprt.browser => {}, } } @@ -2172,6 +2180,7 @@ fn flushAtlasSingle( internal_format: gl.Texture.InternalFormat, format: gl.Texture.Format, ) !void { + std.log.err("starging flushing atlas", .{}); // If the texture isn't modified we do nothing const new_modified = atlas.modified.load(.monotonic); if (new_modified <= modified.*) return; @@ -2184,6 +2193,7 @@ fn flushAtlasSingle( defer texbind.unbind(); const new_resized = atlas.resized.load(.monotonic); + std.log.err("flushing atlas", .{}); if (new_resized > resized.*) { try texbind.image2D( 0, @@ -2278,6 +2288,7 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void { apprt.glfw => surface.window.swapBuffers(), apprt.gtk => {}, apprt.embedded => {}, + apprt.browser => {}, else => @compileError("unsupported runtime"), } } @@ -2451,8 +2462,10 @@ fn drawCells( gl_state: *const GLState, cells: std.ArrayListUnmanaged(CellProgram.Cell), ) !void { + std.log.err("start drawing cells", .{}); // If we have no cells to render, then we render nothing. if (cells.items.len == 0) return; + std.log.err("have cells", .{}); // Todo: get rid of this completely self.gl_cells_written = 0; @@ -2490,7 +2503,7 @@ fn drawCells( // If we have data to write to the GPU, send it. if (self.gl_cells_written < cells.items.len) { const data = cells.items[self.gl_cells_written..]; - // log.info("sending {} cells to GPU", .{data.len}); + log.info("sending {} cells to GPU", .{data.len}); try bind.vbo.setSubData(self.gl_cells_written * @sizeOf(CellProgram.Cell), data); self.gl_cells_written += data.len; @@ -2526,7 +2539,7 @@ const GLState = struct { const arena_alloc = arena.allocator(); // Load our custom shaders - const custom_state: ?custom.State = custom: { + const custom_state: ?custom.State = if (builtin.cpu.arch == .wasm32) null else custom: { const shaders: []const [:0]const u8 = shadertoy.loadFromFiles( arena_alloc, config.custom_shaders, diff --git a/src/renderer/link.zig b/src/renderer/link.zig index 994190ec8..d2de78b59 100644 --- a/src/renderer/link.zig +++ b/src/renderer/link.zig @@ -1,19 +1,20 @@ const std = @import("std"); const Allocator = std.mem.Allocator; -const oni = @import("oniguruma"); const configpkg = @import("../config.zig"); const inputpkg = @import("../input.zig"); const terminal = @import("../terminal/main.zig"); const point = terminal.point; const Screen = terminal.Screen; const Terminal = terminal.Terminal; +const builtin = @import("builtin"); +const js = @import("zig-js"); const log = std.log.scoped(.renderer_link); /// The link configuration needed for renderers. pub const Link = struct { /// The regular expression to match the link against. - regex: oni.Regex, + regex: inputpkg.Link.Regex, /// The situations in which the link should be highlighted. highlight: inputpkg.Link.Highlight, diff --git a/src/renderer/opengl/custom.zig b/src/renderer/opengl/custom.zig index 2cab0940c..1eb8e6fef 100644 --- a/src/renderer/opengl/custom.zig +++ b/src/renderer/opengl/custom.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const gl = @import("opengl"); const Size = @import("../size.zig").Size; +const internal_os = @import("../../os/main.zig"); const log = std.log.scoped(.opengl_custom); @@ -47,11 +48,11 @@ pub const State = struct { /// The first time a frame was drawn. This is used to update /// the time uniform. - first_frame_time: std.time.Instant, + first_frame_time: internal_os.Instant, /// The last time a frame was drawn. This is used to update /// the time uniform. - last_frame_time: std.time.Instant, + last_frame_time: internal_os.Instant, pub fn init( alloc: Allocator, @@ -139,8 +140,8 @@ pub const State = struct { .vao = vao, .ebo = ebo, .fb_texture = fb_tex, - .first_frame_time = try std.time.Instant.now(), - .last_frame_time = try std.time.Instant.now(), + .first_frame_time = try internal_os.Instant.now(), + .last_frame_time = try internal_os.Instant.now(), }; } @@ -183,7 +184,7 @@ pub const State = struct { /// this. pub fn newFrame(self: *State) !void { // Update our frame time - const now = std.time.Instant.now() catch self.first_frame_time; + const now = internal_os.Instant.now() catch self.first_frame_time; const since_ns: f32 = @floatFromInt(now.since(self.first_frame_time)); const delta_ns: f32 = @floatFromInt(now.since(self.last_frame_time)); self.uniforms.time = since_ns / std.time.ns_per_s; diff --git a/src/renderer/opengl/image.zig b/src/renderer/opengl/image.zig index 85f59f1f3..58bf7681c 100644 --- a/src/renderer/opengl/image.zig +++ b/src/renderer/opengl/image.zig @@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const gl = @import("opengl"); const wuffs = @import("wuffs"); +const internal_os = @import("../../os/main.zig"); /// Represents a single image placement on the grid. A placement is a /// request to render an instance of an image. @@ -34,7 +35,7 @@ pub const Placement = struct { /// The map used for storing images. pub const ImageMap = std.AutoHashMapUnmanaged(u32, struct { image: Image, - transmit_time: std.time.Instant, + transmit_time: internal_os.Instant, }); /// The state for a single image that is to be rendered. The image can be diff --git a/src/renderer/shaders/cell.f.glsl b/src/renderer/shaders/cell.f.glsl index f9c1ce2b1..e9982144b 100644 --- a/src/renderer/shaders/cell.f.glsl +++ b/src/renderer/shaders/cell.f.glsl @@ -1,27 +1,27 @@ -#version 330 core +#version 300 es -in vec2 glyph_tex_coords; +in mediump vec2 glyph_tex_coords; flat in uint mode; // The color for this cell. If this is a background pass this is the // background color. Otherwise, this is the foreground color. -flat in vec4 color; +flat in mediump vec4 color; // The position of the cells top-left corner. -flat in vec2 screen_cell_pos; +flat in mediump vec2 screen_cell_pos; // Position the fragment coordinate to the upper left -layout(origin_upper_left) in vec4 gl_FragCoord; +// layout(origin_upper_left) in vec4 gl_FragCoord; // Must declare this output for some versions of OpenGL. -layout(location = 0) out vec4 out_FragColor; +out mediump vec4 out_FragColor; // Font texture uniform sampler2D text; uniform sampler2D text_color; // Dimensions of the cell -uniform vec2 cell_size; +uniform highp vec2 cell_size; // See vertex shader const uint MODE_BG = 1u; @@ -31,7 +31,7 @@ const uint MODE_FG_COLOR = 7u; const uint MODE_FG_POWERLINE = 15u; void main() { - float a; + highp float a; switch (mode) { case MODE_BG: @@ -42,7 +42,7 @@ void main() { case MODE_FG_CONSTRAINED: case MODE_FG_POWERLINE: a = texture(text, glyph_tex_coords).r; - vec3 premult = color.rgb * color.a; + highp vec3 premult = color.rgb * color.a; out_FragColor = vec4(premult.rgb*a, a); break; diff --git a/src/renderer/shaders/cell.v.glsl b/src/renderer/shaders/cell.v.glsl index 942b7ac44..8d0f4c78f 100644 --- a/src/renderer/shaders/cell.v.glsl +++ b/src/renderer/shaders/cell.v.glsl @@ -1,4 +1,4 @@ -#version 330 core +#version 300 es // These are the possible modes that "mode" can be set to. This is // used to multiplex multiple render modes into a single shader. @@ -11,33 +11,33 @@ const uint MODE_FG_COLOR = 7u; const uint MODE_FG_POWERLINE = 15u; // The grid coordinates (x, y) where x < columns and y < rows -layout (location = 0) in vec2 grid_coord; +in vec2 grid_coord; // Position of the glyph in the texture. -layout (location = 1) in vec2 glyph_pos; +in vec2 glyph_pos; // Width/height of the glyph -layout (location = 2) in vec2 glyph_size; +in vec2 glyph_size; // Offset of the top-left corner of the glyph when rendered in a rect. -layout (location = 3) in vec2 glyph_offset; +in vec2 glyph_offset; // The color for this cell in RGBA (0 to 1.0). Background or foreground // depends on mode. -layout (location = 4) in vec4 color_in; +in vec4 color_in; // Only set for MODE_FG, this is the background color of the FG text. // This is used to detect minimal contrast for the text. -layout (location = 5) in vec4 bg_color_in; +in vec4 bg_color_in; // The mode of this shader. The mode determines what fields are used, // what the output will be, etc. This shader is capable of executing in // multiple "modes" so that we can share some logic and so that we can draw // the entire terminal grid in a single GPU pass. -layout (location = 6) in uint mode_in; +in uint mode_in; // The width in cells of this item. -layout (location = 7) in uint grid_width; +in uint grid_width; // The background or foreground color for the fragment, depending on // whether this is a background or foreground pass. @@ -168,22 +168,22 @@ void main() { // Scaled for wide chars vec2 cell_size_scaled = cell_size; - cell_size_scaled.x = cell_size_scaled.x * grid_width; + cell_size_scaled.x = cell_size_scaled.x * float(grid_width); switch (mode) { case MODE_BG: // If we're at the edge of the grid, we add our padding to the background // to extend it. Note: grid_padding is top/right/bottom/left. - if (grid_coord.y == 0 && padding_vertical_top) { + if (grid_coord.y == 0. && padding_vertical_top) { cell_pos.y -= grid_padding.r; cell_size_scaled.y += grid_padding.r; - } else if (grid_coord.y == grid_size.y - 1 && padding_vertical_bottom) { + } else if (grid_coord.y == grid_size.y - 1. && padding_vertical_bottom) { cell_size_scaled.y += grid_padding.b; } - if (grid_coord.x == 0) { + if (grid_coord.x == 0.) { cell_pos.x -= grid_padding.a; cell_size_scaled.x += grid_padding.a; - } else if (grid_coord.x == grid_size.x - 1) { + } else if (grid_coord.x == grid_size.x - 1.) { cell_size_scaled.x += grid_padding.g; } @@ -214,7 +214,7 @@ void main() { if (mode == MODE_FG_CONSTRAINED || mode == MODE_FG_COLOR) { if (glyph_size.x > cell_size_scaled.x) { float new_y = glyph_size.y * (cell_size_scaled.x / glyph_size.x); - glyph_offset_calc.y = glyph_offset_calc.y + ((glyph_size.y - new_y) / 2); + glyph_offset_calc.y = glyph_offset_calc.y + ((glyph_size.y - new_y) / 2.); glyph_size_calc.y = new_y; glyph_size_calc.x = cell_size_scaled.x; } @@ -238,8 +238,8 @@ void main() { text_size = textureSize(text_color, 0); break; } - vec2 glyph_tex_pos = glyph_pos / text_size; - vec2 glyph_tex_size = glyph_size / text_size; + vec2 glyph_tex_pos = glyph_pos / vec2(text_size); + vec2 glyph_tex_size = glyph_size / vec2(text_size); glyph_tex_coords = glyph_tex_pos + glyph_tex_size * position; // If we have a minimum contrast, we need to check if we need to diff --git a/src/renderer/shaders/custom.v.glsl b/src/renderer/shaders/custom.v.glsl index 653e1800e..4e553ae3f 100644 --- a/src/renderer/shaders/custom.v.glsl +++ b/src/renderer/shaders/custom.v.glsl @@ -1,4 +1,4 @@ -#version 330 core +#version 300 es void main(){ vec2 position; diff --git a/src/renderer/shaders/image.f.glsl b/src/renderer/shaders/image.f.glsl index e8c00b271..4ff7aaf58 100644 --- a/src/renderer/shaders/image.f.glsl +++ b/src/renderer/shaders/image.f.glsl @@ -1,12 +1,12 @@ -#version 330 core +#version 300 es -in vec2 tex_coord; +in mediump vec2 tex_coord; -layout(location = 0) out vec4 out_FragColor; +out mediump vec4 out_FragColor; uniform sampler2D image; void main() { - vec4 color = texture(image, tex_coord); + mediump vec4 color = texture(image, tex_coord); out_FragColor = vec4(color.rgb * color.a, color.a); } diff --git a/src/renderer/shaders/image.v.glsl b/src/renderer/shaders/image.v.glsl index e3d07ca9e..0c7ded6da 100644 --- a/src/renderer/shaders/image.v.glsl +++ b/src/renderer/shaders/image.v.glsl @@ -1,9 +1,9 @@ -#version 330 core +#version 300 es -layout (location = 0) in vec2 grid_pos; -layout (location = 1) in vec2 cell_offset; -layout (location = 2) in vec4 source_rect; -layout (location = 3) in vec2 dest_size; +in vec2 grid_pos; +in vec2 cell_offset; +in vec4 source_rect; +in vec2 dest_size; out vec2 tex_coord; @@ -13,7 +13,7 @@ uniform mat4 projection; void main() { // The size of the image in pixels - vec2 image_size = textureSize(image, 0); + vec2 image_size = vec2(textureSize(image, 0)); // Turn the cell position into a vertex point depending on the // gl_VertexID. Since we use instanced drawing, we have 4 vertices diff --git a/src/terminal/Parser.zig b/src/terminal/Parser.zig index 9aebdbd3a..68e68816f 100644 --- a/src/terminal/Parser.zig +++ b/src/terminal/Parser.zig @@ -328,8 +328,14 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action { } // A numeric value. Add it to our accumulator. - if (self.param_acc_idx > 0) { - self.param_acc *|= 10; + if (builtin.cpu.arch == .wasm32) { + if (self.param_acc_idx > 0) { + self.param_acc *= 10; + } + } else { + if (self.param_acc_idx > 0) { + self.param_acc *|= 10; + } } self.param_acc +|= c - '0'; diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index f461757f3..06d6bdd1f 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -108,7 +108,7 @@ pub const LoadingImage = struct { path: []const u8, ) !void { // windows is currently unsupported, does it support shm? - if (comptime builtin.target.os.tag == .windows) { + if (comptime builtin.target.os.tag == .windows or builtin.cpu.arch == .wasm32) { return error.UnsupportedMedium; } diff --git a/src/terminal/osc.zig b/src/terminal/osc.zig index 34bc46745..0dcbd580b 100644 --- a/src/terminal/osc.zig +++ b/src/terminal/osc.zig @@ -11,6 +11,7 @@ const assert = std.debug.assert; const Allocator = mem.Allocator; const RGB = @import("color.zig").RGB; const kitty = @import("kitty.zig"); +const builtin = @import("builtin"); const log = std.log.scoped(.osc); @@ -871,7 +872,11 @@ pub const Parser = struct { self.complete = true; const idx = self.buf_idx - self.buf_start; - if (idx > 0) self.temp_state.num *|= 10; + if (builtin.cpu.arch == .wasm32) { + if (idx > 0) self.temp_state.num *= 10; + } else { + if (idx > 0) self.temp_state.num *|= 10; + } self.temp_state.num +|= c - '0'; }, ';' => { diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index b8d60a13f..c0452a4e9 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -13,6 +13,7 @@ const osc = @import("osc.zig"); const sgr = @import("sgr.zig"); const UTF8Decoder = @import("UTF8Decoder.zig"); const MouseShape = @import("mouse_shape.zig").MouseShape; +const builtin = @import("builtin"); const log = std.log.scoped(.stream); @@ -242,8 +243,13 @@ pub fn Stream(comptime Handler: type) type { 0x18, 0x1A => self.parser.state = .ground, // A parameter digit: '0'...'9' => if (self.parser.params_idx < 16) { - self.parser.param_acc *|= 10; - self.parser.param_acc +|= c - '0'; + if (builtin.cpu.arch == .wasm32) { + self.parser.param_acc *= 10; + self.parser.param_acc += c - '0'; + } else { + self.parser.param_acc *|= 10; + self.parser.param_acc +|= c - '0'; + } // The parser's CSI param action uses param_acc_idx // to decide if there's a final param that needs to // be consumed or not, but it doesn't matter really diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 3b4e0c6a9..827566dbb 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -345,7 +345,7 @@ fn processExit( // Determine how long the process was running for. const runtime_ms: ?u64 = runtime: { - const process_end = internal_os.now() catch break :runtime null; + const process_end = internal_os.Instant.now() catch break :runtime null; const runtime_ns = process_end.since(execdata.start); const runtime_ms = runtime_ns / std.time.ns_per_ms; break :runtime runtime_ms; @@ -686,11 +686,18 @@ const Subprocess = struct { /// a potential execution on the host. const FlatpakHostCommand = if (build_config.flatpak) internal_os.FlatpakHostCommand else void; - const c = @cImport({ + const c = if (builtin.cpu.arch != .wasm32) @cImport({ @cInclude("errno.h"); @cInclude("signal.h"); @cInclude("unistd.h"); - }); + }) else struct { + pub const pid_t = Command.PidT; + pub const ESRCH = 0x03; + pub fn getpgid(pid: pid_t) pid_t { + _ = pid; + return 100; + } + }; arena: std.heap.ArenaAllocator, cwd: ?[]const u8, @@ -744,7 +751,7 @@ const Subprocess = struct { // Assume that the resources directory is adjacent to the terminfo // database - var buf: [std.fs.max_path_bytes]u8 = undefined; + var buf: [if (builtin.cpu.arch == .wasm32) 4096 else std.fs.max_path_bytes]u8 = undefined; const dir = try std.fmt.bufPrint(&buf, "{s}/terminfo", .{ std.fs.path.dirname(base) orelse unreachable, }); @@ -762,8 +769,8 @@ const Subprocess = struct { // Add our binary to the path if we can find it. ghostty_path: { - var exe_buf: [std.fs.max_path_bytes]u8 = undefined; - const exe_bin_path = std.fs.selfExePath(&exe_buf) catch |err| { + var exe_buf: [if (builtin.cpu.arch == .wasm32) 4096 else std.fs.max_path_bytes]u8 = undefined; + const exe_bin_path = if (builtin.cpu.arch == .wasm32) "ghostty-wasm.wasm" else std.fs.selfExePath(&exe_buf) catch |err| { log.warn("failed to get ghostty exe path err={}", .{err}); break :ghostty_path; }; @@ -848,6 +855,7 @@ const Subprocess = struct { // Setup our shell integration, if we can. const integrated_shell: ?shell_integration.Shell, const shell_command: []const u8 = shell: { + if (builtin.cpu.arch == .wasm32) break :shell .{ null, "" }; const default_shell_command = cfg.command orelse switch (builtin.os.tag) { .windows => "cmd.exe", else => "sh", @@ -1247,6 +1255,7 @@ const Subprocess = struct { _ = try command.wait(false); }, + .freestanding, .wasi => {}, else => if (getpgid(pid)) |pgid| { // It is possible to send a killpg between the time that @@ -1279,7 +1288,7 @@ const Subprocess = struct { const res = posix.waitpid(pid, std.c.W.NOHANG); log.debug("waitpid result={}", .{res.pid}); if (res.pid != 0) break; - std.time.sleep(10 * std.time.ns_per_ms); + internal_os.sleep(10 * std.time.ns_per_ms); } }, } @@ -1302,7 +1311,7 @@ const Subprocess = struct { const pgid = c.getpgid(pid); if (pgid == my_pgid) { log.warn("pgid is our own, retrying", .{}); - std.time.sleep(10 * std.time.ns_per_ms); + internal_os.sleep(10 * std.time.ns_per_ms); continue; } diff --git a/src/termio/Termio.zig b/src/termio/Termio.zig index bdc743949..3228afb6d 100644 --- a/src/termio/Termio.zig +++ b/src/termio/Termio.zig @@ -571,18 +571,23 @@ fn processOutputLocked(self: *Termio, buf: []const u8) void { // process a byte at a time alternating between the inspector handler // and the termio handler. This is very slow compared to our optimizations // below but at least users only pay for it if they're using the inspector. - if (self.renderer_state.inspector) |insp| { - for (buf, 0..) |byte, i| { - insp.recordPtyRead(buf[i .. i + 1]) catch |err| { - log.err("error recording pty read in inspector err={}", .{err}); - }; - - self.terminal_stream.next(byte) catch |err| - log.err("error processing terminal data: {}", .{err}); - } - } else { + if (builtin.cpu.arch == .wasm32) { self.terminal_stream.nextSlice(buf) catch |err| log.err("error processing terminal data: {}", .{err}); + } else { + if (self.renderer_state.inspector) |insp| { + for (buf, 0..) |byte, i| { + insp.recordPtyRead(buf[i .. i + 1]) catch |err| { + log.err("error recording pty read in inspector err={}", .{err}); + }; + + self.terminal_stream.next(byte) catch |err| + log.err("error processing terminal data: {}", .{err}); + } + } else { + self.terminal_stream.nextSlice(buf) catch |err| + log.err("error processing terminal data: {}", .{err}); + } } // If our stream handling caused messages to be sent to the mailbox diff --git a/src/termio/stream_handler.zig b/src/termio/stream_handler.zig index 37d176de3..217df1132 100644 --- a/src/termio/stream_handler.zig +++ b/src/termio/stream_handler.zig @@ -1096,7 +1096,7 @@ pub const StreamHandler = struct { // See https://www.rfc-editor.org/rfc/rfc793#section-3.1. const PORT_NUMBER_MAX_DIGITS = 5; // Make sure there is space for a max length hostname + the max number of digits. - var host_and_port_buf: [posix.HOST_NAME_MAX + PORT_NUMBER_MAX_DIGITS]u8 = undefined; + var host_and_port_buf: [(if (builtin.cpu.arch == .wasm32) 64 else posix.HOST_NAME_MAX) + PORT_NUMBER_MAX_DIGITS]u8 = undefined; const hostname_from_uri = internal_os.hostname.bufPrintHostnameFromFileUri( &host_and_port_buf, uri, From 7640e459e78b936b741240037b1ec496d534179a Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Fri, 22 Nov 2024 17:11:25 -0500 Subject: [PATCH 03/10] fix y_offset --- example/app.ts | 2 +- src/font/face/web_canvas.zig | 2 +- src/main_wasm.zig | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example/app.ts b/example/app.ts index 2199e858f..d7a749b7c 100644 --- a/example/app.ts +++ b/example/app.ts @@ -57,7 +57,7 @@ fetch(url.href) }; // Create our config - const config_str = makeStr("font-family = monospace"); + const config_str = makeStr("font-family = monospace\nfont-size 32\n"); old(results); run(config_str.ptr, config_str.len); }) diff --git a/src/font/face/web_canvas.zig b/src/font/face/web_canvas.zig index 8b04fe947..163e45f34 100644 --- a/src/font/face/web_canvas.zig +++ b/src/font/face/web_canvas.zig @@ -419,7 +419,7 @@ pub const Face = struct { // Get the width and height of the render const metrics_2 = try measure_ctx.call(js.Object, "measureText", .{glyph_str}); const top_desc = try metrics_2.get(f32, "actualBoundingBoxDescent") + 1; - const y_offset = @as(i32, @intFromFloat(top_desc)) - @as(i32, @intCast(height)); + const y_offset = @as(i32, @intCast(height)) - @as(i32, @intFromFloat(top_desc)); ctx_temp.deinit(); // Note: width and height both get "+ 1" added to them above. This diff --git a/src/main_wasm.zig b/src/main_wasm.zig index 7ff03f30b..3f4006eb4 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -32,6 +32,8 @@ fn run_(str: []const u8) !void { var iter = cli.args.lineIterator(fbs.reader()); try cli.args.parse(Config, alloc, &config, &iter); try config.finalize(); + std.log.err("font-size {}", .{config.@"font-size"}); + config.@"font-size" = 32; const app = try App.create(alloc); // Create our runtime app var app_runtime = try apprt.App.init(app, .{}); @@ -40,10 +42,8 @@ fn run_(str: []const u8) !void { try surface.init(alloc, &config, app, &app_runtime, apprt_surface); std.log.err("{}", .{surface.size}); try surface.renderer.setScreenSize(surface.size); - try surface.renderer_state.terminal.printString( - \\M_hello - ); - surface.renderer_state.terminal.setCursorPos(2, 2); + try surface.renderer_state.terminal.printString("M_yhelloaaaaaaaaa\n🐏\nπŸ‘πŸ½\nM_ghostty"); + surface.renderer_state.terminal.setCursorPos(4, 2); try surface.renderer_state.terminal.setAttribute(.{ .direct_color_bg = .{ .r = 240, .g = 40, From 133520e067f050bb74658de5ab4a486d26c607ad Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Fri, 22 Nov 2024 17:40:57 -0500 Subject: [PATCH 04/10] render text to correct place this should probably be fixed somewhere in the font metrics but i am not sure how --- src/main_wasm.zig | 28 +++++++++++++++------------- src/renderer/shaders/cell.v.glsl | 2 +- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main_wasm.zig b/src/main_wasm.zig index 3f4006eb4..a45b1a0e4 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -42,19 +42,21 @@ fn run_(str: []const u8) !void { try surface.init(alloc, &config, app, &app_runtime, apprt_surface); std.log.err("{}", .{surface.size}); try surface.renderer.setScreenSize(surface.size); - try surface.renderer_state.terminal.printString("M_yhelloaaaaaaaaa\n🐏\nπŸ‘πŸ½\nM_ghostty"); - surface.renderer_state.terminal.setCursorPos(4, 2); - try surface.renderer_state.terminal.setAttribute(.{ .direct_color_bg = .{ - .r = 240, - .g = 40, - .b = 40, - } }); - try surface.renderer_state.terminal.setAttribute(.{ .direct_color_fg = .{ - .r = 255, - .g = 255, - .b = 255, - } }); - try surface.renderer_state.terminal.printString("hello"); + const esc = "\x1b["; + surface.io.processOutput("M_yhelloaaaaaaaaa\n\r🐏\n\rπŸ‘πŸ½\n\rM_ghostty" ++ esc ++ "2;2H" ++ esc ++ "48;2;240;40;40m" ++ esc ++ "38;2;23;255;80mhello"); + // try surface.renderer_state.terminal.printString("M_yhelloaaaaaaaaa\n🐏\nπŸ‘πŸ½\nM_ghostty"); + // surface.renderer_state.terminal.setCursorPos(4, 2); + // try surface.renderer_state.terminal.setAttribute(.{ .direct_color_bg = .{ + // .r = 240, + // .g = 40, + // .b = 40, + // } }); + // try surface.renderer_state.terminal.setAttribute(.{ .direct_color_fg = .{ + // .r = 255, + // .g = 255, + // .b = 255, + // } }); + // try surface.renderer_state.terminal.printString("hello"); try surface.renderer.updateFrame(apprt_surface, &surface.renderer_state, false); try surface.renderer.drawFrame(apprt_surface); try surface.renderer.updateFrame(apprt_surface, &surface.renderer_state, false); diff --git a/src/renderer/shaders/cell.v.glsl b/src/renderer/shaders/cell.v.glsl index 8d0f4c78f..3b68eb3ed 100644 --- a/src/renderer/shaders/cell.v.glsl +++ b/src/renderer/shaders/cell.v.glsl @@ -205,7 +205,7 @@ void main() { // The glyph_offset.y is the y bearing, a y value that when added // to the baseline is the offset (+y is up). Our grid goes down. // So we flip it with `cell_size.y - glyph_offset.y`. - glyph_offset_calc.y = cell_size_scaled.y - glyph_offset_calc.y; + glyph_offset_calc.y = -glyph_offset_calc.y; // If this is a constrained mode, we need to constrain it! // We also always constrain colored glyphs since we should have From fa24e0482ba6840c3d46f30834883e9e64fa5119 Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Sat, 23 Nov 2024 06:25:48 -0500 Subject: [PATCH 05/10] compiling again doesnt change font size though --- src/apprt/browser.zig | 3 ++- src/config/Config.zig | 1 + src/font/shaper/web_canvas.zig | 4 ++-- src/main_wasm.zig | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/apprt/browser.zig b/src/apprt/browser.zig index cecea4ef4..f7a290672 100644 --- a/src/apprt/browser.zig +++ b/src/apprt/browser.zig @@ -67,7 +67,8 @@ pub const App = struct { .renderer_health, .color_change, .pwd, - .config_change_conditional_state, + .reload_config, + .config_change, => log.info("unimplemented action={}", .{action}), } } diff --git a/src/config/Config.zig b/src/config/Config.zig index 319504088..eaacdb69f 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -2638,6 +2638,7 @@ pub fn changeConditionalState( /// Expand the relative paths in config-files to be absolute paths /// relative to the base directory. fn expandPaths(self: *Config, base: []const u8) !void { + if (builtin.cpu.arch == .wasm32) return error.WasmCannotExpandPaths; const arena_alloc = self._arena.?.allocator(); // Keep track of this step for replays diff --git a/src/font/shaper/web_canvas.zig b/src/font/shaper/web_canvas.zig index 323a587fa..11b304da7 100644 --- a/src/font/shaper/web_canvas.zig +++ b/src/font/shaper/web_canvas.zig @@ -383,8 +383,8 @@ pub const Wasm = struct { const y_start = -@as(isize, @intCast(cell.render.glyph.atlas_y)); try ctx.call(void, "putImageData", .{ if (cell.render.presentation == .emoji) colour_data else gray_data, - x_start + @as(isize, @intCast(cell.x * grid.metrics.cell_width)) + cell.render.glyph.offset_x, - y_start + @as(isize, @intCast(cell.y * grid.metrics.cell_height)) + cell.render.glyph.offset_y, + x_start + @as(isize, @intCast(cell.x * grid.metrics.cell_width)) - cell.render.glyph.offset_x, + y_start + @as(isize, @intCast(cell.y * grid.metrics.cell_height)) - cell.render.glyph.offset_y, cell.render.glyph.atlas_x, cell.render.glyph.atlas_y, cell.render.glyph.width, diff --git a/src/main_wasm.zig b/src/main_wasm.zig index a45b1a0e4..6a890a157 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -34,12 +34,14 @@ fn run_(str: []const u8) !void { try config.finalize(); std.log.err("font-size {}", .{config.@"font-size"}); config.@"font-size" = 32; + std.log.err("font-size {}", .{config.@"font-size"}); const app = try App.create(alloc); // Create our runtime app var app_runtime = try apprt.App.init(app, .{}); const surface = try alloc.create(Surface); const apprt_surface = try alloc.create(apprt.Surface); try surface.init(alloc, &config, app, &app_runtime, apprt_surface); + try surface.updateConfig(&config); std.log.err("{}", .{surface.size}); try surface.renderer.setScreenSize(surface.size); const esc = "\x1b["; From b90eef78615e4a18f41c0d8ecbb97587467e60ed Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Sat, 23 Nov 2024 13:41:41 -0500 Subject: [PATCH 06/10] fix font loading --- example/app.ts | 2 +- src/main_wasm.zig | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/example/app.ts b/example/app.ts index d7a749b7c..1168a3162 100644 --- a/example/app.ts +++ b/example/app.ts @@ -57,7 +57,7 @@ fetch(url.href) }; // Create our config - const config_str = makeStr("font-family = monospace\nfont-size 32\n"); + const config_str = makeStr("font-family = monospace\nfont-size = 32\n"); old(results); run(config_str.ptr, config_str.len); }) diff --git a/src/main_wasm.zig b/src/main_wasm.zig index 6a890a157..f51b201db 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -30,18 +30,15 @@ fn run_(str: []const u8) !void { var config = try Config.default(alloc); var fbs = std.io.fixedBufferStream(str); var iter = cli.args.lineIterator(fbs.reader()); - try cli.args.parse(Config, alloc, &config, &iter); + try config.loadIter(alloc, &iter); try config.finalize(); std.log.err("font-size {}", .{config.@"font-size"}); - config.@"font-size" = 32; - std.log.err("font-size {}", .{config.@"font-size"}); const app = try App.create(alloc); // Create our runtime app var app_runtime = try apprt.App.init(app, .{}); const surface = try alloc.create(Surface); const apprt_surface = try alloc.create(apprt.Surface); try surface.init(alloc, &config, app, &app_runtime, apprt_surface); - try surface.updateConfig(&config); std.log.err("{}", .{surface.size}); try surface.renderer.setScreenSize(surface.size); const esc = "\x1b["; From 7d7e5e8196e7ebfa1f210ddd82e04010591ea90c Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Sun, 24 Nov 2024 00:26:42 -0500 Subject: [PATCH 07/10] working using seperate threads --- example/app.ts | 42 +++- example/imports.ts | 461 +++++++++++++++++++++++++++++++++-- example/wasi.ts | 431 ++++++++++++++++++++++++++++++++ example/worker.ts | 13 +- src/Command.zig | 43 +--- src/font/face/web_canvas.zig | 58 ++--- src/main_wasm.zig | 51 ++-- src/renderer/OpenGL.zig | 7 +- src/renderer/Thread.zig | 3 +- src/termio/Exec.zig | 3 +- src/termio/Termio.zig | 2 + 11 files changed, 989 insertions(+), 125 deletions(-) create mode 100644 example/wasi.ts diff --git a/example/app.ts b/example/app.ts index 1168a3162..387502218 100644 --- a/example/app.ts +++ b/example/app.ts @@ -1,11 +1,11 @@ -import { importObject, zjs } from "./imports"; +import { importObject, setFiles, setStdin, setWasmModule, zjs } from "./imports"; import { old } from "./old"; const url = new URL("ghostty-wasm.wasm", import.meta.url); fetch(url.href) .then((response) => response.arrayBuffer()) .then((bytes) => WebAssembly.instantiate(bytes, importObject)) - .then((results) => { + .then(async (results) => { const memory = importObject.env.memory; const { atlas_clear, @@ -39,6 +39,7 @@ fetch(url.href) shared_grid_index_for_codepoint, shared_grid_render_glyph, run, + draw, } = results.instance.exports; // Give us access to the zjs value for debugging. @@ -59,5 +60,42 @@ fetch(url.href) // Create our config const config_str = makeStr("font-family = monospace\nfont-size = 32\n"); old(results); + setWasmModule(results.module); + const stdin = new SharedArrayBuffer(1024); + const files = { + nextFd: new SharedArrayBuffer(4), + polling: new SharedArrayBuffer(4), + has: new SharedArrayBuffer(1024), + } + new Int32Array(files.nextFd)[0] = 4; + setFiles(files); + setStdin(stdin); run(config_str.ptr, config_str.len); + await new Promise((resolve) => setTimeout(resolve, 500)) + const io = new Uint8ClampedArray(stdin, 4); + const text = new TextEncoder().encode("hello world\n\r"); + io.set(text); + const n = new Int32Array(stdin); + console.error("storing"); + Atomics.store(n, 0, text.length) + Atomics.notify(n, 0); + console.error("done storing"); + function drawing() { + setTimeout(() => { + draw(); + drawing(); + }, 100); + + } + drawing() + setTimeout(() => { + const io = new Uint8ClampedArray(stdin, 4); + const text = new TextEncoder().encode("🐏\n\rπŸ‘πŸ½\n\rM_ghostty\033[2;2H\033[48;2;240;40;40m\033[38;2;23;255;80mhello"); + io.set(text); + const n = new Int32Array(stdin); + console.error("storing"); + Atomics.store(n, 0, text.length) + Atomics.notify(n, 0); + console.error("done storing"); + }, 5000) }) diff --git a/example/imports.ts b/example/imports.ts index 78745dd8e..ca60ef7d3 100644 --- a/example/imports.ts +++ b/example/imports.ts @@ -1,13 +1,142 @@ import { ZigJS } from "zig-js/src/index.ts"; - +import { + WASI_ESUCCESS, + WASI_EBADF, + WASI_EINVAL, + WASI_ENOSYS, + WASI_EPERM, + //WASI_ENOTCAPABLE, + WASI_FILETYPE_UNKNOWN, + WASI_FILETYPE_BLOCK_DEVICE, + WASI_FILETYPE_CHARACTER_DEVICE, + WASI_FILETYPE_DIRECTORY, + WASI_FILETYPE_REGULAR_FILE, + WASI_FILETYPE_SOCKET_STREAM, + WASI_FILETYPE_SYMBOLIC_LINK, + WASI_FILETYPE, + WASI_FDFLAG_APPEND, + WASI_FDFLAG_DSYNC, + WASI_FDFLAG_NONBLOCK, + WASI_FDFLAG_RSYNC, + WASI_FDFLAG_SYNC, + WASI_RIGHT_FD_DATASYNC, + WASI_RIGHT_FD_READ, + WASI_RIGHT_FD_SEEK, + WASI_RIGHT_FD_FDSTAT_SET_FLAGS, + WASI_RIGHT_FD_SYNC, + WASI_RIGHT_FD_TELL, + WASI_RIGHT_FD_WRITE, + WASI_RIGHT_FD_ADVISE, + WASI_RIGHT_FD_ALLOCATE, + WASI_RIGHT_PATH_CREATE_DIRECTORY, + WASI_RIGHT_PATH_CREATE_FILE, + WASI_RIGHT_PATH_LINK_SOURCE, + WASI_RIGHT_PATH_LINK_TARGET, + WASI_RIGHT_PATH_OPEN, + WASI_RIGHT_FD_READDIR, + WASI_RIGHT_PATH_READLINK, + WASI_RIGHT_PATH_RENAME_SOURCE, + WASI_RIGHT_PATH_RENAME_TARGET, + WASI_RIGHT_PATH_FILESTAT_GET, + WASI_RIGHT_PATH_FILESTAT_SET_SIZE, + WASI_RIGHT_PATH_FILESTAT_SET_TIMES, + WASI_RIGHT_FD_FILESTAT_GET, + WASI_RIGHT_FD_FILESTAT_SET_SIZE, + WASI_RIGHT_FD_FILESTAT_SET_TIMES, + WASI_RIGHT_PATH_SYMLINK, + WASI_RIGHT_PATH_REMOVE_DIRECTORY, + WASI_RIGHT_POLL_FD_READWRITE, + WASI_RIGHT_PATH_UNLINK_FILE, + RIGHTS_BLOCK_DEVICE_BASE, + RIGHTS_BLOCK_DEVICE_INHERITING, + RIGHTS_CHARACTER_DEVICE_BASE, + RIGHTS_CHARACTER_DEVICE_INHERITING, + RIGHTS_REGULAR_FILE_BASE, + RIGHTS_REGULAR_FILE_INHERITING, + RIGHTS_DIRECTORY_BASE, + RIGHTS_DIRECTORY_INHERITING, + RIGHTS_SOCKET_BASE, + RIGHTS_SOCKET_INHERITING, + RIGHTS_TTY_BASE, + RIGHTS_TTY_INHERITING, + WASI_CLOCK_MONOTONIC, + WASI_CLOCK_PROCESS_CPUTIME_ID, + WASI_CLOCK_REALTIME, + WASI_CLOCK_THREAD_CPUTIME_ID, + WASI_EVENTTYPE_CLOCK, + WASI_EVENTTYPE_FD_READ, + WASI_EVENTTYPE_FD_WRITE, + WASI_FILESTAT_SET_ATIM, + WASI_FILESTAT_SET_ATIM_NOW, + WASI_FILESTAT_SET_MTIM, + WASI_FILESTAT_SET_MTIM_NOW, + WASI_O_CREAT, + WASI_O_DIRECTORY, + WASI_O_EXCL, + WASI_O_TRUNC, + WASI_PREOPENTYPE_DIR, + WASI_STDIN_FILENO, + WASI_STDOUT_FILENO, + WASI_STDERR_FILENO, + ERROR_MAP, + SIGNAL_MAP, + WASI_WHENCE_CUR, + WASI_WHENCE_END, + WASI_WHENCE_SET, +} from "./wasi"; const textDecoder = new TextDecoder("utf-8"); +let stdin = new SharedArrayBuffer(1024); +export function setStdin(buf: SharedArrayBuffer) { + stdin = buf; +} +let bytes: null | Uint8ClampedArray = null +function perfNow() { + return performance.now() + performance.timeOrigin; +} +function readStdin() { + const len = new Int32Array(stdin); + if (len[0] == 0) { + Atomics.wait(len, 0, 0, 1000); + } + const length = len[0]; + console.error("stdin", length); + if (length === 0) { + bytes = null; + return; + } + bytes = new Uint8ClampedArray(stdin, 4, length).slice(); + console.log(textDecoder.decode(bytes)); + Atomics.store(len, 0, 0); + Atomics.notify(len, 0); +} +function sleep(ms: number) { + const buf = new SharedArrayBuffer(4); + const view = new Int32Array(buf); + view[0] = 1; + Atomics.wait(view, 0, 1, ms); + +} +let wasmModule; +export function setWasmModule(mod) { + wasmModule = mod; +} +let mainThread = true; +export function setMainThread(isMain) { + mainThread = isMain; +} let gl: WebGL2RenderingContext; export function setGl(l) { gl = l; } export const zjs = new ZigJS(); +globalThis.fontCanvas = new OffscreenCanvas(0, 0); +// window.fontContext = () => { +// const ctx = fontCanvas.getContext("2d"); +// ctx.willReadFrequently = true; +// return ctx; +// } try { const $webgl = document.getElementById("main-canvas"); let webgl2Supported = typeof WebGL2RenderingContext !== "undefined"; @@ -28,7 +157,7 @@ try { throw new Error("The browser supports WebGL2, but initialization failed."); } } -} catch (e){console.error(e) } +} catch (e) { console.error(e) } // OpenGL operates on numeric IDs while WebGL on objects. The following is a // hack made to allow keeping current API on the native side while resolving IDs @@ -230,7 +359,7 @@ const glBufferData = (type, count, pointer, drawType) => { // The Float32Array multiplies by size of float which is 4, and the call to // this method, due to OpenGL compatibility, also receives already // pre-multiplied value. - gl.bufferData(type, zjs.memory.buffer.slice(pointer, pointer+count), drawType); + gl.bufferData(type, zjs.memory.buffer.slice(pointer, pointer + count), drawType); }; const glBufferSubData = (target, offset, size, data) => { @@ -506,6 +635,39 @@ const webgl = { glDrawElementsInstanced, glBindBufferBase, } +const nsToMs = (ns: number | bigint) => { + if (typeof ns === "number") { + ns = Math.trunc(ns); + } + const nsInt = BigInt(ns); + return Number(nsInt / BigInt(1000000)); +}; +const msToNs = (ms: number) => { + const msInt = Math.trunc(ms); + const decimal = BigInt(Math.round((ms - msInt) * 1000000)); + const ns = BigInt(msInt) * BigInt(1000000); + return ns + decimal; +}; +const CPUTIME_START = msToNs(perfNow()); +const now = (clockId?: number) => { + switch (clockId) { + case WASI_CLOCK_MONOTONIC: + return msToNs(perfNow()); + case WASI_CLOCK_REALTIME: + return msToNs(Date.now()); + case WASI_CLOCK_PROCESS_CPUTIME_ID: + case WASI_CLOCK_THREAD_CPUTIME_ID: // TODO -- this assumes 1 thread + return msToNs(perfNow()) - CPUTIME_START; + default: + return null; + } +}; +let files: { polling: SharedArrayBuffer, nextFd: SharedArrayBuffer, has: SharedArrayBuffer }; +export function setFiles(file) { + files = file; +} +const fdStart = 4; + export const importObject = { module: {}, env: { @@ -521,6 +683,11 @@ export const importObject = { const str = textDecoder.decode(data); console.error(str); }, + eventFd() { + const next = new Int32Array(files.nextFd); + return Atomics.add(next, 0, 1); + + }, fork: (...params) => { console.error("fork", params); }, @@ -555,17 +722,32 @@ export const importObject = { }, wasi_snapshot_preview1: { fd_write: (fd, iovs, iovs_len, nwritten_ptr) => { - const memory = new DataView(zjs.memory.buffer); - let buf = ""; - let nwritten = 0; - for (let offset = iovs; offset < iovs + iovs_len * 8; offset += 8) { - const iov_base = memory.getUint32(offset, true); - const iov_len = memory.getUint32(offset + 4, true); - buf += textDecoder.decode(new Uint8ClampedArray(memory.buffer.slice(iov_base, iov_base + iov_len)).slice()); - nwritten += iov_len; + if (fd >= fdStart) { + const memory = new DataView(zjs.memory.buffer); + let nwritten = 0; + for (let offset = iovs; offset < iovs + iovs_len * 8; offset += 8) { + const iov_len = memory.getUint32(offset + 4, true); + nwritten += iov_len; + } + const has = new Int32Array(files.has); + Atomics.store(has, fd - fdStart, 1); + Atomics.notify(has, fd - fdStart); + console.error("notify", fd); + memory.setUint32(nwritten_ptr, nwritten, true); + + } else { + const memory = new DataView(zjs.memory.buffer); + let buf = ""; + let nwritten = 0; + for (let offset = iovs; offset < iovs + iovs_len * 8; offset += 8) { + const iov_base = memory.getUint32(offset, true); + const iov_len = memory.getUint32(offset + 4, true); + buf += textDecoder.decode(new Uint8ClampedArray(memory.buffer.slice(iov_base, iov_base + iov_len)).slice()); + nwritten += iov_len; + } + memory.setUint32(nwritten_ptr, nwritten, true); + console.error(buf); } - memory.setUint32(nwritten_ptr, nwritten, true); - console.error(buf); }, fd_close: (...params) => { console.error("fd_close", params); @@ -591,8 +773,38 @@ export const importObject = { fd_pwrite: (...params) => { console.error("fd_pwrite", params); }, - fd_read: (...params) => { - console.error("fd_read", params); + fd_read(fd, iovs, iovsLen, nreadPtr) { + if (fd >= fdStart) { + const memory = new DataView(zjs.memory.buffer); + let nwritten = 0; + for (let offset = iovs; offset < iovs + iovsLen * 8; offset += 8) { + const iov_base = memory.getUint32(offset, true); + const iov_len = memory.getUint32(offset + 4, true); + // new Uint8ClampedArray(memory.buffer.slice(iov_base, iov_base + iov_len)).fill(1); + nwritten += iov_len; + memory.setUint32(nreadPtr, nwritten, true); + } + } else { + const memory = new DataView(zjs.memory.buffer); + let nwritten = 0; + for (let offset = iovs; offset < iovs + iovsLen * 8; offset += 8) { + if (bytes == null) readStdin(); + if (bytes == null) break; + const iov_base = memory.getUint32(offset, true); + const iov_len = memory.getUint32(offset + 4, true); + const read = Math.min(iov_len, bytes.length); + const io = new Uint8ClampedArray(zjs.memory.buffer, iov_base, iov_len); + io.set(bytes.slice(0, read)); + bytes = bytes.slice(read); + if (bytes.length === 0) bytes = null; + nwritten += read; + if (read !== iov_len) break; + } + + memory.setUint32(nreadPtr, nwritten, true); + if (nwritten > 0) + console.error("fd_read", nwritten); + } }, fd_seek: (...params) => { console.error("fd_seek", params); @@ -606,7 +818,207 @@ export const importObject = { path_unlink_file: (...params) => { console.error("path_unlink_file", params); }, - poll_oneoff: (...params) => { + poll_oneoff( + sin: number, + sout: number, + nsubscriptions: number, + neventsPtr: number + ) { + + let nevents = 0; + let name = ""; + + // May have to wait this long (this gets computed below in the WASI_EVENTTYPE_CLOCK case). + + let waitTimeNs = BigInt(0); + + let fd = -1; + let fd_type: "read" | "write" = "read"; + let fd_timeout_ms = 0; + + const startNs = BigInt(msToNs(perfNow())); + let view = new DataView(zjs.memory.buffer); + let last_sin = sin; + for (let i = 0; i < nsubscriptions; i += 1) { + const userdata = view.getBigUint64(sin, true); + sin += 8; + const type = view.getUint8(sin); + sin += 1; + sin += 7; // padding + if (type == WASI_EVENTTYPE_CLOCK) { + name = "poll_oneoff (type=WASI_EVENTTYPE_CLOCK): "; + } else if (type == WASI_EVENTTYPE_FD_READ) { + name = "poll_oneoff (type=WASI_EVENTTYPE_FD_READ): "; + } else { + name = "poll_oneoff (type=WASI_EVENTTYPE_FD_WRITE): "; + } + console.log(name); + switch (type) { + case WASI_EVENTTYPE_CLOCK: { + // see packages/zig/dist/lib/libc/include/wasm-wasi-musl/wasi/api.h + // for exactly how these values are encoded. I carefully looked + // at that header and **this is definitely right**. Same with the fd + // in the other case below. + const clockid = view.getUint32(sin, true); + sin += 4; + sin += 4; // padding + let timeout = view.getBigUint64(sin, true); + console.log(timeout); + sin += 8; + // const precision = view.getBigUint64(sin, true); + sin += 8; + const subclockflags = view.getUint16(sin, true); + sin += 2; + sin += 6; // padding + + const absolute = subclockflags === 1; + console.log(name, { clockid, timeout, absolute }); + if (!absolute) { + fd_timeout_ms = Number(timeout / BigInt(1000000)); + } + + let e = WASI_ESUCCESS; + const t = now(clockid); + // logToFile(t, clockid, timeout, subclockflags, absolute); + if (t == null) { + e = WASI_EINVAL; + } else { + const end = absolute ? timeout : t + timeout; + const waitNs = end - t; + if (waitNs > waitTimeNs) { + waitTimeNs = waitNs; + } + } + + view = new DataView(zjs.memory.buffer); + view.setBigUint64(sout, userdata, true); + sout += 8; + view.setUint16(sout, e, true); // error + sout += 8; // pad offset 2 + view.setUint8(sout, WASI_EVENTTYPE_CLOCK); + sout += 8; // pad offset 1 + sout += 8; // padding to 8 + + nevents += 1; + + break; + } + case WASI_EVENTTYPE_FD_READ: + case WASI_EVENTTYPE_FD_WRITE: { + /* + Look at + lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c + to see how poll_oneoff is actually used by wasi to implement pselect. + It's also used in + lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/poll/poll.c + + "If none of the selected descriptors are ready for the + requested operation, the pselect() or select() function shall + block until at least one of the requested operations becomes + ready, until the timeout occurs, or until interrupted by a signal." + Thus what is supposed to happen below is supposed + to block until the fd is ready to read from or write + to, etc. + + For now at least if reading from stdin then we block for a short amount + of time if getStdin defined; otherwise, we at least *pause* for a moment + (to avoid cpu burn) if this.sleep is available. + */ + fd = view.getUint32(sin, true); + fd_type = type == WASI_EVENTTYPE_FD_READ ? "read" : "write"; + sin += 4; + console.log(name, "fd =", fd); + sin += 28; + let notify = true; + if (fd >= fdStart) { + const has = new Int32Array(files.has); + Atomics.wait(has, fd - fdStart, 0, 500); + if (has[fd - fdStart] == 0) { + console.warn("not notify"); + notify = false; + } else { + console.warn("notify"); + notify = true; + Atomics.store(has, fd - fdStart, 0) + } + } + + if (notify) { + view = new DataView(zjs.memory.buffer); + view.setBigUint64(sout, userdata, true); + sout += 8; + view.setUint16(sout, WASI_ENOSYS, true); // error + sout += 8; // pad offset 2 + view.setUint8(sout, type); + sout += 8; // pad offset 3 + sout += 8; // padding to 8 + + nevents += 1; + } + /* + TODO: for now for stdin we are just doing a dumb hack. + + We just do something really naive, which is "pause for a little while". + It seems to work for every application I have so far, from Python to + to ncurses, etc. This also makes it easy to have non-blocking sleep + in node.js at the terminal without a worker thread, which is very nice! + + Before I had it block here via getStdin when available, but that does not work + in general; in particular, it breaks ncurses completely. In + ncurses/tty/tty_update.c + the following call is assumed not to block, and if it does, then ncurses + interaction becomes totally broken: + + select(SP_PARM->_checkfd + 1, &fdset, NULL, NULL, &ktimeout) + + */ + if (fd == WASI_STDIN_FILENO && WASI_EVENTTYPE_FD_READ == type) { + sleep(5000); + } + + break; + } + default: + return WASI_EINVAL; + } + + // Consistency check that we consumed exactly the right amount + // of the __wasi_subscription_t. See zig/lib/libc/include/wasm-wasi-musl/wasi/api.h + if (sin - last_sin != 48) { + console.warn("*** BUG in wasi-js in poll_oneoff ", { + i, + sin, + last_sin, + diff: sin - last_sin, + }); + } + last_sin = sin; + } + + view = new DataView(zjs.memory.buffer); + view.setUint32(neventsPtr, nevents, true); + + // if (nevents == 2 && fd >= 0) { + // const r = this.wasiImport.sock_pollSocket(fd, fd_type, fd_timeout_ms); + // if (r != WASI_ENOSYS) { + // // special implementation from outside + // return r; + // } + // // fall back to below + // } + + // Account for the time it took to do everything above, which + // can be arbitrarily long: + if (waitTimeNs > 0) { + waitTimeNs -= msToNs(perfNow()) - startNs; + // logToFile("waitTimeNs", waitTimeNs); + if (waitTimeNs >= 1000000) { + const ms = nsToMs(waitTimeNs); + sleep(ms); + } + } + + return WASI_ESUCCESS; }, proc_exit: (...params) => { console.error("proc_exit", params); @@ -632,7 +1044,7 @@ export const importObject = { }, clock_time_get: (_clock, _precision, ptr) => { const data = new DataView(zjs.memory.buffer); - data.setBigUint64(ptr, BigInt(Date.now()) * 1000000n, true) + data.setBigUint64(ptr, msToNs(perfNow()), true) }, environ_sizes_get: (...params) => { console.error("environ_sizes_get", params); @@ -640,11 +1052,22 @@ export const importObject = { }, wasi: { "thread-spawn": (instance) => { - const worker = new Worker(new URL("worker.ts", import.meta.url), { type: "module" }); - worker.postMessage([zjs.memory, instance]); + if (mainThread) { + spawnWorker(instance) + } else { + postMessage([instance]) + } } }, ...zjs.importObject(), }; +function spawnWorker(instance) { + const worker = new Worker(new URL("worker.ts", import.meta.url), { type: "module" }); + worker.postMessage([zjs.memory, instance, stdin, wasmModule, files]); + worker.onmessage = (event) => { + const [instance] = event.data; + spawnWorker(instance); + } +} diff --git a/example/wasi.ts b/example/wasi.ts new file mode 100644 index 000000000..84526b75a --- /dev/null +++ b/example/wasi.ts @@ -0,0 +1,431 @@ +/* + +This project is based from the Node implementation made by Gus Caplan +https://github.com/devsnek/node-wasi +However, JavaScript WASI is focused on: + * Bringing WASI to the Browsers + * Make easy to plug different filesystems + * Provide a type-safe api using Typescript + + +Copyright 2019 Gus Caplan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + + */ + +export const WASI_ESUCCESS = 0; +export const WASI_E2BIG = 1; +export const WASI_EACCES = 2; +export const WASI_EADDRINUSE = 3; +export const WASI_EADDRNOTAVAIL = 4; +export const WASI_EAFNOSUPPORT = 5; +export const WASI_EAGAIN = 6; +export const WASI_EALREADY = 7; +export const WASI_EBADF = 8; +export const WASI_EBADMSG = 9; +export const WASI_EBUSY = 10; +export const WASI_ECANCELED = 11; +export const WASI_ECHILD = 12; +export const WASI_ECONNABORTED = 13; +export const WASI_ECONNREFUSED = 14; +export const WASI_ECONNRESET = 15; +export const WASI_EDEADLK = 16; +export const WASI_EDESTADDRREQ = 17; +export const WASI_EDOM = 18; +export const WASI_EDQUOT = 19; +export const WASI_EEXIST = 20; +export const WASI_EFAULT = 21; +export const WASI_EFBIG = 22; +export const WASI_EHOSTUNREACH = 23; +export const WASI_EIDRM = 24; +export const WASI_EILSEQ = 25; +export const WASI_EINPROGRESS = 26; +export const WASI_EINTR = 27; +export const WASI_EINVAL = 28; +export const WASI_EIO = 29; +export const WASI_EISCONN = 30; +export const WASI_EISDIR = 31; +export const WASI_ELOOP = 32; +export const WASI_EMFILE = 33; +export const WASI_EMLINK = 34; +export const WASI_EMSGSIZE = 35; +export const WASI_EMULTIHOP = 36; +export const WASI_ENAMETOOLONG = 37; +export const WASI_ENETDOWN = 38; +export const WASI_ENETRESET = 39; +export const WASI_ENETUNREACH = 40; +export const WASI_ENFILE = 41; +export const WASI_ENOBUFS = 42; +export const WASI_ENODEV = 43; +export const WASI_ENOENT = 44; +export const WASI_ENOEXEC = 45; +export const WASI_ENOLCK = 46; +export const WASI_ENOLINK = 47; +export const WASI_ENOMEM = 48; +export const WASI_ENOMSG = 49; +export const WASI_ENOPROTOOPT = 50; +export const WASI_ENOSPC = 51; +export const WASI_ENOSYS = 52; +export const WASI_ENOTCONN = 53; +export const WASI_ENOTDIR = 54; +export const WASI_ENOTEMPTY = 55; +export const WASI_ENOTRECOVERABLE = 56; +export const WASI_ENOTSOCK = 57; +export const WASI_ENOTSUP = 58; +export const WASI_ENOTTY = 59; +export const WASI_ENXIO = 60; +export const WASI_EOVERFLOW = 61; +export const WASI_EOWNERDEAD = 62; +export const WASI_EPERM = 63; +export const WASI_EPIPE = 64; +export const WASI_EPROTO = 65; +export const WASI_EPROTONOSUPPORT = 66; +export const WASI_EPROTOTYPE = 67; +export const WASI_ERANGE = 68; +export const WASI_EROFS = 69; +export const WASI_ESPIPE = 70; +export const WASI_ESRCH = 71; +export const WASI_ESTALE = 72; +export const WASI_ETIMEDOUT = 73; +export const WASI_ETXTBSY = 74; +export const WASI_EXDEV = 75; +export const WASI_ENOTCAPABLE = 76; + +export const WASI_SIGABRT = 0; +export const WASI_SIGALRM = 1; +export const WASI_SIGBUS = 2; +export const WASI_SIGCHLD = 3; +export const WASI_SIGCONT = 4; +export const WASI_SIGFPE = 5; +export const WASI_SIGHUP = 6; +export const WASI_SIGILL = 7; +export const WASI_SIGINT = 8; +export const WASI_SIGKILL = 9; +export const WASI_SIGPIPE = 10; +export const WASI_SIGQUIT = 11; +export const WASI_SIGSEGV = 12; +export const WASI_SIGSTOP = 13; +export const WASI_SIGTERM = 14; +export const WASI_SIGTRAP = 15; +export const WASI_SIGTSTP = 16; +export const WASI_SIGTTIN = 17; +export const WASI_SIGTTOU = 18; +export const WASI_SIGURG = 19; +export const WASI_SIGUSR1 = 20; +export const WASI_SIGUSR2 = 21; +export const WASI_SIGVTALRM = 22; +export const WASI_SIGXCPU = 23; +export const WASI_SIGXFSZ = 24; + +export const WASI_FILETYPE_UNKNOWN = 0; +export const WASI_FILETYPE_BLOCK_DEVICE = 1; +export const WASI_FILETYPE_CHARACTER_DEVICE = 2; +export const WASI_FILETYPE_DIRECTORY = 3; +export const WASI_FILETYPE_REGULAR_FILE = 4; +export const WASI_FILETYPE_SOCKET_DGRAM = 5; +export const WASI_FILETYPE_SOCKET_STREAM = 6; +export const WASI_FILETYPE_SYMBOLIC_LINK = 7; + +export type WASI_FILETYPE = + | typeof WASI_FILETYPE_UNKNOWN + | typeof WASI_FILETYPE_BLOCK_DEVICE + | typeof WASI_FILETYPE_CHARACTER_DEVICE + | typeof WASI_FILETYPE_DIRECTORY + | typeof WASI_FILETYPE_REGULAR_FILE + | typeof WASI_FILETYPE_SOCKET_DGRAM + | typeof WASI_FILETYPE_SOCKET_STREAM + | typeof WASI_FILETYPE_SYMBOLIC_LINK; + +export const WASI_FDFLAG_APPEND = 0x0001; +export const WASI_FDFLAG_DSYNC = 0x0002; +export const WASI_FDFLAG_NONBLOCK = 0x0004; +export const WASI_FDFLAG_RSYNC = 0x0008; +export const WASI_FDFLAG_SYNC = 0x0010; + +export const WASI_RIGHT_FD_DATASYNC = BigInt(0x0000000000000001); +export const WASI_RIGHT_FD_READ = BigInt(0x0000000000000002); +export const WASI_RIGHT_FD_SEEK = BigInt(0x0000000000000004); +export const WASI_RIGHT_FD_FDSTAT_SET_FLAGS = BigInt(0x0000000000000008); +export const WASI_RIGHT_FD_SYNC = BigInt(0x0000000000000010); +export const WASI_RIGHT_FD_TELL = BigInt(0x0000000000000020); +export const WASI_RIGHT_FD_WRITE = BigInt(0x0000000000000040); +export const WASI_RIGHT_FD_ADVISE = BigInt(0x0000000000000080); +export const WASI_RIGHT_FD_ALLOCATE = BigInt(0x0000000000000100); +export const WASI_RIGHT_PATH_CREATE_DIRECTORY = BigInt(0x0000000000000200); +export const WASI_RIGHT_PATH_CREATE_FILE = BigInt(0x0000000000000400); +export const WASI_RIGHT_PATH_LINK_SOURCE = BigInt(0x0000000000000800); +export const WASI_RIGHT_PATH_LINK_TARGET = BigInt(0x0000000000001000); +export const WASI_RIGHT_PATH_OPEN = BigInt(0x0000000000002000); +export const WASI_RIGHT_FD_READDIR = BigInt(0x0000000000004000); +export const WASI_RIGHT_PATH_READLINK = BigInt(0x0000000000008000); +export const WASI_RIGHT_PATH_RENAME_SOURCE = BigInt(0x0000000000010000); +export const WASI_RIGHT_PATH_RENAME_TARGET = BigInt(0x0000000000020000); +export const WASI_RIGHT_PATH_FILESTAT_GET = BigInt(0x0000000000040000); +export const WASI_RIGHT_PATH_FILESTAT_SET_SIZE = BigInt(0x0000000000080000); +export const WASI_RIGHT_PATH_FILESTAT_SET_TIMES = BigInt(0x0000000000100000); +export const WASI_RIGHT_FD_FILESTAT_GET = BigInt(0x0000000000200000); +export const WASI_RIGHT_FD_FILESTAT_SET_SIZE = BigInt(0x0000000000400000); +export const WASI_RIGHT_FD_FILESTAT_SET_TIMES = BigInt(0x0000000000800000); +export const WASI_RIGHT_PATH_SYMLINK = BigInt(0x0000000001000000); +export const WASI_RIGHT_PATH_REMOVE_DIRECTORY = BigInt(0x0000000002000000); +export const WASI_RIGHT_PATH_UNLINK_FILE = BigInt(0x0000000004000000); +export const WASI_RIGHT_POLL_FD_READWRITE = BigInt(0x0000000008000000); +export const WASI_RIGHT_SOCK_SHUTDOWN = BigInt(0x0000000010000000); + +export const RIGHTS_ALL = + WASI_RIGHT_FD_DATASYNC | + WASI_RIGHT_FD_READ | + WASI_RIGHT_FD_SEEK | + WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + WASI_RIGHT_FD_SYNC | + WASI_RIGHT_FD_TELL | + WASI_RIGHT_FD_WRITE | + WASI_RIGHT_FD_ADVISE | + WASI_RIGHT_FD_ALLOCATE | + WASI_RIGHT_PATH_CREATE_DIRECTORY | + WASI_RIGHT_PATH_CREATE_FILE | + WASI_RIGHT_PATH_LINK_SOURCE | + WASI_RIGHT_PATH_LINK_TARGET | + WASI_RIGHT_PATH_OPEN | + WASI_RIGHT_FD_READDIR | + WASI_RIGHT_PATH_READLINK | + WASI_RIGHT_PATH_RENAME_SOURCE | + WASI_RIGHT_PATH_RENAME_TARGET | + WASI_RIGHT_PATH_FILESTAT_GET | + WASI_RIGHT_PATH_FILESTAT_SET_SIZE | + WASI_RIGHT_PATH_FILESTAT_SET_TIMES | + WASI_RIGHT_FD_FILESTAT_GET | + WASI_RIGHT_FD_FILESTAT_SET_TIMES | + WASI_RIGHT_FD_FILESTAT_SET_SIZE | + WASI_RIGHT_PATH_SYMLINK | + WASI_RIGHT_PATH_UNLINK_FILE | + WASI_RIGHT_PATH_REMOVE_DIRECTORY | + WASI_RIGHT_POLL_FD_READWRITE | + WASI_RIGHT_SOCK_SHUTDOWN; + +export const RIGHTS_BLOCK_DEVICE_BASE = RIGHTS_ALL; +export const RIGHTS_BLOCK_DEVICE_INHERITING = RIGHTS_ALL; + +export const RIGHTS_CHARACTER_DEVICE_BASE = RIGHTS_ALL; +export const RIGHTS_CHARACTER_DEVICE_INHERITING = RIGHTS_ALL; + +export const RIGHTS_REGULAR_FILE_BASE = + WASI_RIGHT_FD_DATASYNC | + WASI_RIGHT_FD_READ | + WASI_RIGHT_FD_SEEK | + WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + WASI_RIGHT_FD_SYNC | + WASI_RIGHT_FD_TELL | + WASI_RIGHT_FD_WRITE | + WASI_RIGHT_FD_ADVISE | + WASI_RIGHT_FD_ALLOCATE | + WASI_RIGHT_FD_FILESTAT_GET | + WASI_RIGHT_FD_FILESTAT_SET_SIZE | + WASI_RIGHT_FD_FILESTAT_SET_TIMES | + WASI_RIGHT_POLL_FD_READWRITE; +export const RIGHTS_REGULAR_FILE_INHERITING = BigInt(0); + +export const RIGHTS_DIRECTORY_BASE = + WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + WASI_RIGHT_FD_SYNC | + WASI_RIGHT_FD_ADVISE | + WASI_RIGHT_PATH_CREATE_DIRECTORY | + WASI_RIGHT_PATH_CREATE_FILE | + WASI_RIGHT_PATH_LINK_SOURCE | + WASI_RIGHT_PATH_LINK_TARGET | + WASI_RIGHT_PATH_OPEN | + WASI_RIGHT_FD_READDIR | + WASI_RIGHT_PATH_READLINK | + WASI_RIGHT_PATH_RENAME_SOURCE | + WASI_RIGHT_PATH_RENAME_TARGET | + WASI_RIGHT_PATH_FILESTAT_GET | + WASI_RIGHT_PATH_FILESTAT_SET_SIZE | + WASI_RIGHT_PATH_FILESTAT_SET_TIMES | + WASI_RIGHT_FD_FILESTAT_GET | + WASI_RIGHT_FD_FILESTAT_SET_TIMES | + WASI_RIGHT_PATH_SYMLINK | + WASI_RIGHT_PATH_UNLINK_FILE | + WASI_RIGHT_PATH_REMOVE_DIRECTORY | + WASI_RIGHT_POLL_FD_READWRITE; +export const RIGHTS_DIRECTORY_INHERITING = + RIGHTS_DIRECTORY_BASE | RIGHTS_REGULAR_FILE_BASE; + +export const RIGHTS_SOCKET_BASE = + WASI_RIGHT_FD_READ | + WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + WASI_RIGHT_FD_WRITE | + WASI_RIGHT_FD_FILESTAT_GET | + WASI_RIGHT_POLL_FD_READWRITE | + WASI_RIGHT_SOCK_SHUTDOWN; +export const RIGHTS_SOCKET_INHERITING = RIGHTS_ALL; + +export const RIGHTS_TTY_BASE = + WASI_RIGHT_FD_READ | + WASI_RIGHT_FD_FDSTAT_SET_FLAGS | + WASI_RIGHT_FD_WRITE | + WASI_RIGHT_FD_FILESTAT_GET | + WASI_RIGHT_POLL_FD_READWRITE; +export const RIGHTS_TTY_INHERITING = BigInt(0); + +export const WASI_CLOCK_REALTIME = 0; +export const WASI_CLOCK_MONOTONIC = 1; +export const WASI_CLOCK_PROCESS_CPUTIME_ID = 2; +export const WASI_CLOCK_THREAD_CPUTIME_ID = 3; + +export const WASI_EVENTTYPE_CLOCK = 0; +export const WASI_EVENTTYPE_FD_READ = 1; +export const WASI_EVENTTYPE_FD_WRITE = 2; + +export const WASI_FILESTAT_SET_ATIM = 1 << 0; +export const WASI_FILESTAT_SET_ATIM_NOW = 1 << 1; +export const WASI_FILESTAT_SET_MTIM = 1 << 2; +export const WASI_FILESTAT_SET_MTIM_NOW = 1 << 3; + +export const WASI_O_CREAT = 1 << 0; +export const WASI_O_DIRECTORY = 1 << 1; +export const WASI_O_EXCL = 1 << 2; +export const WASI_O_TRUNC = 1 << 3; + +export const WASI_PREOPENTYPE_DIR = 0; + +export const WASI_DIRCOOKIE_START = 0; + +export const WASI_STDIN_FILENO = 0; +export const WASI_STDOUT_FILENO = 1; +export const WASI_STDERR_FILENO = 2; + +export const WASI_WHENCE_SET = 0; +export const WASI_WHENCE_CUR = 1; +export const WASI_WHENCE_END = 2; + +// http://man7.org/linux/man-pages/man3/errno.3.html +export const ERROR_MAP: { [key: string]: number } = { + E2BIG: WASI_E2BIG, + EACCES: WASI_EACCES, + EADDRINUSE: WASI_EADDRINUSE, + EADDRNOTAVAIL: WASI_EADDRNOTAVAIL, + EAFNOSUPPORT: WASI_EAFNOSUPPORT, + EALREADY: WASI_EALREADY, + EAGAIN: WASI_EAGAIN, + // EBADE: WASI_EBADE, + EBADF: WASI_EBADF, + // EBADFD: WASI_EBADFD, + EBADMSG: WASI_EBADMSG, + // EBADR: WASI_EBADR, + // EBADRQC: WASI_EBADRQC, + // EBADSLT: WASI_EBADSLT, + EBUSY: WASI_EBUSY, + ECANCELED: WASI_ECANCELED, + ECHILD: WASI_ECHILD, + // ECHRNG: WASI_ECHRNG, + // ECOMM: WASI_ECOMM, + ECONNABORTED: WASI_ECONNABORTED, + ECONNREFUSED: WASI_ECONNREFUSED, + ECONNRESET: WASI_ECONNRESET, + EDEADLOCK: WASI_EDEADLK, + EDESTADDRREQ: WASI_EDESTADDRREQ, + EDOM: WASI_EDOM, + EDQUOT: WASI_EDQUOT, + EEXIST: WASI_EEXIST, + EFAULT: WASI_EFAULT, + EFBIG: WASI_EFBIG, + EHOSTDOWN: WASI_EHOSTUNREACH, + EHOSTUNREACH: WASI_EHOSTUNREACH, + // EHWPOISON: WASI_EHWPOISON, + EIDRM: WASI_EIDRM, + EILSEQ: WASI_EILSEQ, + EINPROGRESS: WASI_EINPROGRESS, + EINTR: WASI_EINTR, + EINVAL: WASI_EINVAL, + EIO: WASI_EIO, + EISCONN: WASI_EISCONN, + EISDIR: WASI_EISDIR, + ELOOP: WASI_ELOOP, + EMFILE: WASI_EMFILE, + EMLINK: WASI_EMLINK, + EMSGSIZE: WASI_EMSGSIZE, + EMULTIHOP: WASI_EMULTIHOP, + ENAMETOOLONG: WASI_ENAMETOOLONG, + ENETDOWN: WASI_ENETDOWN, + ENETRESET: WASI_ENETRESET, + ENETUNREACH: WASI_ENETUNREACH, + ENFILE: WASI_ENFILE, + ENOBUFS: WASI_ENOBUFS, + ENODEV: WASI_ENODEV, + ENOENT: WASI_ENOENT, + ENOEXEC: WASI_ENOEXEC, + ENOLCK: WASI_ENOLCK, + ENOLINK: WASI_ENOLINK, + ENOMEM: WASI_ENOMEM, + ENOMSG: WASI_ENOMSG, + ENOPROTOOPT: WASI_ENOPROTOOPT, + ENOSPC: WASI_ENOSPC, + ENOSYS: WASI_ENOSYS, + ENOTCONN: WASI_ENOTCONN, + ENOTDIR: WASI_ENOTDIR, + ENOTEMPTY: WASI_ENOTEMPTY, + ENOTRECOVERABLE: WASI_ENOTRECOVERABLE, + ENOTSOCK: WASI_ENOTSOCK, + ENOTTY: WASI_ENOTTY, + ENXIO: WASI_ENXIO, + EOVERFLOW: WASI_EOVERFLOW, + EOWNERDEAD: WASI_EOWNERDEAD, + EPERM: WASI_EPERM, + EPIPE: WASI_EPIPE, + EPROTO: WASI_EPROTO, + EPROTONOSUPPORT: WASI_EPROTONOSUPPORT, + EPROTOTYPE: WASI_EPROTOTYPE, + ERANGE: WASI_ERANGE, + EROFS: WASI_EROFS, + ESPIPE: WASI_ESPIPE, + ESRCH: WASI_ESRCH, + ESTALE: WASI_ESTALE, + ETIMEDOUT: WASI_ETIMEDOUT, + ETXTBSY: WASI_ETXTBSY, + EXDEV: WASI_EXDEV +}; + +export const SIGNAL_MAP: { [key: string]: string } = { + [WASI_SIGHUP]: "SIGHUP", + [WASI_SIGINT]: "SIGINT", + [WASI_SIGQUIT]: "SIGQUIT", + [WASI_SIGILL]: "SIGILL", + [WASI_SIGTRAP]: "SIGTRAP", + [WASI_SIGABRT]: "SIGABRT", + [WASI_SIGBUS]: "SIGBUS", + [WASI_SIGFPE]: "SIGFPE", + [WASI_SIGKILL]: "SIGKILL", + [WASI_SIGUSR1]: "SIGUSR1", + [WASI_SIGSEGV]: "SIGSEGV", + [WASI_SIGUSR2]: "SIGUSR2", + [WASI_SIGPIPE]: "SIGPIPE", + [WASI_SIGALRM]: "SIGALRM", + [WASI_SIGTERM]: "SIGTERM", + [WASI_SIGCHLD]: "SIGCHLD", + [WASI_SIGCONT]: "SIGCONT", + [WASI_SIGSTOP]: "SIGSTOP", + [WASI_SIGTSTP]: "SIGTSTP", + [WASI_SIGTTIN]: "SIGTTIN", + [WASI_SIGTTOU]: "SIGTTOU", + [WASI_SIGURG]: "SIGURG", + [WASI_SIGXCPU]: "SIGXCPU", + [WASI_SIGXFSZ]: "SIGXFSZ", + [WASI_SIGVTALRM]: "SIGVTALRM" +}; diff --git a/example/worker.ts b/example/worker.ts index 5ca06d8f3..83b9d9313 100644 --- a/example/worker.ts +++ b/example/worker.ts @@ -1,11 +1,14 @@ -import { importObject, zjs } from "./imports"; +import { importObject, setFiles, setMainThread, setStdin, zjs } from "./imports"; onmessage = async (e) => { console.log("module received from main thread"); - const [memory, instance] = e.data; + const [memory, instance, stdin, wasmModule, files] = e.data; + console.log(wasmModule) + setStdin(stdin); + setMainThread(false); + setFiles(files); importObject.env.memory = memory; -const url = new URL("ghostty-wasm.wasm", import.meta.url); - const results = await WebAssembly.instantiateStreaming(fetch(url), importObject) + const results = await WebAssembly.instantiate(wasmModule, importObject); zjs.memory = memory; - results.instance.exports.wasi_thread_start(100, instance); + results.exports.wasi_thread_start(100, instance); }; diff --git a/src/Command.zig b/src/Command.zig index 9f3ce499c..6c5066716 100644 --- a/src/Command.zig +++ b/src/Command.zig @@ -203,48 +203,9 @@ fn startWasi(self: *Command, arena: Allocator) !void { else @compileError("missing env vars"); - // Fork. If we have a cgroup specified on Linxu then we use clone - const pid: posix.pid_t = switch (builtin.os.tag) { - .linux => if (self.linux_cgroup) |cgroup| - try internal_os.cgroup.cloneInto(cgroup) - else - try posix.fork(), + self.pid = 100; - else => try posix.fork(), - }; - - if (pid != 0) { - // Parent, return immediately. - self.pid = @intCast(pid); - return; - } - - // We are the child. - - // Setup our file descriptors for std streams. - if (self.stdin) |f| setupFd(f.handle, posix.STDIN_FILENO) catch - return error.ExecFailedInChild; - if (self.stdout) |f| setupFd(f.handle, posix.STDOUT_FILENO) catch - return error.ExecFailedInChild; - if (self.stderr) |f| setupFd(f.handle, posix.STDERR_FILENO) catch - return error.ExecFailedInChild; - - // Setup our working directory - if (self.cwd) |cwd| posix.chdir(cwd) catch { - // 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. - }; - - // If the user requested a pre exec callback, call it now. - if (self.pre_exec) |f| f(self); - std.log.err("{s} {*}", .{ pathZ, envp }); - - // If we are executing this code, the exec failed. In that scenario, - // we return a very specific error that can be detected to determine - // we're in the child. + std.log.err("need to fork {s} {*}", .{ pathZ, envp }); return; } diff --git a/src/font/face/web_canvas.zig b/src/font/face/web_canvas.zig index 163e45f34..48717eadc 100644 --- a/src/font/face/web_canvas.zig +++ b/src/font/face/web_canvas.zig @@ -29,9 +29,6 @@ pub const Face = struct { /// Metrics for this font face. These are useful for renderers. metrics: font.face.Metrics, - /// The canvas element that we will reuse to render glyphs - canvas: js.Object, - /// The map to store multi-codepoint grapheme clusters that are rendered. /// We use 1 above the maximum unicode codepoint up to the max 32-bit /// unsigned integer to store the "glyph index" for graphemes. @@ -58,20 +55,12 @@ pub const Face = struct { const font_str = try alloc.dupe(u8, raw); errdefer alloc.free(font_str); - // Create our canvasxx that we're going to continue to reuse. - const OffscreenCanvas = try js.global.get(js.Object, "OffscreenCanvas"); - defer OffscreenCanvas.deinit(); - const canvas = try OffscreenCanvas.new(.{ 0, 0 }); - errdefer canvas.deinit(); - var result = Face{ .alloc = alloc, .font_str = font_str, .size = size, .presentation = presentation, - .canvas = canvas, - // We're going to calculate these right after initialization. .metrics = undefined, }; @@ -89,7 +78,6 @@ pub const Face = struct { while (it.next()) |value| self.alloc.free(value.*); self.glyph_to_grapheme.deinit(self.alloc); } - self.canvas.deinit(); self.* = undefined; } @@ -291,13 +279,15 @@ pub const Face = struct { fn context(self: Face) !js.Object { // This will return the same context on subsequent calls so it // is important to reset it. - const ctx = try self.canvas.call(js.Object, "getContext", .{js.string("2d")}); + const canvas = try js.global.get(js.Object, "fontCanvas"); + defer canvas.deinit(); + const ctx = try canvas.call(js.Object, "getContext", .{js.string("2d")}); errdefer ctx.deinit(); // Clear the canvas { - const width = try self.canvas.get(f64, "width"); - const height = try self.canvas.get(f64, "height"); + const width = try canvas.get(f64, "width"); + const height = try canvas.get(f64, "height"); try ctx.call(void, "clearRect", .{ 0, 0, width, height }); } @@ -428,8 +418,10 @@ pub const Face = struct { // Resize canvas to match the glyph size exactly { - try self.canvas.set("width", width); - try self.canvas.set("height", height); + const canvas = try js.global.get(js.Object, "fontCanvas"); + defer canvas.deinit(); + try canvas.set("width", width); + try canvas.set("height", height); // const width_str = try std.fmt.allocPrint(alloc, "{d}px", .{width}); // defer alloc.free(width_str); @@ -562,25 +554,25 @@ pub const Wasm = struct { }; } - export fn face_debug_canvas(face: *Face) void { - face_debug_canvas_(face) catch |err| { - log.warn("error adding debug canvas err={}", .{err}); - }; - } + // export fn face_debug_canvas(face: *Face) void { + // face_debug_canvas_(face) catch |err| { + // log.warn("error adding debug canvas err={}", .{err}); + // }; + // } - fn face_debug_canvas_(face: *Face) !void { - const doc = try js.global.get(js.Object, "document"); - defer doc.deinit(); + // fn face_debug_canvas_(face: *Face) !void { + // const doc = try js.global.get(js.Object, "document"); + // defer doc.deinit(); - const elem = try doc.call( - ?js.Object, - "getElementById", - .{js.string("face-canvas")}, - ) orelse return error.CanvasContainerNotFound; - defer elem.deinit(); + // const elem = try doc.call( + // ?js.Object, + // "getElementById", + // .{js.string("face-canvas")}, + // ) orelse return error.CanvasContainerNotFound; + // defer elem.deinit(); - try elem.call(void, "append", .{face.canvas}); - } + // try elem.call(void, "append", .{face.canvas}); + // } fn face_render_glyph_(face: *Face, atlas: *font.Atlas, codepoint: u32) !*font.Glyph { const glyph = try face.renderGlyph(alloc, atlas, codepoint, .{}); diff --git a/src/main_wasm.zig b/src/main_wasm.zig index f51b201db..66d1650cc 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -26,6 +26,14 @@ export fn run(str: [*]const u8, len: usize) void { std.log.err("err: {?}", .{err}); }; } + +var surf: ?*Surface = null; +export fn draw() void { + surf.?.renderer.drawFrame(surf.?.rt_surface) catch |err| { + std.log.err("err: {?}", .{err}); + }; +} + fn run_(str: []const u8) !void { var config = try Config.default(alloc); var fbs = std.io.fixedBufferStream(str); @@ -41,28 +49,29 @@ fn run_(str: []const u8) !void { try surface.init(alloc, &config, app, &app_runtime, apprt_surface); std.log.err("{}", .{surface.size}); try surface.renderer.setScreenSize(surface.size); - const esc = "\x1b["; - surface.io.processOutput("M_yhelloaaaaaaaaa\n\r🐏\n\rπŸ‘πŸ½\n\rM_ghostty" ++ esc ++ "2;2H" ++ esc ++ "48;2;240;40;40m" ++ esc ++ "38;2;23;255;80mhello"); - // try surface.renderer_state.terminal.printString("M_yhelloaaaaaaaaa\n🐏\nπŸ‘πŸ½\nM_ghostty"); - // surface.renderer_state.terminal.setCursorPos(4, 2); - // try surface.renderer_state.terminal.setAttribute(.{ .direct_color_bg = .{ - // .r = 240, - // .g = 40, - // .b = 40, - // } }); - // try surface.renderer_state.terminal.setAttribute(.{ .direct_color_fg = .{ - // .r = 255, - // .g = 255, - // .b = 255, - // } }); - // try surface.renderer_state.terminal.printString("hello"); - try surface.renderer.updateFrame(apprt_surface, &surface.renderer_state, false); - try surface.renderer.drawFrame(apprt_surface); - try surface.renderer.updateFrame(apprt_surface, &surface.renderer_state, false); - try surface.renderer.drawFrame(apprt_surface); + surf = surface; + // const esc = "\x1b["; + // surface.io.processOutput("M_yhelloaaaaaaaaa\n\r🐏\n\rπŸ‘πŸ½\n\rM_ghostty" ++ esc ++ "2;2H" ++ esc ++ "48;2;240;40;40m" ++ esc ++ "38;2;23;255;80mhello"); + // // try surface.renderer_state.terminal.printString("M_yhelloaaaaaaaaa\n🐏\nπŸ‘πŸ½\nM_ghostty"); + // // surface.renderer_state.terminal.setCursorPos(4, 2); + // // try surface.renderer_state.terminal.setAttribute(.{ .direct_color_bg = .{ + // // .r = 240, + // // .g = 40, + // // .b = 40, + // // } }); + // // try surface.renderer_state.terminal.setAttribute(.{ .direct_color_fg = .{ + // // .r = 255, + // // .g = 255, + // // .b = 255, + // // } }); + // // try surface.renderer_state.terminal.printString("hello"); + // try surface.renderer.updateFrame(apprt_surface, &surface.renderer_state, false); + // try surface.renderer.drawFrame(apprt_surface); + // try surface.renderer.updateFrame(apprt_surface, &surface.renderer_state, false); + // try surface.renderer.drawFrame(apprt_surface); - // const webgl = try renderer.OpenGL.init(alloc, .{ .config = try renderer.OpenGL.DerivedConfig.init(alloc, &config) }); - // _ = webgl; + // // const webgl = try renderer.OpenGL.init(alloc, .{ .config = try renderer.OpenGL.DerivedConfig.init(alloc, &config) }); + // // _ = webgl; } pub const std_options: std.Options = .{ diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 67a6212db..37a5c2c09 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -683,6 +683,7 @@ pub fn updateFrame( ) !void { _ = surface; + std.log.err("update frame", .{}); // Data we extract out of the critical area. const Critical = struct { full_rebuild: bool, @@ -701,6 +702,7 @@ pub fn updateFrame( state.mutex.lock(); defer state.mutex.unlock(); + std.log.err("critical", .{}); // If we're in a synchronized output state, we pause all rendering. if (state.terminal.modes.get(.synchronized_output)) { @@ -2229,8 +2231,9 @@ fn flushAtlasSingle( /// the cells. pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void { // If we're in single-threaded more we grab a lock since we use shared data. - if (single_threaded_draw) self.draw_mutex.lock(); - defer if (single_threaded_draw) self.draw_mutex.unlock(); + // wasm can't use a mutex on the main thread because it uses Atomic.wait + if (single_threaded_draw and builtin.cpu.arch != .wasm32) self.draw_mutex.lock(); + defer if (single_threaded_draw and builtin.cpu.arch != .wasm32) self.draw_mutex.unlock(); const gl_state: *GLState = if (self.gl_state) |*v| v else return; // Go through our images and see if we need to setup any textures. diff --git a/src/renderer/Thread.zig b/src/renderer/Thread.zig index 91e355480..acf6a9baf 100644 --- a/src/renderer/Thread.zig +++ b/src/renderer/Thread.zig @@ -232,7 +232,7 @@ fn threadMain_(self: *Thread) !void { self.startDrawTimer(); // Run - log.debug("starting renderer thread", .{}); + log.err("starting renderer thread", .{}); defer log.debug("starting renderer thread shutdown", .{}); _ = try self.loop.run(.until_done); } @@ -437,6 +437,7 @@ fn wakeupCallback( }; const t = self_.?; + log.err("wakeup", .{}); // When we wake up, we check the mailbox. Mailbox producers should // wake up our thread after publishing. diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 827566dbb..8995ad360 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -126,7 +126,7 @@ pub fn threadEnter( // Start our read thread const read_thread = try std.Thread.spawn( - .{}, + .{ .allocator = alloc }, if (builtin.os.tag == .windows) ReadThread.threadMainWindows else ReadThread.threadMainPosix, .{ pty_fds.read, io, pipe[0] }, ); @@ -1424,6 +1424,7 @@ pub const ReadThread = struct { // child process dies. To be safe, we just break the loop // and let our poll happen. if (n == 0) break; + std.log.err("{} {} {}", .{ buf[0], n, @intFromPtr(&buf) }); // log.info("DATA: {d}", .{n}); @call(.always_inline, termio.Termio.processOutput, .{ io, buf[0..n] }); diff --git a/src/termio/Termio.zig b/src/termio/Termio.zig index 3228afb6d..c48071046 100644 --- a/src/termio/Termio.zig +++ b/src/termio/Termio.zig @@ -554,6 +554,7 @@ fn processOutputLocked(self: *Termio, buf: []const u8) void { // use a timer under the covers if (internal_os.Instant.now()) |now| cursor_reset: { if (self.last_cursor_reset) |last| { + log.err("now: {} last: {}", .{ now, last }); if (now.since(last) <= (500 * std.time.ns_per_ms)) { break :cursor_reset; } @@ -571,6 +572,7 @@ fn processOutputLocked(self: *Termio, buf: []const u8) void { // process a byte at a time alternating between the inspector handler // and the termio handler. This is very slow compared to our optimizations // below but at least users only pay for it if they're using the inspector. + std.log.err("to print {s}", .{buf}); if (builtin.cpu.arch == .wasm32) { self.terminal_stream.nextSlice(buf) catch |err| log.err("error processing terminal data: {}", .{err}); From 487f3dabc7927a505f9afa774aaf3cfe8eaa2e62 Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Sun, 24 Nov 2024 15:45:46 -0500 Subject: [PATCH 08/10] only try to lock on main --- example/app.ts | 12 ++++++------ src/main_wasm.zig | 11 +++++------ src/renderer/OpenGL.zig | 26 ++++++++++++++------------ 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/example/app.ts b/example/app.ts index 387502218..655ce44a0 100644 --- a/example/app.ts +++ b/example/app.ts @@ -59,7 +59,7 @@ fetch(url.href) // Create our config const config_str = makeStr("font-family = monospace\nfont-size = 32\n"); - old(results); + // old(results); setWasmModule(results.module); const stdin = new SharedArrayBuffer(1024); const files = { @@ -81,20 +81,20 @@ fetch(url.href) Atomics.notify(n, 0); console.error("done storing"); function drawing() { - setTimeout(() => { + requestAnimationFrame(() => { draw(); drawing(); - }, 100); + }); } drawing() setTimeout(() => { - const io = new Uint8ClampedArray(stdin, 4); const text = new TextEncoder().encode("🐏\n\rπŸ‘πŸ½\n\rM_ghostty\033[2;2H\033[48;2;240;40;40m\033[38;2;23;255;80mhello"); - io.set(text); const n = new Int32Array(stdin); console.error("storing"); - Atomics.store(n, 0, text.length) + const place = Atomics.add(n, 0, text.length) + const io = new Uint8ClampedArray(stdin, 4 + place); + io.set(text); Atomics.notify(n, 0); console.error("done storing"); }, 5000) diff --git a/src/main_wasm.zig b/src/main_wasm.zig index 66d1650cc..fcdb9b1ae 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -78,12 +78,11 @@ pub const std_options: std.Options = .{ // Set our log level. We try to get as much logging as possible but in // ReleaseSmall mode where we're optimizing for space, we elevate the // log level. - // .log_level = switch (builtin.mode) { - // .Debug => .debug, - // .ReleaseSmall => .warn, - // else => .info, - // }, - .log_level = .info, + .log_level = switch (builtin.mode) { + .Debug => .debug, + .ReleaseSmall => .warn, + else => .info, + }, // Set our log function .logFn = @import("os/wasm/log.zig").log, diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 37a5c2c09..d745d7674 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -2182,7 +2182,6 @@ fn flushAtlasSingle( internal_format: gl.Texture.InternalFormat, format: gl.Texture.Format, ) !void { - std.log.err("starging flushing atlas", .{}); // If the texture isn't modified we do nothing const new_modified = atlas.modified.load(.monotonic); if (new_modified <= modified.*) return; @@ -2195,7 +2194,6 @@ fn flushAtlasSingle( defer texbind.unbind(); const new_resized = atlas.resized.load(.monotonic); - std.log.err("flushing atlas", .{}); if (new_resized > resized.*) { try texbind.image2D( 0, @@ -2231,9 +2229,15 @@ fn flushAtlasSingle( /// the cells. pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void { // If we're in single-threaded more we grab a lock since we use shared data. - // wasm can't use a mutex on the main thread because it uses Atomic.wait - if (single_threaded_draw and builtin.cpu.arch != .wasm32) self.draw_mutex.lock(); - defer if (single_threaded_draw and builtin.cpu.arch != .wasm32) self.draw_mutex.unlock(); + // wasm can't lock a mutex on the main thread because it uses Atomic.wait + if (single_threaded_draw) { + if (builtin.cpu.arch == .wasm32) { + if (!self.draw_mutex.tryLock()) return; + } else { + self.draw_mutex.lock(); + } + } + defer if (single_threaded_draw) self.draw_mutex.unlock(); const gl_state: *GLState = if (self.gl_state) |*v| v else return; // Go through our images and see if we need to setup any textures. @@ -2465,10 +2469,8 @@ fn drawCells( gl_state: *const GLState, cells: std.ArrayListUnmanaged(CellProgram.Cell), ) !void { - std.log.err("start drawing cells", .{}); // If we have no cells to render, then we render nothing. if (cells.items.len == 0) return; - std.log.err("have cells", .{}); // Todo: get rid of this completely self.gl_cells_written = 0; @@ -2489,10 +2491,10 @@ fn drawCells( // Our allocated buffer on the GPU is smaller than our capacity. // We reallocate a new buffer with the full new capacity. if (self.gl_cells_size < cells.capacity) { - log.info("reallocating GPU buffer old={} new={}", .{ - self.gl_cells_size, - cells.capacity, - }); + // log.info("reallocating GPU buffer old={} new={}", .{ + // self.gl_cells_size, + // cells.capacity, + // }); try bind.vbo.setDataNullManual( @sizeOf(CellProgram.Cell) * cells.capacity, @@ -2506,7 +2508,7 @@ fn drawCells( // If we have data to write to the GPU, send it. if (self.gl_cells_written < cells.items.len) { const data = cells.items[self.gl_cells_written..]; - log.info("sending {} cells to GPU", .{data.len}); + // log.info("sending {} cells to GPU", .{data.len}); try bind.vbo.setSubData(self.gl_cells_written * @sizeOf(CellProgram.Cell), data); self.gl_cells_written += data.len; From 0ff5e5635dfea2dc0093d770344bbe240f1ec9da Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Sun, 24 Nov 2024 15:50:05 -0500 Subject: [PATCH 09/10] clean up uncessary hacks --- example/app.ts | 11 +++----- example/imports.ts | 1 + pkg/simdutf/vendor/simdutf.h | 34 ++++++++++++------------- src/App.zig | 2 +- src/Surface.zig | 11 ++++---- src/build_config.zig | 24 +++++++++-------- src/input/Link.zig | 2 -- src/os/main.zig | 13 ---------- src/renderer/link.zig | 5 ++-- src/renderer/opengl/custom.zig | 11 ++++---- src/renderer/opengl/image.zig | 2 +- src/terminal/kitty/graphics_image.zig | 4 +-- src/terminal/kitty/graphics_storage.zig | 2 +- src/termio/Exec.zig | 10 ++++---- src/termio/Termio.zig | 4 +-- 15 files changed, 60 insertions(+), 76 deletions(-) diff --git a/example/app.ts b/example/app.ts index 655ce44a0..ffe9e58e9 100644 --- a/example/app.ts +++ b/example/app.ts @@ -73,13 +73,11 @@ fetch(url.href) run(config_str.ptr, config_str.len); await new Promise((resolve) => setTimeout(resolve, 500)) const io = new Uint8ClampedArray(stdin, 4); - const text = new TextEncoder().encode("hello world\n\r"); + const text = new TextEncoder().encode("hello world\r\n"); io.set(text); const n = new Int32Array(stdin); - console.error("storing"); Atomics.store(n, 0, text.length) Atomics.notify(n, 0); - console.error("done storing"); function drawing() { requestAnimationFrame(() => { draw(); @@ -88,14 +86,13 @@ fetch(url.href) } drawing() - setTimeout(() => { - const text = new TextEncoder().encode("🐏\n\rπŸ‘πŸ½\n\rM_ghostty\033[2;2H\033[48;2;240;40;40m\033[38;2;23;255;80mhello"); + setInterval(() => { + // const text = new TextEncoder().encode("🐏\n\rπŸ‘πŸ½\n\rM_ghostty\033[2;2H\033[48;2;240;40;40m\033[38;2;23;255;80mhello"); + const text = new TextEncoder().encode("🐏\r\nπŸ‘πŸ½\r\nM_ghostty\033[48;2;240;40;40m\033[38;2;23;255;80mhello\r\n"); const n = new Int32Array(stdin); - console.error("storing"); const place = Atomics.add(n, 0, text.length) const io = new Uint8ClampedArray(stdin, 4 + place); io.set(text); Atomics.notify(n, 0); - console.error("done storing"); }, 5000) }) diff --git a/example/imports.ts b/example/imports.ts index ca60ef7d3..9cf483ed1 100644 --- a/example/imports.ts +++ b/example/imports.ts @@ -795,6 +795,7 @@ export const importObject = { const read = Math.min(iov_len, bytes.length); const io = new Uint8ClampedArray(zjs.memory.buffer, iov_base, iov_len); io.set(bytes.slice(0, read)); + console.error(bytes[read-1]); bytes = bytes.slice(read); if (bytes.length === 0) bytes = null; nwritten += read; diff --git a/pkg/simdutf/vendor/simdutf.h b/pkg/simdutf/vendor/simdutf.h index 89c9d1ca1..d37bd2c7d 100644 --- a/pkg/simdutf/vendor/simdutf.h +++ b/pkg/simdutf/vendor/simdutf.h @@ -619,9 +619,9 @@ enum { #ifndef SIMDUTF_IMPLEMENTATION_H #define SIMDUTF_IMPLEMENTATION_H #include -// #if !defined(SIMDUTF_NO_THREADS) -// #include -// #endif +#if !defined(SIMDUTF_NO_THREADS) +#include +#endif #include #include /* begin file include/simdutf/internal/isadetection.h */ @@ -3692,7 +3692,7 @@ class atomic_ptr { public: atomic_ptr(T *_ptr) : ptr{_ptr} {} -// #if defined(SIMDUTF_NO_THREADS) +#if defined(SIMDUTF_NO_THREADS) operator const T*() const { return ptr; } const T& operator*() const { return *ptr; } const T* operator->() const { return ptr; } @@ -3702,24 +3702,24 @@ public: T* operator->() { return ptr; } atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; } -// #else -// operator const T*() const { return ptr.load(); } -// const T& operator*() const { return *ptr; } -// const T* operator->() const { return ptr.load(); } +#else + operator const T*() const { return ptr.load(); } + const T& operator*() const { return *ptr; } + const T* operator->() const { return ptr.load(); } -// operator T*() { return ptr.load(); } -// T& operator*() { return *ptr; } -// T* operator->() { return ptr.load(); } -// atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; } + operator T*() { return ptr.load(); } + T& operator*() { return *ptr; } + T* operator->() { return ptr.load(); } + atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; } -// #endif +#endif private: -// #if defined(SIMDUTF_NO_THREADS) +#if defined(SIMDUTF_NO_THREADS) T* ptr; -// #else -// std::atomic ptr; -// #endif +#else + std::atomic ptr; +#endif }; class detect_best_supported_implementation_on_first_use; diff --git a/src/App.zig b/src/App.zig index 24d9018bc..279c4e497 100644 --- a/src/App.zig +++ b/src/App.zig @@ -64,7 +64,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: ?internal_os.Instant = null, +last_notification_time: ?std.time.Instant = null, last_notification_digest: u64 = 0, /// The conditional state of the configuration. See the equivalent field diff --git a/src/Surface.zig b/src/Surface.zig index 7f3b9af6b..77c9224e3 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -21,6 +21,7 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const global_state = &@import("global.zig").state; +const oni = @import("oniguruma"); const crash = @import("crash/main.zig"); const unicode = @import("unicode/main.zig"); const renderer = @import("renderer.zig"); @@ -173,7 +174,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: internal_os.Instant = undefined, + left_click_time: std.time.Instant = undefined, /// The last x/y sent for mouse reports. event_point: ?terminal.point.Coordinate = null, @@ -255,7 +256,7 @@ const DerivedConfig = struct { links: []Link, const Link = struct { - regex: input.Link.Regex, + regex: oni.Regex, action: input.Link.Action, highlight: input.Link.Highlight, }; @@ -2723,7 +2724,7 @@ pub fn mouseButtonCallback( // If we are within the interval that the click would register // an increment then we do not extend the selection. - if (internal_os.Instant.now()) |now| { + if (std.time.Instant.now()) |now| { const since = now.since(self.mouse.left_click_time); if (since <= self.config.mouse_interval) { // Click interval very short, we may be increasing @@ -2869,7 +2870,7 @@ pub fn mouseButtonCallback( self.mouse.left_click_ypos = pos.y; // Setup our click counter and timer - if (internal_os.Instant.now()) |now| { + if (std.time.Instant.now()) |now| { // 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) { @@ -4477,7 +4478,7 @@ 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 internal_os.Instant.now(); + const now = try std.time.Instant.now(); // Set a limit of one desktop notification per second so that the OS // doesn't kill us when we run out of resources. diff --git a/src/build_config.zig b/src/build_config.zig index f4360620e..8f9df129c 100644 --- a/src/build_config.zig +++ b/src/build_config.zig @@ -127,18 +127,20 @@ pub const Artifact = enum { wasm_module, pub fn detect() Artifact { - // assert(builtin.output_mode == .Obj); - // assert(builtin.link_mode == .Static); - return .wasm_module; + if (builtin.target.isWasm()) { + // assert(builtin.output_mode == .Obj); + // assert(builtin.link_mode == .Static); + return .wasm_module; + } - // return switch (builtin.output_mode) { - // .Exe => .exe, - // .Lib => .lib, - // else => { - // @compileLog(builtin.output_mode); - // @compileError("unsupported artifact output mode"); - // }, - // }; + return switch (builtin.output_mode) { + .Exe => .exe, + .Lib => .lib, + else => { + @compileLog(builtin.output_mode); + @compileError("unsupported artifact output mode"); + }, + }; } }; diff --git a/src/input/Link.zig b/src/input/Link.zig index eeec2ac71..a0b63f5dd 100644 --- a/src/input/Link.zig +++ b/src/input/Link.zig @@ -8,8 +8,6 @@ const oni = @import("oniguruma"); const Mods = @import("key.zig").Mods; const builtin = @import("builtin"); -pub const Regex = oni.Regex; - /// The regular expression that will be used to match the link. Ownership /// of this memory is up to the caller. The link will never free this memory. regex: []const u8, diff --git a/src/os/main.zig b/src/os/main.zig index afafbd145..796b27536 100644 --- a/src/os/main.zig +++ b/src/os/main.zig @@ -44,16 +44,3 @@ pub const clickInterval = mouse.clickInterval; pub const open = openpkg.open; pub const pipe = pipepkg.pipe; pub const resourcesDir = resourcesdir.resourcesDir; -pub const Instant = if (true) std.time.Instant else struct { - pub fn now() !@This() { - return .{}; - } - pub fn order(self: *const Instant, other: Instant) std.math.Order { - _ = self; - _ = other; - return .eq; - } -}; -pub fn sleep(nanosecond: u64) void { - _ = nanosecond; -} diff --git a/src/renderer/link.zig b/src/renderer/link.zig index d2de78b59..994190ec8 100644 --- a/src/renderer/link.zig +++ b/src/renderer/link.zig @@ -1,20 +1,19 @@ const std = @import("std"); const Allocator = std.mem.Allocator; +const oni = @import("oniguruma"); const configpkg = @import("../config.zig"); const inputpkg = @import("../input.zig"); const terminal = @import("../terminal/main.zig"); const point = terminal.point; const Screen = terminal.Screen; const Terminal = terminal.Terminal; -const builtin = @import("builtin"); -const js = @import("zig-js"); const log = std.log.scoped(.renderer_link); /// The link configuration needed for renderers. pub const Link = struct { /// The regular expression to match the link against. - regex: inputpkg.Link.Regex, + regex: oni.Regex, /// The situations in which the link should be highlighted. highlight: inputpkg.Link.Highlight, diff --git a/src/renderer/opengl/custom.zig b/src/renderer/opengl/custom.zig index 1eb8e6fef..2cab0940c 100644 --- a/src/renderer/opengl/custom.zig +++ b/src/renderer/opengl/custom.zig @@ -2,7 +2,6 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const gl = @import("opengl"); const Size = @import("../size.zig").Size; -const internal_os = @import("../../os/main.zig"); const log = std.log.scoped(.opengl_custom); @@ -48,11 +47,11 @@ pub const State = struct { /// The first time a frame was drawn. This is used to update /// the time uniform. - first_frame_time: internal_os.Instant, + first_frame_time: std.time.Instant, /// The last time a frame was drawn. This is used to update /// the time uniform. - last_frame_time: internal_os.Instant, + last_frame_time: std.time.Instant, pub fn init( alloc: Allocator, @@ -140,8 +139,8 @@ pub const State = struct { .vao = vao, .ebo = ebo, .fb_texture = fb_tex, - .first_frame_time = try internal_os.Instant.now(), - .last_frame_time = try internal_os.Instant.now(), + .first_frame_time = try std.time.Instant.now(), + .last_frame_time = try std.time.Instant.now(), }; } @@ -184,7 +183,7 @@ pub const State = struct { /// this. pub fn newFrame(self: *State) !void { // Update our frame time - const now = internal_os.Instant.now() catch self.first_frame_time; + const now = std.time.Instant.now() catch self.first_frame_time; const since_ns: f32 = @floatFromInt(now.since(self.first_frame_time)); const delta_ns: f32 = @floatFromInt(now.since(self.last_frame_time)); self.uniforms.time = since_ns / std.time.ns_per_s; diff --git a/src/renderer/opengl/image.zig b/src/renderer/opengl/image.zig index 58bf7681c..599363ff6 100644 --- a/src/renderer/opengl/image.zig +++ b/src/renderer/opengl/image.zig @@ -35,7 +35,7 @@ pub const Placement = struct { /// The map used for storing images. pub const ImageMap = std.AutoHashMapUnmanaged(u32, struct { image: Image, - transmit_time: internal_os.Instant, + transmit_time: std.time.Instant, }); /// The state for a single image that is to be rendered. The image can be diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index 06d6bdd1f..f13b571f4 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -346,7 +346,7 @@ pub const LoadingImage = struct { } // Set our time - self.image.transmit_time = internal_os.Instant.now() catch |err| { + self.image.transmit_time = std.time.Instant.now() catch |err| { log.warn("failed to get time: {}", .{err}); return error.InternalError; }; @@ -453,7 +453,7 @@ pub const Image = struct { format: command.Transmission.Format = .rgb, compression: command.Transmission.Compression = .none, data: []const u8 = "", - transmit_time: internal_os.Instant = undefined, + transmit_time: std.time.Instant = undefined, pub const Error = error{ InternalError, diff --git a/src/terminal/kitty/graphics_storage.zig b/src/terminal/kitty/graphics_storage.zig index 3a4501922..9bfea0ebb 100644 --- a/src/terminal/kitty/graphics_storage.zig +++ b/src/terminal/kitty/graphics_storage.zig @@ -496,7 +496,7 @@ pub const ImageStorage = struct { // bit is fine compared to the megabytes we're looking to save. const Candidate = struct { id: u32, - time: internal_os.Instant, + time: std.time.Instant, used: bool, }; diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 8995ad360..e1347e37c 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -102,7 +102,7 @@ pub fn threadEnter( }; // Track our process start time for abnormal exits - const process_start = try internal_os.Instant.now(); + const process_start = try std.time.Instant.now(); // Create our pipe that we'll use to kill our read thread. // pipe[0] is the read end, pipe[1] is the write end. @@ -345,7 +345,7 @@ fn processExit( // Determine how long the process was running for. const runtime_ms: ?u64 = runtime: { - const process_end = internal_os.Instant.now() catch break :runtime null; + const process_end = std.time.Instant.now() catch break :runtime null; const runtime_ns = process_end.since(execdata.start); const runtime_ms = runtime_ns / std.time.ns_per_ms; break :runtime runtime_ms; @@ -603,7 +603,7 @@ pub const ThreadData = struct { const WRITE_REQ_PREALLOC = std.math.pow(usize, 2, 5); /// Process start time and boolean of whether its already exited. - start: internal_os.Instant, + start: std.time.Instant, exited: bool = false, /// The number of milliseconds below which we consider a process @@ -1288,7 +1288,7 @@ const Subprocess = struct { const res = posix.waitpid(pid, std.c.W.NOHANG); log.debug("waitpid result={}", .{res.pid}); if (res.pid != 0) break; - internal_os.sleep(10 * std.time.ns_per_ms); + std.time.sleep(10 * std.time.ns_per_ms); } }, } @@ -1311,7 +1311,7 @@ const Subprocess = struct { const pgid = c.getpgid(pid); if (pgid == my_pgid) { log.warn("pgid is our own, retrying", .{}); - internal_os.sleep(10 * std.time.ns_per_ms); + std.time.sleep(10 * std.time.ns_per_ms); continue; } diff --git a/src/termio/Termio.zig b/src/termio/Termio.zig index c48071046..567c2b586 100644 --- a/src/termio/Termio.zig +++ b/src/termio/Termio.zig @@ -68,7 +68,7 @@ terminal_stream: terminal.Stream(StreamHandler), /// Last time the cursor was reset. This is used to prevent message /// flooding with cursor resets. -last_cursor_reset: ?internal_os.Instant = null, +last_cursor_reset: ?std.time.Instant = null, /// The configuration for this IO that is derived from the main /// configuration. This must be exported so that we don't need to @@ -552,7 +552,7 @@ fn processOutputLocked(self: *Termio, buf: []const u8) void { // non-blink state so it is rendered if visible. If we're under // HEAVY read load, we don't want to send a ton of these so we // use a timer under the covers - if (internal_os.Instant.now()) |now| cursor_reset: { + if (std.time.Instant.now()) |now| cursor_reset: { if (self.last_cursor_reset) |last| { log.err("now: {} last: {}", .{ now, last }); if (now.since(last) <= (500 * std.time.ns_per_ms)) { From 1ece6f06b397c8853038b6fc3902529faeef04da Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David Date: Sun, 24 Nov 2024 19:17:13 -0500 Subject: [PATCH 10/10] correct eventfd implementation --- example/app.ts | 2 +- example/imports.ts | 20 +++++++++++--------- example/worker.ts | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/example/app.ts b/example/app.ts index ffe9e58e9..ac7940b0f 100644 --- a/example/app.ts +++ b/example/app.ts @@ -94,5 +94,5 @@ fetch(url.href) const io = new Uint8ClampedArray(stdin, 4 + place); io.set(text); Atomics.notify(n, 0); - }, 5000) + }, 10000) }) diff --git a/example/imports.ts b/example/imports.ts index 9cf483ed1..b7b86f5bb 100644 --- a/example/imports.ts +++ b/example/imports.ts @@ -99,7 +99,6 @@ function readStdin() { Atomics.wait(len, 0, 0, 1000); } const length = len[0]; - console.error("stdin", length); if (length === 0) { bytes = null; return; @@ -113,6 +112,7 @@ function sleep(ms: number) { const buf = new SharedArrayBuffer(4); const view = new Int32Array(buf); view[0] = 1; + console.error("sleep", ms); Atomics.wait(view, 0, 1, ms); @@ -732,6 +732,7 @@ export const importObject = { const has = new Int32Array(files.has); Atomics.store(has, fd - fdStart, 1); Atomics.notify(has, fd - fdStart); + if (!mainThread) Atomics.wait(has, fd-fdStart, 1, 1000); console.error("notify", fd); memory.setUint32(nwritten_ptr, nwritten, true); @@ -784,6 +785,10 @@ export const importObject = { nwritten += iov_len; memory.setUint32(nreadPtr, nwritten, true); } + const has = new Int32Array(files.has); + Atomics.store(has, fd - fdStart, 0) + Atomics.notify(has, fd-fdStart); + console.error("read", fd); } else { const memory = new DataView(zjs.memory.buffer); let nwritten = 0; @@ -795,7 +800,6 @@ export const importObject = { const read = Math.min(iov_len, bytes.length); const io = new Uint8ClampedArray(zjs.memory.buffer, iov_base, iov_len); io.set(bytes.slice(0, read)); - console.error(bytes[read-1]); bytes = bytes.slice(read); if (bytes.length === 0) bytes = null; nwritten += read; @@ -803,8 +807,6 @@ export const importObject = { } memory.setUint32(nreadPtr, nwritten, true); - if (nwritten > 0) - console.error("fd_read", nwritten); } }, fd_seek: (...params) => { @@ -940,7 +942,6 @@ export const importObject = { } else { console.warn("notify"); notify = true; - Atomics.store(has, fd - fdStart, 0) } } @@ -973,9 +974,9 @@ export const importObject = { select(SP_PARM->_checkfd + 1, &fdset, NULL, NULL, &ktimeout) */ - if (fd == WASI_STDIN_FILENO && WASI_EVENTTYPE_FD_READ == type) { - sleep(5000); - } + // if (fd == WASI_STDIN_FILENO && WASI_EVENTTYPE_FD_READ == type) { + // sleep(5000); + // } break; } @@ -1064,9 +1065,10 @@ export const importObject = { ...zjs.importObject(), }; +let pid = 100; function spawnWorker(instance) { const worker = new Worker(new URL("worker.ts", import.meta.url), { type: "module" }); - worker.postMessage([zjs.memory, instance, stdin, wasmModule, files]); + worker.postMessage([zjs.memory, instance, stdin, wasmModule, files, pid++]); worker.onmessage = (event) => { const [instance] = event.data; spawnWorker(instance); diff --git a/example/worker.ts b/example/worker.ts index 83b9d9313..9c167e7f3 100644 --- a/example/worker.ts +++ b/example/worker.ts @@ -2,7 +2,7 @@ import { importObject, setFiles, setMainThread, setStdin, zjs } from "./imports" onmessage = async (e) => { console.log("module received from main thread"); - const [memory, instance, stdin, wasmModule, files] = e.data; + const [memory, instance, stdin, wasmModule, files, pid] = e.data; console.log(wasmModule) setStdin(stdin); setMainThread(false); @@ -10,5 +10,5 @@ onmessage = async (e) => { importObject.env.memory = memory; const results = await WebAssembly.instantiate(wasmModule, importObject); zjs.memory = memory; - results.exports.wasi_thread_start(100, instance); + results.exports.wasi_thread_start(pid, instance); };