From e85a085e419f6999272fe5345afa1f0fcb59de5c Mon Sep 17 00:00:00 2001 From: Kalle Carlbark Date: Tue, 20 Aug 2024 07:59:09 +0200 Subject: [PATCH] wip: kitty image graphics --- build.zig | 12 +++++ build.zig.zon | 6 ++- src/macos.zig | 79 ++++++++++++++++++++++++++++++ src/main.zig | 133 +++++++++++++++++++++++++++++++------------------- src/root.zig | 32 ++++++++++++ 5 files changed, 212 insertions(+), 50 deletions(-) create mode 100644 src/macos.zig diff --git a/build.zig b/build.zig index fd2a83e..3243b0a 100644 --- a/build.zig +++ b/build.zig @@ -17,6 +17,8 @@ pub fn build(b: *std.Build) void { const vaxis_dep = b.dependency("vaxis", .{ .target = target, .optimize = optimize }); const vaxis = vaxis_dep.module("vaxis"); + const objc_dep = b.dependency("objc", .{ .target = target, .optimize = optimize }); + const objc = objc_dep.module("objc"); const lib = b.addStaticLibrary(.{ .name = "infoz", @@ -26,11 +28,20 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); + const macos = b.addStaticLibrary(.{ + .name = "infoz", + // In this case the main source file is merely a path, however, in more + // complicated build scripts, this could be a generated file. + .root_source_file = b.path("src/macos.zig"), + .target = target, + .optimize = optimize, + }); // This declares intent for the library to be installed into the standard // location when the user invokes the "install" step (the default step when // running `zig build`). b.installArtifact(lib); + b.installArtifact(macos); const exe = b.addExecutable(.{ .name = "infoz", @@ -40,6 +51,7 @@ pub fn build(b: *std.Build) void { }); exe.root_module.addImport("vaxis", vaxis); + exe.root_module.addImport("objc", objc); // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default diff --git a/build.zig.zon b/build.zig.zon index 1aa47e1..940dd0e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -25,7 +25,11 @@ .dependencies = .{ .vaxis = .{ .url = "https://github.com/rockorager/libvaxis/archive/refs/heads/main.tar.gz", - .hash = "1220dab128b93a06ebca095bd38747f50cf9762b524e39e76de8771685264fd16841", + .hash = "12209fb0db1467d24882f4a0fca367d25d7ef7353882270ba3b76382c185332c52ae", + }, + .objc = .{ + .url = "https://github.com/mitchellh/zig-objc/archive/refs/heads/main.tar.gz", + .hash = "122034b3e15d582d8d101a7713e5f13c872b8b8eb6d9cb47515b8e34ee75e122630d", }, }, .paths = .{ diff --git a/src/macos.zig b/src/macos.zig new file mode 100644 index 0000000..e0fcacc --- /dev/null +++ b/src/macos.zig @@ -0,0 +1,79 @@ +const objc = @import("objc"); +const std = @import("std"); +const utsname = @cImport({ + @cInclude("sys/utsname.h"); +}); +const OsVersion = struct { + name: []const u8, + major: i64, +}; + +pub fn macosVersionAtLeast(major: i64, minor: i64, patch: i64) bool { + // Get the objc class from the runtime + const NSProcessInfo = objc.getClass("NSProcessInfo").?; + + // Call a class method with no arguments that returns another objc object. + const info = NSProcessInfo.msgSend(objc.Object, "processInfo", .{}); + + // var NSOperatingSystemVersion = objc.getClass("NSOperatingSystemVersion").?; + + // const os = info.msgSend(objc.Object, "") + + // Call an instance method that returns a boolean and takes a single + // argument. + return info.msgSend(bool, "isOperatingSystemAtLeastVersion:", .{ + NSOperatingSystemVersion{ .major = major, .minor = minor, .patch = patch }, + }); +} + +pub fn macos_version(allocator: std.mem.Allocator) ![]const u8 { + const NSProcessInfo = objc.getClass("NSProcessInfo").?; + + // Call a class method with no arguments that returns another objc object. + const info = NSProcessInfo.msgSend(objc.Object, "processInfo", .{}); + + const koko = info.msgSend(NSOperatingSystemVersion, "operatingSystemVersion", .{}); + + const buf = try std.fmt.allocPrint(allocator, "\n\t\t\tOS: {s} {d}.{d}.{d}", .{ version_to_name(koko), koko.major, koko.minor, koko.patch }); + + return buf; +} + +pub fn macos_kernel_version(allocator: std.mem.Allocator) ![]const u8 { + var name: utsname.struct_utsname = undefined; + + if (utsname.uname(&name) != 0) { + return error.UnameFailed; + } + + const kaka = std.mem.span(@ptrCast(name.sysname)); + + const buf = try std.fmt.allocPrint(allocator, "\n\t\t\tKernel: {s} {s}", .{ kaka, name.version }); + + return buf; +} + +fn version_to_name(version: NSOperatingSystemVersion) []const u8 { + switch (version.major) { + 15 => return "Sequoia", + 14 => return "Sonoma", + 13 => return "Ventura", + 12 => return "Monterey", + 11 => return "Big Sur", + 10 => { + switch (version.minor) { + 15 => return "Catalina", + 14 => return "Mojave", + else => return "Unknown", + } + }, + else => return "Unknown", + } +} + +/// This extern struct matches the Cocoa headers for layout. +const NSOperatingSystemVersion = extern struct { + major: i64, + minor: i64, + patch: i64, +}; diff --git a/src/main.zig b/src/main.zig index 9b6d0ff..9ca39ca 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,5 +1,7 @@ const std = @import("std"); const vaxis = @import("vaxis"); +const root = @import("root.zig"); +const macos = @import("macos.zig"); const log = std.log.scoped(.main); @@ -17,6 +19,7 @@ pub fn main() !void { } } + // const stdout = std.io.getStdOut().writer(); const allocator = gpa.allocator(); var tty = try vaxis.Tty.init(); @@ -25,68 +28,100 @@ pub fn main() !void { var vx = try vaxis.init(allocator, .{}); defer vx.deinit(allocator, tty.anyWriter()); - try vx.resetState(tty.anyWriter()); + vx.caps.unicode = .unicode; + vx.caps.kitty_graphics = true; - var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx }; - try loop.init(); + const os = try macos.macos_version(allocator); + defer allocator.free(os); + const kernel = try macos.macos_kernel_version(allocator); + defer allocator.free(kernel); - try loop.start(); - defer loop.stop(); + // try tty.anyWriter().writeAll(vaxis.ctlseqs.kitty_graphics_clear); + try tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_set); + defer vx.queryTerminalSend(tty.anyWriter()) catch {}; + defer tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_reset) catch {}; - const stdout_file = std.io.getStdOut().writer(); - // try stdout_file.writeAll("\x1b[2J"); + var buf_writer = tty.bufferedWriter(); + const writer = buf_writer.writer().any(); + + const winsize: vaxis.Winsize = try vaxis.Tty.getWinsize(tty.fd); + try vx.resize(allocator, tty.anyWriter(), winsize); + + const win = vx.window(); + + win.clear(); + var result: vaxis.Window.PrintResult = .{ .col = 0, .row = 0, .overflow = false }; + // try tty.anyWriter().writeAll(vaxis.ctlseqs.kitty_graphics_query); + // var img1 = try vaxis.zigimg.Image.fromFilePath(allocator, "/Users/kc/Sync/Images/apple-white.png"); + // defer img1.deinit(); + // const koko = try vx.transmitImage(allocator, tty.anyWriter(), &img1, .png); + // win.hideCursor(); + // win.showCursor(10, 1); + const system_info = [_]vaxis.Cell.Segment{ + .{ .text = os }, + .{ .text = kernel }, + .{ .text = "\t\t\tKernel: lalal\n" }, + .{ .text = "\t\t\tKernel: lalal\n" }, + .{ .text = "\t\t\tKernel: lalal\n" }, + .{ .text = "\t\t\tKernel: lalal\n" }, + .{ .text = "\t\t\tKernel: lalal\n" }, + .{ .text = "\t\t\tKernel: lalal\n" }, + .{ .text = "\t\t\tKernel: lalal\n" }, + .{ .text = "\t\t\tKernel: lalal\n" }, + }; + try root.kitty_image_inline_copy(allocator, tty.anyWriter(), "/Users/kc/Sync/Images/apple-dark.png"); + try tty.anyWriter().print("\x1b[10;1H", .{}); + try tty.anyWriter().print("\r\t\tTEST: testest\n", .{}); + try tty.anyWriter().print("\r\t\tTEST: testest\n", .{}); + try tty.anyWriter().print("\r\t\tTEST: testest\n", .{}); + try tty.anyWriter().print("\r\t\tTEST: testest\n", .{}); + try tty.anyWriter().print("\r\t\tTEST: testest\n", .{}); + + result = try win.print(&system_info, .{ .wrap = .word }); + + try vx.prettyPrint(writer); + // const dimensions = try koko.cellSize(win); + + // const image = try vx.transmitLocalImagePath(allocator, tty.anyWriter(), "/Users/kc/Sync/Images/apple-white.png", dimensions.rows, dimensions.cols, .file, .png); + // defer vx.freeImage(tty.anyWriter(), image.id); + // try root.kitty_image_inline_copy(allocator, tty.anyWriter(), "/Users/kc/Sync/Images/apple-white.png"); + // try image.draw(win, .{}); + + // try vx.render(tty.anyWriter()); + try buf_writer.flush(); + // try vx.queryTerminalSend(tty.anyWriter()); + + // Clear from cursor to beginning of the screen + // try stdout.writeAll("\x1b[1J"); + // Move cursor to the upper left corner of the screen + // try stdout.writeAll("\x1b[1;1H"); // try stdout_file.writeAll("\x1b[9B"); // try vx.enterAltScreen(tty.anyWriter()); - try vx.queryTerminal(tty.anyWriter(), 1 * std.time.ns_per_s); - const image = [_]vaxis.Image{ - try vx.loadImage(allocator, tty.anyWriter(), .{ .path = "/Users/kc/Sync/Images/apple-dark.png" }), - // vaxis.Image.DrawOptions{ .clip_region = .{ .x = 0, .y = 0 } }, - }; + // var img2 = try vaxis. + // const koko = try vx.loadImage(allocator, writer, .{ .path = "/Users/kc/Sync/Images/apple-white.png" }); + // const koko = try vx.transmitImage(allocator, writer, &img1, .png); + // const image = [_]vaxis.Image{ + // // try vx.transmitImage(allocator, writer, &img1, .png), + // try vx.loadImage(allocator, writer, .{ .path = "/Users/kc/Sync/Images/apple-white.png" }), + // // vaxis.Image.DrawOptions{ .clip_region = .{ .x = 0, .y = 0 } }, + // }; - // defer vx.freeImage(tty.anyWriter(), image[0].id); - - const event = loop.nextEvent(); - switch (event) { - .winsize => |ws| try vx.resize(allocator, tty.anyWriter(), ws), - else => {}, - } - _ = loop.nextEvent(); - const win = vx.window(); - // win.clear(); - // vx.refresh = true; // win.hideCursor(); - // _ = loop.nextEvent(); + // const dims = try image[0].cellSize(win); - const dims = try image[0].cellSize(win); + // const logo_win = win.child(.{ .y_off = 0, .x_off = 0, .width = .{ .limit = dims.cols }, .height = .{ .limit = dims.rows } }); + // const logo_win = win.child(.{ .y_off = 0, .x_off = 0 }); + // const os_info = win.child(.{ .y_off = 0, .x_off = dims.cols, .width = .{ .limit = 30 }, .height = .{ .limit = 10 } }); - // try stdout_file.print("{d} - {d}\n", .{ dims.cols, dims.rows }); + // try vx.render(writer); + // try stdout.print("\n\n\n\n\n\n\n\n\n", .{}); - const logo_win = win.child(.{ .y_off = 0, .x_off = 0, .width = .{ .limit = dims.cols }, .height = .{ .limit = dims.rows } }); - logo_win.clear(); - const os_info = win.child(.{ .y_off = 0, .x_off = dims.cols, .width = .{ .limit = 30 }, .height = .{ .limit = 8 } }); - - const system_info = [_]vaxis.Cell.Segment{ - .{ .text = "\tOS: lalal\n" }, - .{ .text = "\tKernel: lalal\n" }, - .{ .text = "\tKernel: lalal\n" }, - .{ .text = "\tKernel: lalal\n" }, - .{ .text = "\tKernel: lalal\n" }, - .{ .text = "\tKernel: lalal\n" }, - .{ .text = "\tKernel: lalal\n" }, - .{ .text = "\tKernel: lalal\n" }, - .{ .text = "\tKernel: lalal\n" }, - .{ .text = "\tKernel: lalal\n" }, - }; - - try image[0].draw(logo_win, .{}); - _ = try os_info.print(&system_info, .{ .wrap = .word }); - try vx.render(tty.anyWriter()); - // try stdout_file.writeAll("\x1b[9B"); - try stdout_file.print("\n\n\n\n\n\n\n\n\n", .{}); + // try writer.writeFile() + // try stdout.writeAll("\x1b[m"); // win.showCursor(); - std.time.sleep(10000000); + // std.time.sleep(10000000); } test "simple test" { diff --git a/src/root.zig b/src/root.zig index ecfeade..f9bca5d 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1,10 +1,42 @@ const std = @import("std"); const testing = std.testing; +const vaxis = @import("vaxis"); +const base64Encoder = std.base64.standard.Encoder; export fn add(a: i32, b: i32) i32 { return a + b; } +pub fn kitty_image_inline_copy(allocator: std.mem.Allocator, tty: std.io.AnyWriter, path: []const u8) !void { + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + + // const png_buf = try arena.allocator().alloc(u8, img.imageByteSize()); + // const png = try img.writeToMemory(png_buf, .{ .png = .{} }); + + const b64_buf = try arena.allocator().alloc(u8, base64Encoder.calcSize(path.len)); + const encoded = base64Encoder.encode(b64_buf, path); + + try tty.print("\x1b_Ga=T,q=2,f=100,t=f,p=0,s=267,v=314;{s}\x1b\\", .{encoded}); + try tty.print("\x1b\\", .{}); + + // if (encoded.len <= 4096) { + // try tty.print("\x1b_Ga=T,q=2,f=100,t=f,s=267,v=314,m=1;{s}\x1b\\", .{encoded}); + // } else { + // var n: usize = 4096; + + // try tty.print("\x1b_Ga=T,q=2,f=100,t=f,s=267,v=314,m=1;{s}\x1b\\", .{encoded[0..n]}); + + // while (n < encoded.len) : (n += 4096) { + // const end: usize = @min(n + 4096, encoded.len); + // const m: u2 = if (end == encoded.len) 0 else 1; + // try tty.print("\x1b_Gm={d};{s}\x1b\\", .{ m, encoded[n..end] }); + // } + // } + + // try tty.print("_Ga=T,q=2,f=100,t=f,s=267,v=314;{s}\\", .{encoded}); +} + test "basic add functionality" { try testing.expect(add(3, 7) == 10); }