From 52d22a140cf35e2d6468351bea7548929e4df7f2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 27 Feb 2023 11:26:31 -0800 Subject: [PATCH] termio: exec uses new flatpak command, no more host-spawn --- build.zig | 18 ++++++++-- com.mitchellh.ghostty.yml | 22 +----------- src/build_config.zig | 3 ++ src/passwd.zig | 11 ++++-- src/termio/Exec.zig | 76 ++++++++++++++++++++++++++++++++++----- 5 files changed, 94 insertions(+), 36 deletions(-) diff --git a/build.zig b/build.zig index 9f0b44766..794d261b5 100644 --- a/build.zig +++ b/build.zig @@ -46,6 +46,7 @@ comptime { var tracy: bool = false; var enable_coretext: bool = false; var enable_fontconfig: bool = false; +var flatpak: bool = false; var app_runtime: apprt.Runtime = .none; pub fn build(b: *std.build.Builder) !void { @@ -67,6 +68,12 @@ pub fn build(b: *std.build.Builder) !void { "Enable Tracy integration (default true in Debug on Linux)", ) orelse (optimize == .Debug and target.isLinux()); + flatpak = b.option( + bool, + "flatpak", + "Build for Flatpak (integrates with Flatpak APIs). Only has an effect targeting Linux.", + ) orelse false; + enable_coretext = b.option( bool, "coretext", @@ -123,6 +130,7 @@ pub fn build(b: *std.build.Builder) !void { }); const exe_options = b.addOptions(); exe_options.addOption(bool, "tracy_enabled", tracy); + exe_options.addOption(bool, "flatpak", flatpak); exe_options.addOption(bool, "coretext", enable_coretext); exe_options.addOption(bool, "fontconfig", enable_fontconfig); exe_options.addOption(apprt.Runtime, "app_runtime", app_runtime); @@ -578,6 +586,13 @@ fn addDeps( step.addIncludePath("vendor/glad/include/"); step.addCSourceFile("vendor/glad/src/gl.c", &.{}); + // When we're targeting flatpak we ALWAYS link GTK so we + // get access to glib for dbus. + if (flatpak) { + step.linkSystemLibrary("gtk4"); + step.addLibraryPath("/usr/lib/aarch64-linux-gnu"); + } + switch (app_runtime) { .none => {}, @@ -604,9 +619,6 @@ fn addDeps( try glfw.link(b, step, glfw_opts); step.linkSystemLibrary("gtk4"); - - // This is for Flatpak - step.addLibraryPath("/usr/lib/aarch64-linux-gnu"); }, } } diff --git a/com.mitchellh.ghostty.yml b/com.mitchellh.ghostty.yml index b369c0807..d5a33ae7a 100644 --- a/com.mitchellh.ghostty.yml +++ b/com.mitchellh.ghostty.yml @@ -41,30 +41,10 @@ modules: only-arches: - aarch64 - # We use this to get a proper PTY on our host spawn. We should eventually - # replace this with using dbus directly. - - name: host-spawn - buildsystem: simple - build-commands: - - mkdir -p /app/bin - - mv ./host-spawn-* /app/bin/host-spawn - - chmod +x /app/bin/host-spawn - sources: - - type: file - url: https://github.com/1player/host-spawn/releases/download/1.4.1/host-spawn-x86_64 - sha256: d81bb6125ec73a9a2e26266c48787367fbcb665d67c2e7fb42217f0981106cf7 - only-arches: - - x86_64 - - type: file - url: https://github.com/1player/host-spawn/releases/download/1.4.1/host-spawn-aarch64 - sha256: 29bff846d72e37093b3fdf7859bae16addd64acc98087f8c059548df3c2273c4 - only-arches: - - aarch64 - - name: ghostty buildsystem: simple build-commands: - - MACH_SDK_PATH="$(pwd)/vendor/mach-sdk" zig build -Dcpu=baseline -Dapp-runtime=gtk --prefix /app + - MACH_SDK_PATH="$(pwd)/vendor/mach-sdk" zig build -Dcpu=baseline -Dflatpak=true -Dapp-runtime=gtk --prefix /app sources: - type: dir path: . diff --git a/src/build_config.zig b/src/build_config.zig index 05aa38825..27575c79d 100644 --- a/src/build_config.zig +++ b/src/build_config.zig @@ -22,6 +22,9 @@ pub const app_runtime = std.meta.stringToEnum( /// compiled. pub const devmode_enabled = artifact == .exe and app_runtime == .glfw; +/// We want to integrate with Flatpak APIs. +pub const flatpak = options.flatpak; + pub const Artifact = enum { /// Standalone executable exe, diff --git a/src/passwd.zig b/src/passwd.zig index f334341f5..e492d120a 100644 --- a/src/passwd.zig +++ b/src/passwd.zig @@ -1,5 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); +const build_config = @import("build_config.zig"); const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const internal_os = @import("os/main.zig"); @@ -49,8 +50,13 @@ pub fn get(alloc: Allocator) !Entry { // If we're in flatpak then our entry is always empty so we grab it // by shelling out to the host. note that we do HAVE an entry in the // sandbox but only the username is correct. - if (internal_os.isFlatpak()) { - log.info("flatpak detected, will use host-spawn to get our entry", .{}); + if (internal_os.isFlatpak()) flatpak: { + if (comptime !build_config.flatpak) { + log.warn("flatpak detected, but this build doesn't contain flatpak support", .{}); + break :flatpak; + } + + log.info("flatpak detected, will use host command to get our entry", .{}); // Note: we wrap our getent call in a /bin/sh login shell because // some operating systems (NixOS tested) don't set the PATH for various @@ -104,7 +110,6 @@ pub fn get(alloc: Allocator) !Entry { break :output try output.toOwnedSlice(alloc); }; - log.warn("DONE output={s}", .{output}); // Shell and home are the last two entries var it = std.mem.splitBackwards(u8, std.mem.trimRight(u8, output, " \r\n"), ":"); diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 61eb98d63..39cb47e4e 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -322,6 +322,10 @@ fn ttyWrite( /// Subprocess manages the lifecycle of the shell subprocess. const Subprocess = struct { + /// If we build with flatpak support then we have to keep track of + /// a potential execution on the host. + const FlatpakHostCommand = if (build_config.flatpak) internal_os.FlatpakHostCommand else void; + arena: std.heap.ArenaAllocator, cwd: ?[]const u8, env: EnvMap, @@ -331,6 +335,7 @@ const Subprocess = struct { screen_size: renderer.ScreenSize, pty: ?Pty = null, command: ?Command = null, + flatpak_command: ?FlatpakHostCommand = null, /// Initialize the subprocess. This will NOT start it, this only sets /// up the internal state necessary to start it later. @@ -364,8 +369,18 @@ const Subprocess = struct { break :argv0 argv0_buf; } else null; - // Set our env vars - var env = try std.process.getEnvMap(alloc); + // Set our env vars. For Flatpak builds running in Flatpak we don't + // inherit our environment because the login shell on the host side + // will get it. + var env = env: { + if (comptime build_config.flatpak) { + if (internal_os.isFlatpak()) { + break :env std.process.EnvMap.init(alloc); + } + } + + break :env try std.process.getEnvMap(alloc); + }; errdefer env.deinit(); try env.put("TERM", "xterm-256color"); try env.put("COLORTERM", "truecolor"); @@ -394,13 +409,9 @@ const Subprocess = struct { var args = try std.ArrayList([]const u8).initCapacity(alloc, 8); defer args.deinit(); - // We use host-spawn so the PTY is setup properly. - // future: rewrite host-spawn into pure Zig using dbus and - // we can run this directly. - try args.append("/app/bin/host-spawn"); - try args.append("-pty"); - try args.append("-env"); - try args.append("TERM,COLORTERM"); + // We run our shell wrapped in a /bin/sh login shell because + // some systems do not properly initialize the env vars unless + // we start this way (NixOS!) try args.append("/bin/sh"); try args.append("-l"); try args.append("-c"); @@ -451,6 +462,37 @@ const Subprocess = struct { self.args, }); + // In flatpak, we use the HostCommand to execute our shell. + if (internal_os.isFlatpak()) flatpak: { + if (comptime !build_config.flatpak) { + log.warn("flatpak detected, but flatpak support not built-in", .{}); + break :flatpak; + } + + // For flatpak our path and argv[0] must match because that is + // used for execution by the dbus API. + assert(std.mem.eql(u8, self.path, self.args[0])); + + // Flatpak command must have a stable pointer. + self.flatpak_command = .{ + .argv = self.args, + .env = &self.env, + .stdin = pty.slave, + .stdout = pty.slave, + .stderr = pty.slave, + }; + var cmd = &self.flatpak_command.?; + const pid = try cmd.spawn(alloc); + errdefer killCommandFlatpak(cmd); + + log.info("started subcommand on host via flatpak API path={s} pid={?}", .{ + self.path, + pid, + }); + + return pty.master; + } + // Build our subcommand var cmd: Command = .{ .path = self.path, @@ -489,6 +531,17 @@ const Subprocess = struct { self.command = null; } + // Kill our Flatpak command + if (FlatpakHostCommand != void) { + if (self.flatpak_command) |*cmd| { + killCommandFlatpak(cmd) catch |err| + log.err("error sending SIGHUP to command, may hang: {}", .{err}); + _ = cmd.wait() catch |err| + log.err("error waiting for command to exit: {}", .{err}); + self.flatpak_command = null; + } + } + // Close our PTY. We do this after killing our command because on // macOS, close will block until all blocking operations read/write // are done with it and our reader thread is probably still alive. @@ -547,6 +600,11 @@ const Subprocess = struct { } } } + + fn killCommandFlatpak(command: *FlatpakHostCommand) !void { + // TODO + _ = command; + } }; /// The read thread sits in a loop doing the following pseudo code: