mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00
Merge pull request #1324 from mitchellh/libghostty-linux
libghostty: support build on Linux
This commit is contained in:
19
.github/workflows/test.yml
vendored
19
.github/workflows/test.yml
vendored
@ -40,6 +40,25 @@ jobs:
|
|||||||
- name: Test Build
|
- name: Test Build
|
||||||
run: nix develop -c zig build -Dstatic=true -Dapp-runtime=glfw -Dtarget=${{ matrix.target }}
|
run: nix develop -c zig build -Dstatic=true -Dapp-runtime=glfw -Dtarget=${{ matrix.target }}
|
||||||
|
|
||||||
|
build-linux-libghostty:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
|
- uses: cachix/install-nix-action@v24
|
||||||
|
with:
|
||||||
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
- uses: cachix/cachix-action@v14
|
||||||
|
with:
|
||||||
|
name: ghostty
|
||||||
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
|
|
||||||
|
- name: Build Libghostty
|
||||||
|
run: nix develop -c zig build -Dapp-runtime=none
|
||||||
|
|
||||||
build-nix:
|
build-nix:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: test
|
needs: test
|
||||||
|
48
build.zig
48
build.zig
@ -405,7 +405,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// App (Linux)
|
// App (Linux)
|
||||||
if (target.result.os.tag == .linux) {
|
if (target.result.os.tag == .linux and config.app_runtime != .none) {
|
||||||
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
||||||
|
|
||||||
// Desktop file so that we have an icon and other metadata
|
// Desktop file so that we have an icon and other metadata
|
||||||
@ -428,6 +428,52 @@ pub fn build(b: *std.Build) !void {
|
|||||||
b.installFile("images/icons/icon_256x256@2x@2x.png", "share/icons/hicolor/256x256@2/apps/com.mitchellh.ghostty.png");
|
b.installFile("images/icons/icon_256x256@2x@2x.png", "share/icons/hicolor/256x256@2/apps/com.mitchellh.ghostty.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// libghostty (non-Darwin)
|
||||||
|
if (!builtin.target.isDarwin() and config.app_runtime == .none) {
|
||||||
|
// Shared
|
||||||
|
{
|
||||||
|
const lib = b.addSharedLibrary(.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.root_source_file = .{ .path = "src/main_c.zig" },
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
lib.root_module.addOptions("build_options", exe_options);
|
||||||
|
_ = try addDeps(b, lib, config);
|
||||||
|
|
||||||
|
const lib_install = b.addInstallLibFile(
|
||||||
|
lib.getEmittedBin(),
|
||||||
|
"libghostty.so",
|
||||||
|
);
|
||||||
|
b.getInstallStep().dependOn(&lib_install.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static
|
||||||
|
{
|
||||||
|
const lib = b.addStaticLibrary(.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.root_source_file = .{ .path = "src/main_c.zig" },
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
lib.root_module.addOptions("build_options", exe_options);
|
||||||
|
_ = try addDeps(b, lib, config);
|
||||||
|
|
||||||
|
const lib_install = b.addInstallLibFile(
|
||||||
|
lib.getEmittedBin(),
|
||||||
|
"libghostty.a",
|
||||||
|
);
|
||||||
|
b.getInstallStep().dependOn(&lib_install.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy our ghostty.h to include.
|
||||||
|
const header_install = b.addInstallHeaderFile(
|
||||||
|
"include/ghostty.h",
|
||||||
|
"ghostty.h",
|
||||||
|
);
|
||||||
|
b.getInstallStep().dependOn(&header_install.step);
|
||||||
|
}
|
||||||
|
|
||||||
// On Mac we can build the embedding library. This only handles the macOS lib.
|
// On Mac we can build the embedding library. This only handles the macOS lib.
|
||||||
if (builtin.target.isDarwin() and target.result.os.tag == .macos) {
|
if (builtin.target.isDarwin() and target.result.os.tag == .macos) {
|
||||||
// Create the universal macOS lib.
|
// Create the universal macOS lib.
|
||||||
|
@ -30,6 +30,11 @@ typedef void *ghostty_surface_t;
|
|||||||
typedef void *ghostty_inspector_t;
|
typedef void *ghostty_inspector_t;
|
||||||
|
|
||||||
// Enums are up top so we can reference them later.
|
// Enums are up top so we can reference them later.
|
||||||
|
typedef enum {
|
||||||
|
GHOSTTY_PLATFORM_INVALID,
|
||||||
|
GHOSTTY_PLATFORM_MACOS,
|
||||||
|
} ghostty_platform_e;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GHOSTTY_CLIPBOARD_STANDARD,
|
GHOSTTY_CLIPBOARD_STANDARD,
|
||||||
GHOSTTY_CLIPBOARD_SELECTION,
|
GHOSTTY_CLIPBOARD_SELECTION,
|
||||||
@ -350,8 +355,17 @@ typedef struct {
|
|||||||
} ghostty_error_s;
|
} ghostty_error_s;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void *userdata;
|
|
||||||
void *nsview;
|
void *nsview;
|
||||||
|
} ghostty_platform_macos_s;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
ghostty_platform_macos_s macos;
|
||||||
|
} ghostty_platform_u;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ghostty_platform_e platform_tag;
|
||||||
|
ghostty_platform_u platform;
|
||||||
|
void *userdata;
|
||||||
double scale_factor;
|
double scale_factor;
|
||||||
uint16_t font_size;
|
uint16_t font_size;
|
||||||
const char *working_directory;
|
const char *working_directory;
|
||||||
@ -472,9 +486,6 @@ uintptr_t ghostty_surface_pwd(ghostty_surface_t, char *, uintptr_t);
|
|||||||
|
|
||||||
ghostty_inspector_t ghostty_surface_inspector(ghostty_surface_t);
|
ghostty_inspector_t ghostty_surface_inspector(ghostty_surface_t);
|
||||||
void ghostty_inspector_free(ghostty_surface_t);
|
void ghostty_inspector_free(ghostty_surface_t);
|
||||||
bool ghostty_inspector_metal_init(ghostty_inspector_t, void *);
|
|
||||||
void ghostty_inspector_metal_render(ghostty_inspector_t, void *, void *);
|
|
||||||
bool ghostty_inspector_metal_shutdown(ghostty_inspector_t);
|
|
||||||
void ghostty_inspector_set_focus(ghostty_inspector_t, bool);
|
void ghostty_inspector_set_focus(ghostty_inspector_t, bool);
|
||||||
void ghostty_inspector_set_content_scale(ghostty_inspector_t, double, double);
|
void ghostty_inspector_set_content_scale(ghostty_inspector_t, double, double);
|
||||||
void ghostty_inspector_set_size(ghostty_inspector_t, uint32_t, uint32_t);
|
void ghostty_inspector_set_size(ghostty_inspector_t, uint32_t, uint32_t);
|
||||||
@ -484,6 +495,12 @@ void ghostty_inspector_mouse_scroll(ghostty_inspector_t, double, double, ghostty
|
|||||||
void ghostty_inspector_key(ghostty_inspector_t, ghostty_input_action_e, ghostty_input_key_e, ghostty_input_mods_e);
|
void ghostty_inspector_key(ghostty_inspector_t, ghostty_input_action_e, ghostty_input_key_e, ghostty_input_mods_e);
|
||||||
void ghostty_inspector_text(ghostty_inspector_t, const char *);
|
void ghostty_inspector_text(ghostty_inspector_t, const char *);
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
bool ghostty_inspector_metal_init(ghostty_inspector_t, void *);
|
||||||
|
void ghostty_inspector_metal_render(ghostty_inspector_t, void *, void *);
|
||||||
|
bool ghostty_inspector_metal_shutdown(ghostty_inspector_t);
|
||||||
|
#endif
|
||||||
|
|
||||||
// APIs I'd like to get rid of eventually but are still needed for now.
|
// APIs I'd like to get rid of eventually but are still needed for now.
|
||||||
// Don't use these unless you know what you're doing.
|
// Don't use these unless you know what you're doing.
|
||||||
void ghostty_set_window_background_blur(ghostty_surface_t, void *);
|
void ghostty_set_window_background_blur(ghostty_surface_t, void *);
|
||||||
|
@ -277,8 +277,11 @@ extension Ghostty {
|
|||||||
/// in the returned struct is only valid as long as this struct is retained.
|
/// in the returned struct is only valid as long as this struct is retained.
|
||||||
func ghosttyConfig(view: SurfaceView) -> ghostty_surface_config_s {
|
func ghosttyConfig(view: SurfaceView) -> ghostty_surface_config_s {
|
||||||
var config = ghostty_surface_config_new()
|
var config = ghostty_surface_config_new()
|
||||||
|
config.platform_tag = GHOSTTY_PLATFORM_MACOS
|
||||||
|
config.platform = ghostty_platform_u(macos: ghostty_platform_macos_s(
|
||||||
|
nsview: Unmanaged.passUnretained(view).toOpaque()
|
||||||
|
))
|
||||||
config.userdata = Unmanaged.passUnretained(view).toOpaque()
|
config.userdata = Unmanaged.passUnretained(view).toOpaque()
|
||||||
config.nsview = Unmanaged.passUnretained(view).toOpaque()
|
|
||||||
config.scale_factor = NSScreen.main!.backingScaleFactor
|
config.scale_factor = NSScreen.main!.backingScaleFactor
|
||||||
|
|
||||||
if let fontSize = fontSize { config.font_size = fontSize }
|
if let fontSize = fontSize { config.font_size = fontSize }
|
||||||
|
@ -231,9 +231,49 @@ pub const App = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Platform-specific configuration for libghostty.
|
||||||
|
pub const Platform = union(PlatformTag) {
|
||||||
|
macos: MacOS,
|
||||||
|
|
||||||
|
// If our build target for libghostty is not darwin then we do
|
||||||
|
// not include macos support at all.
|
||||||
|
pub const MacOS = if (builtin.target.isDarwin()) struct {
|
||||||
|
/// The view to render the surface on.
|
||||||
|
nsview: objc.Object,
|
||||||
|
} else void;
|
||||||
|
|
||||||
|
// The C ABI compatible version of this union. The tag is expected
|
||||||
|
// to be stored elsewhere.
|
||||||
|
pub const C = extern union {
|
||||||
|
macos: extern struct {
|
||||||
|
nsview: ?*anyopaque,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Initialize a Platform a tag and configuration from the C ABI.
|
||||||
|
pub fn init(tag_int: c_int, c_platform: C) !Platform {
|
||||||
|
const tag = try std.meta.intToEnum(PlatformTag, tag_int);
|
||||||
|
return switch (tag) {
|
||||||
|
.macos => if (MacOS != void) macos: {
|
||||||
|
const config = c_platform.macos;
|
||||||
|
const nsview = objc.Object.fromId(config.nsview orelse
|
||||||
|
break :macos error.NSViewMustBeSet);
|
||||||
|
break :macos .{ .macos = .{ .nsview = nsview } };
|
||||||
|
} else error.UnsupportedPlatform,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PlatformTag = enum(c_int) {
|
||||||
|
// "0" is reserved for invalid so we can detect unset values
|
||||||
|
// from the C API.
|
||||||
|
|
||||||
|
macos = 1,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Surface = struct {
|
pub const Surface = struct {
|
||||||
app: *App,
|
app: *App,
|
||||||
nsview: objc.Object,
|
platform: Platform,
|
||||||
core_surface: CoreSurface,
|
core_surface: CoreSurface,
|
||||||
content_scale: apprt.ContentScale,
|
content_scale: apprt.ContentScale,
|
||||||
size: apprt.SurfaceSize,
|
size: apprt.SurfaceSize,
|
||||||
@ -243,12 +283,14 @@ pub const Surface = struct {
|
|||||||
inspector: ?*Inspector = null,
|
inspector: ?*Inspector = null,
|
||||||
|
|
||||||
pub const Options = extern struct {
|
pub const Options = extern struct {
|
||||||
|
/// The platform that this surface is being initialized for and
|
||||||
|
/// the associated platform-specific configuration.
|
||||||
|
platform_tag: c_int = 0,
|
||||||
|
platform: Platform.C = undefined,
|
||||||
|
|
||||||
/// Userdata passed to some of the callbacks.
|
/// Userdata passed to some of the callbacks.
|
||||||
userdata: ?*anyopaque = null,
|
userdata: ?*anyopaque = null,
|
||||||
|
|
||||||
/// The pointer to the backing NSView for the surface.
|
|
||||||
nsview: ?*anyopaque = null,
|
|
||||||
|
|
||||||
/// The scale factor of the screen.
|
/// The scale factor of the screen.
|
||||||
scale_factor: f64 = 1,
|
scale_factor: f64 = 1,
|
||||||
|
|
||||||
@ -279,13 +321,10 @@ pub const Surface = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(self: *Surface, app: *App, opts: Options) !void {
|
pub fn init(self: *Surface, app: *App, opts: Options) !void {
|
||||||
const nsview = objc.Object.fromId(opts.nsview orelse
|
|
||||||
return error.NSViewMustBeSet);
|
|
||||||
|
|
||||||
self.* = .{
|
self.* = .{
|
||||||
.app = app,
|
.app = app,
|
||||||
|
.platform = try Platform.init(opts.platform_tag, opts.platform),
|
||||||
.core_surface = undefined,
|
.core_surface = undefined,
|
||||||
.nsview = nsview,
|
|
||||||
.content_scale = .{
|
.content_scale = .{
|
||||||
.x = @floatCast(opts.scale_factor),
|
.x = @floatCast(opts.scale_factor),
|
||||||
.y = @floatCast(opts.scale_factor),
|
.y = @floatCast(opts.scale_factor),
|
||||||
@ -1584,30 +1623,33 @@ pub const CAPI = struct {
|
|||||||
ptr.freeInspector();
|
ptr.freeInspector();
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn ghostty_inspector_metal_init(ptr: *Inspector, device: objc.c.id) bool {
|
// Inspector Metal APIs are only available on Apple systems
|
||||||
return ptr.initMetal(objc.Object.fromId(device));
|
usingnamespace if (builtin.target.isDarwin()) struct {
|
||||||
}
|
export fn ghostty_inspector_metal_init(ptr: *Inspector, device: objc.c.id) bool {
|
||||||
|
return ptr.initMetal(objc.Object.fromId(device));
|
||||||
export fn ghostty_inspector_metal_render(
|
|
||||||
ptr: *Inspector,
|
|
||||||
command_buffer: objc.c.id,
|
|
||||||
descriptor: objc.c.id,
|
|
||||||
) void {
|
|
||||||
return ptr.renderMetal(
|
|
||||||
objc.Object.fromId(command_buffer),
|
|
||||||
objc.Object.fromId(descriptor),
|
|
||||||
) catch |err| {
|
|
||||||
log.err("error rendering inspector err={}", .{err});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export fn ghostty_inspector_metal_shutdown(ptr: *Inspector) void {
|
|
||||||
if (ptr.backend) |v| {
|
|
||||||
v.deinit();
|
|
||||||
ptr.backend = null;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
export fn ghostty_inspector_metal_render(
|
||||||
|
ptr: *Inspector,
|
||||||
|
command_buffer: objc.c.id,
|
||||||
|
descriptor: objc.c.id,
|
||||||
|
) void {
|
||||||
|
return ptr.renderMetal(
|
||||||
|
objc.Object.fromId(command_buffer),
|
||||||
|
objc.Object.fromId(descriptor),
|
||||||
|
) catch |err| {
|
||||||
|
log.err("error rendering inspector err={}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn ghostty_inspector_metal_shutdown(ptr: *Inspector) void {
|
||||||
|
if (ptr.backend) |v| {
|
||||||
|
v.deinit();
|
||||||
|
ptr.backend = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else struct {};
|
||||||
|
|
||||||
export fn ghostty_inspector_set_size(ptr: *Inspector, w: u32, h: u32) void {
|
export fn ghostty_inspector_set_size(ptr: *Inspector, w: u32, h: u32) void {
|
||||||
ptr.updateSize(w, h);
|
ptr.updateSize(w, h);
|
||||||
|
@ -466,7 +466,9 @@ pub fn finalizeSurfaceInit(self: *const Metal, surface: *apprt.Surface) !void {
|
|||||||
},
|
},
|
||||||
|
|
||||||
apprt.embedded => .{
|
apprt.embedded => .{
|
||||||
.view = surface.nsview,
|
.view = switch (surface.platform) {
|
||||||
|
.macos => |v| v.nsview,
|
||||||
|
},
|
||||||
.scaleFactor = @floatCast(surface.content_scale.x),
|
.scaleFactor = @floatCast(surface.content_scale.x),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -422,6 +422,12 @@ pub fn surfaceInit(surface: *apprt.Surface) !void {
|
|||||||
},
|
},
|
||||||
|
|
||||||
apprt.glfw => try self.threadEnter(surface),
|
apprt.glfw => try self.threadEnter(surface),
|
||||||
|
|
||||||
|
apprt.embedded => {
|
||||||
|
// TODO(mitchellh): this does nothing today to allow libghostty
|
||||||
|
// to compile for OpenGL targets but libghostty is strictly
|
||||||
|
// broken for rendering on this platforms.
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are very noisy so this is commented, but easy to uncomment
|
// These are very noisy so this is commented, but easy to uncomment
|
||||||
@ -529,6 +535,12 @@ pub fn threadEnter(self: *const OpenGL, surface: *apprt.Surface) !void {
|
|||||||
gl.glad.versionMinor(@intCast(version)),
|
gl.glad.versionMinor(@intCast(version)),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
apprt.embedded => {
|
||||||
|
// TODO(mitchellh): this does nothing today to allow libghostty
|
||||||
|
// to compile for OpenGL targets but libghostty is strictly
|
||||||
|
// broken for rendering on this platforms.
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,6 +560,10 @@ pub fn threadExit(self: *const OpenGL) void {
|
|||||||
gl.glad.unload();
|
gl.glad.unload();
|
||||||
glfw.makeContextCurrent(null);
|
glfw.makeContextCurrent(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
apprt.embedded => {
|
||||||
|
// TODO: see threadEnter
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1815,6 +1831,7 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void {
|
|||||||
switch (apprt.runtime) {
|
switch (apprt.runtime) {
|
||||||
apprt.glfw => surface.window.swapBuffers(),
|
apprt.glfw => surface.window.swapBuffers(),
|
||||||
apprt.gtk => {},
|
apprt.gtk => {},
|
||||||
|
apprt.embedded => {},
|
||||||
else => @compileError("unsupported runtime"),
|
else => @compileError("unsupported runtime"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user