mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-09-05 19:08:17 +00:00
core, gtk: implement host resources dir for Flatpak (#6661)
Introduces host resources directory as a new concept: A directory containing application resources that can only be accessed from the host operating system. This is significant for sandboxed application runtimes like Flatpak where shells spawned on the host should have access to application resources to enable integrations. Alongside this, apprt is now allowed to override the resources lookup logic.
This commit is contained in:
@@ -546,7 +546,7 @@ pub fn init(
|
||||
.shell_integration = config.@"shell-integration",
|
||||
.shell_integration_features = config.@"shell-integration-features",
|
||||
.working_directory = config.@"working-directory",
|
||||
.resources_dir = global_state.resources_dir,
|
||||
.resources_dir = global_state.resources_dir.host(),
|
||||
.term = config.term,
|
||||
|
||||
// Get the cgroup if we're on linux and have the decl. I'd love
|
||||
|
@@ -1,2 +1,4 @@
|
||||
const internal_os = @import("../os/main.zig");
|
||||
pub const resourcesDir = internal_os.resourcesDir;
|
||||
pub const App = struct {};
|
||||
pub const Window = struct {};
|
||||
|
@@ -23,6 +23,8 @@ const Config = configpkg.Config;
|
||||
|
||||
const log = std.log.scoped(.embedded_window);
|
||||
|
||||
pub const resourcesDir = internal_os.resourcesDir;
|
||||
|
||||
pub const App = struct {
|
||||
/// Because we only expect the embedding API to be used in embedded
|
||||
/// environments, the options are extern so that we can expose it
|
||||
|
@@ -35,6 +35,8 @@ const darwin_enabled = builtin.target.os.tag.isDarwin() and
|
||||
|
||||
const log = std.log.scoped(.glfw);
|
||||
|
||||
pub const resourcesDir = internal_os.resourcesDir;
|
||||
|
||||
pub const App = struct {
|
||||
app: *CoreApp,
|
||||
config: Config,
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
pub const App = @import("gtk/App.zig");
|
||||
pub const Surface = @import("gtk/Surface.zig");
|
||||
pub const resourcesDir = @import("gtk/flatpak.zig").resourcesDir;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
|
29
src/apprt/gtk/flatpak.zig
Normal file
29
src/apprt/gtk/flatpak.zig
Normal file
@@ -0,0 +1,29 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const build_config = @import("../../build_config.zig");
|
||||
const internal_os = @import("../../os/main.zig");
|
||||
const glib = @import("glib");
|
||||
|
||||
pub fn resourcesDir(alloc: Allocator) !internal_os.ResourcesDir {
|
||||
if (comptime build_config.flatpak) {
|
||||
// Only consult Flatpak runtime data for host case.
|
||||
if (internal_os.isFlatpak()) {
|
||||
var result: internal_os.ResourcesDir = .{
|
||||
.app_path = try alloc.dupe(u8, "/app/share/ghostty"),
|
||||
};
|
||||
errdefer alloc.free(result.app_path.?);
|
||||
|
||||
const keyfile = glib.KeyFile.new();
|
||||
defer keyfile.unref();
|
||||
|
||||
if (keyfile.loadFromFile("/.flatpak-info", .{}, null) == 0) return result;
|
||||
const app_dir = std.mem.span(keyfile.getString("Instance", "app-path", null)) orelse return result;
|
||||
defer glib.free(app_dir.ptr);
|
||||
|
||||
result.host_path = try std.fs.path.join(alloc, &[_][]const u8{ app_dir, "share", "ghostty" });
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return try internal_os.resourcesDir(alloc);
|
||||
}
|
@@ -1,2 +1,4 @@
|
||||
const internal_os = @import("../os/main.zig");
|
||||
pub const resourcesDir = internal_os.resourcesDir;
|
||||
pub const App = struct {};
|
||||
pub const Surface = struct {};
|
||||
|
@@ -115,7 +115,8 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
if (global_state.resources_dir == null)
|
||||
const resources_dir = global_state.resources_dir.app();
|
||||
if (resources_dir == null)
|
||||
try stderr.print("Could not find the Ghostty resources directory. Please ensure " ++
|
||||
"that Ghostty is installed correctly.\n", .{});
|
||||
|
||||
|
@@ -56,7 +56,7 @@ pub const Location = enum {
|
||||
},
|
||||
|
||||
.resources => try std.fs.path.join(arena_alloc, &.{
|
||||
global_state.resources_dir orelse return null,
|
||||
global_state.resources_dir.app() orelse return null,
|
||||
"themes",
|
||||
}),
|
||||
};
|
||||
|
@@ -9,6 +9,7 @@ const harfbuzz = @import("harfbuzz");
|
||||
const oni = @import("oniguruma");
|
||||
const crash = @import("crash/main.zig");
|
||||
const renderer = @import("renderer.zig");
|
||||
const apprt = @import("apprt.zig");
|
||||
|
||||
/// We export the xev backend we want to use so that the rest of
|
||||
/// Ghostty can import this once and have access to the proper
|
||||
@@ -35,7 +36,7 @@ pub const GlobalState = struct {
|
||||
|
||||
/// The app resources directory, equivalent to zig-out/share when we build
|
||||
/// from source. This is null if we can't detect it.
|
||||
resources_dir: ?[]const u8,
|
||||
resources_dir: internal_os.ResourcesDir,
|
||||
|
||||
/// Where logging should go
|
||||
pub const Logging = union(enum) {
|
||||
@@ -62,7 +63,7 @@ pub const GlobalState = struct {
|
||||
.action = null,
|
||||
.logging = .{ .stderr = {} },
|
||||
.rlimits = .{},
|
||||
.resources_dir = null,
|
||||
.resources_dir = .{},
|
||||
};
|
||||
errdefer self.deinit();
|
||||
|
||||
@@ -170,11 +171,11 @@ pub const GlobalState = struct {
|
||||
|
||||
// Find our resources directory once for the app so every launch
|
||||
// hereafter can use this cached value.
|
||||
self.resources_dir = try internal_os.resourcesDir(self.alloc);
|
||||
errdefer if (self.resources_dir) |dir| self.alloc.free(dir);
|
||||
self.resources_dir = try apprt.runtime.resourcesDir(self.alloc);
|
||||
errdefer self.resources_dir.deinit(self.alloc);
|
||||
|
||||
// Setup i18n
|
||||
if (self.resources_dir) |v| internal_os.i18n.init(v) catch |err| {
|
||||
if (self.resources_dir.app()) |v| internal_os.i18n.init(v) catch |err| {
|
||||
std.log.warn("failed to init i18n, translations will not be available err={}", .{err});
|
||||
};
|
||||
}
|
||||
@@ -182,7 +183,7 @@ pub const GlobalState = struct {
|
||||
/// Cleans up the global state. This doesn't _need_ to be called but
|
||||
/// doing so in dev modes will check for memory leaks.
|
||||
pub fn deinit(self: *GlobalState) void {
|
||||
if (self.resources_dir) |dir| self.alloc.free(dir);
|
||||
self.resources_dir.deinit(self.alloc);
|
||||
|
||||
// Flush our crash logs
|
||||
crash.deinit();
|
||||
|
@@ -56,6 +56,7 @@ pub const open = openpkg.open;
|
||||
pub const OpenType = openpkg.Type;
|
||||
pub const pipe = pipepkg.pipe;
|
||||
pub const resourcesDir = resourcesdir.resourcesDir;
|
||||
pub const ResourcesDir = resourcesdir.ResourcesDir;
|
||||
pub const ShellEscapeWriter = shell.ShellEscapeWriter;
|
||||
|
||||
test {
|
||||
|
@@ -2,13 +2,42 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const ResourcesDir = struct {
|
||||
/// Avoid accessing these directly, use the app() and host() methods instead.
|
||||
app_path: ?[]const u8 = null,
|
||||
host_path: ?[]const u8 = null,
|
||||
|
||||
/// Free resources held. Requires the same allocator as when resourcesDir()
|
||||
/// is called.
|
||||
pub fn deinit(self: *ResourcesDir, alloc: Allocator) void {
|
||||
if (self.app_path) |p| alloc.free(p);
|
||||
if (self.host_path) |p| alloc.free(p);
|
||||
}
|
||||
|
||||
/// Get the directory to the bundled resources directory accessible
|
||||
/// by the application.
|
||||
pub fn app(self: *ResourcesDir) ?[]const u8 {
|
||||
return self.app_path;
|
||||
}
|
||||
|
||||
/// Get the directory to the bundled resources directory accessible
|
||||
/// by the host environment (i.e. for sandboxed applications). The
|
||||
/// returned directory might not be accessible from the application
|
||||
/// itself.
|
||||
///
|
||||
/// In non-sandboxed environment, this should be the same as app().
|
||||
pub fn host(self: *ResourcesDir) ?[]const u8 {
|
||||
return self.host_path orelse self.app_path;
|
||||
}
|
||||
};
|
||||
|
||||
/// Gets the directory to the bundled resources directory, if it
|
||||
/// exists (not all platforms or packages have it). The output is
|
||||
/// owned by the caller.
|
||||
///
|
||||
/// This is highly Ghostty-specific and can likely be generalized at
|
||||
/// some point but we can cross that bridge if we ever need to.
|
||||
pub fn resourcesDir(alloc: std.mem.Allocator) !?[]const u8 {
|
||||
pub fn resourcesDir(alloc: Allocator) !ResourcesDir {
|
||||
// Use the GHOSTTY_RESOURCES_DIR environment variable in release builds.
|
||||
//
|
||||
// In debug builds we try using terminfo detection first instead, since
|
||||
@@ -20,7 +49,7 @@ pub fn resourcesDir(alloc: std.mem.Allocator) !?[]const u8 {
|
||||
// freed, do not try to use internal_os.getenv or posix getenv.
|
||||
if (comptime builtin.mode != .Debug) {
|
||||
if (std.process.getEnvVarOwned(alloc, "GHOSTTY_RESOURCES_DIR")) |dir| {
|
||||
if (dir.len > 0) return dir;
|
||||
if (dir.len > 0) return .{ .app_path = dir };
|
||||
} else |err| switch (err) {
|
||||
error.EnvironmentVariableNotFound => {},
|
||||
else => return err,
|
||||
@@ -38,7 +67,7 @@ pub fn resourcesDir(alloc: std.mem.Allocator) !?[]const u8 {
|
||||
|
||||
// Get the path to our running binary
|
||||
var exe_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
var exe: []const u8 = std.fs.selfExePath(&exe_buf) catch return null;
|
||||
var exe: []const u8 = std.fs.selfExePath(&exe_buf) catch return .{};
|
||||
|
||||
// We have an exe path! Climb the tree looking for the terminfo
|
||||
// bundle as we expect it.
|
||||
@@ -50,7 +79,7 @@ pub fn resourcesDir(alloc: std.mem.Allocator) !?[]const u8 {
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
inline for (sentinels) |sentinel| {
|
||||
if (try maybeDir(&dir_buf, dir, "Contents/Resources", sentinel)) |v| {
|
||||
return try std.fs.path.join(alloc, &.{ v, "ghostty" });
|
||||
return .{ .app_path = try std.fs.path.join(alloc, &.{ v, "ghostty" }) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +94,7 @@ pub fn resourcesDir(alloc: std.mem.Allocator) !?[]const u8 {
|
||||
if (builtin.target.os.tag == .freebsd) "local/share" else "share",
|
||||
sentinel,
|
||||
)) |v| {
|
||||
return try std.fs.path.join(alloc, &.{ v, "ghostty" });
|
||||
return .{ .app_path = try std.fs.path.join(alloc, &.{ v, "ghostty" }) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,14 +103,14 @@ pub fn resourcesDir(alloc: std.mem.Allocator) !?[]const u8 {
|
||||
// fallback and use the provided resources dir.
|
||||
if (comptime builtin.mode == .Debug) {
|
||||
if (std.process.getEnvVarOwned(alloc, "GHOSTTY_RESOURCES_DIR")) |dir| {
|
||||
if (dir.len > 0) return dir;
|
||||
if (dir.len > 0) return .{ .app_path = dir };
|
||||
} else |err| switch (err) {
|
||||
error.EnvironmentVariableNotFound => {},
|
||||
else => return err,
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return .{};
|
||||
}
|
||||
|
||||
/// Little helper to check if the "base/sub/suffix" directory exists and
|
||||
|
Reference in New Issue
Block a user