Skip to content

Commit 4982c0d

Browse files
committed
core, gtk: implement host resources dir for Flatpak
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.
1 parent 1b52d5c commit 4982c0d

File tree

5 files changed

+49
-3
lines changed

5 files changed

+49
-3
lines changed

src/Surface.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ pub fn init(
543543
.shell_integration = config.@"shell-integration",
544544
.shell_integration_features = config.@"shell-integration-features",
545545
.working_directory = config.@"working-directory",
546-
.resources_dir = global_state.resources_dir,
546+
.resources_dir = global_state.host_resources_dir,
547547
.term = config.term,
548548

549549
// Get the cgroup if we're on linux and have the decl. I'd love

src/apprt/gtk.zig

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
pub const App = @import("gtk/App.zig");
44
pub const Surface = @import("gtk/Surface.zig");
5+
pub const resourcesDir = @import("gtk/flatpak.zig").resourcesDir;
56

67
test {
78
@import("std").testing.refAllDecls(@This());

src/apprt/gtk/flatpak.zig

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const std = @import("std");
2+
const Allocator = std.mem.Allocator;
3+
const build_config = @import("../../build_config.zig");
4+
const internal_os = @import("../../os/main.zig");
5+
const glib = @import("glib");
6+
7+
pub fn resourcesDir(alloc: Allocator, hostAccessible: bool) !?[]const u8 {
8+
if (comptime build_config.flatpak) {
9+
// Only consult Flatpak runtime data for host case.
10+
if (hostAccessible and internal_os.isFlatpak()) {
11+
const keyfile = glib.KeyFile.new();
12+
defer keyfile.unref();
13+
14+
if (keyfile.loadFromFile("/.flatpak-info", .{}, null) == 0) return null;
15+
const app_dir = std.mem.span(keyfile.getString("Instance", "app-path", null)) orelse return null;
16+
defer glib.free(app_dir.ptr);
17+
18+
return try std.fs.path.join(alloc, &[_][]const u8{ app_dir, "share", "ghostty" });
19+
}
20+
}
21+
22+
return try internal_os.resourcesDir(alloc, hostAccessible);
23+
}

src/global.zig

+17-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const harfbuzz = @import("harfbuzz");
99
const oni = @import("oniguruma");
1010
const crash = @import("crash/main.zig");
1111
const renderer = @import("renderer.zig");
12+
const apprt = @import("apprt.zig");
1213

1314
/// We export the xev backend we want to use so that the rest of
1415
/// Ghostty can import this once and have access to the proper
@@ -37,6 +38,10 @@ pub const GlobalState = struct {
3738
/// from source. This is null if we can't detect it.
3839
resources_dir: ?[]const u8,
3940

41+
/// The app resources directory as seen from the host file system. Except for
42+
/// sandboxed cases, this should be the same as resources_dir.
43+
host_resources_dir: ?[]const u8,
44+
4045
/// Where logging should go
4146
pub const Logging = union(enum) {
4247
disabled: void,
@@ -63,6 +68,7 @@ pub const GlobalState = struct {
6368
.logging = .{ .stderr = {} },
6469
.rlimits = .{},
6570
.resources_dir = null,
71+
.host_resources_dir = null,
6672
};
6773
errdefer self.deinit();
6874

@@ -170,9 +176,18 @@ pub const GlobalState = struct {
170176

171177
// Find our resources directory once for the app so every launch
172178
// hereafter can use this cached value.
173-
self.resources_dir = try internal_os.resourcesDir(self.alloc);
179+
self.resources_dir = rd: {
180+
if (@hasDecl(apprt.runtime, "resourcesDir")) break :rd try apprt.runtime.resourcesDir(self.alloc, false);
181+
break :rd try internal_os.resourcesDir(self.alloc, false);
182+
};
174183
errdefer if (self.resources_dir) |dir| self.alloc.free(dir);
175184

185+
self.host_resources_dir = rd: {
186+
if (@hasDecl(apprt.runtime, "resourcesDir")) break :rd try apprt.runtime.resourcesDir(self.alloc, true);
187+
break :rd try internal_os.resourcesDir(self.alloc, true);
188+
};
189+
errdefer if (self.host_resources_dir) |dir| self.alloc.free(dir);
190+
176191
// Setup i18n
177192
if (self.resources_dir) |v| internal_os.i18n.init(v) catch |err| {
178193
std.log.warn("failed to init i18n, translations will not be available err={}", .{err});
@@ -183,6 +198,7 @@ pub const GlobalState = struct {
183198
/// doing so in dev modes will check for memory leaks.
184199
pub fn deinit(self: *GlobalState) void {
185200
if (self.resources_dir) |dir| self.alloc.free(dir);
201+
if (self.host_resources_dir) |dir| self.alloc.free(dir);
186202

187203
// Flush our crash logs
188204
crash.deinit();

src/os/resourcesdir.zig

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@ const Allocator = std.mem.Allocator;
66
/// exists (not all platforms or packages have it). The output is
77
/// owned by the caller.
88
///
9+
/// `hostAccessible` should be set to true to obtain directory accessible
10+
/// from host environment (i.e. for sandboxed application). The returned
11+
/// directory might not be accesible from the application itself.
12+
///
913
/// This is highly Ghostty-specific and can likely be generalized at
1014
/// some point but we can cross that bridge if we ever need to.
11-
pub fn resourcesDir(alloc: std.mem.Allocator) !?[]const u8 {
15+
pub fn resourcesDir(alloc: std.mem.Allocator, hostAccessible: bool) !?[]const u8 {
16+
_ = hostAccessible;
17+
1218
// Use the GHOSTTY_RESOURCES_DIR environment variable in release builds.
1319
//
1420
// In debug builds we try using terminfo detection first instead, since

0 commit comments

Comments
 (0)