add app runtime option, add gtk backend

This commit is contained in:
Mitchell Hashimoto
2023-02-20 15:13:06 -08:00
parent 35cb9d20b1
commit 48c9c65915
7 changed files with 128 additions and 18 deletions

View File

@ -3,6 +3,7 @@ const builtin = @import("builtin");
const fs = std.fs;
const Builder = std.build.Builder;
const LibExeObjStep = std.build.LibExeObjStep;
const apprt = @import("src/apprt.zig");
const glfw = @import("vendor/mach/libs/glfw/build.zig");
const fontconfig = @import("pkg/fontconfig/build.zig");
const freetype = @import("pkg/freetype/build.zig");
@ -45,6 +46,7 @@ comptime {
var tracy: bool = false;
var enable_coretext: bool = false;
var enable_fontconfig: bool = false;
var app_runtime: apprt.Runtime = .none;
pub fn build(b: *std.build.Builder) !void {
const optimize = b.standardOptimizeOption(.{});
@ -77,6 +79,12 @@ pub fn build(b: *std.build.Builder) !void {
"Enable fontconfig for font discovery (default true on Linux)",
) orelse target.isLinux();
app_runtime = b.option(
apprt.Runtime,
"app-runtime",
"The app runtime to use. Not all values supported on all platforms.",
) orelse apprt.Runtime.default(target);
const static = b.option(
bool,
"static",
@ -111,6 +119,7 @@ pub fn build(b: *std.build.Builder) !void {
exe_options.addOption(bool, "tracy_enabled", tracy);
exe_options.addOption(bool, "coretext", enable_coretext);
exe_options.addOption(bool, "fontconfig", enable_fontconfig);
exe_options.addOption(apprt.Runtime, "app_runtime", app_runtime);
// Exe
{
@ -120,7 +129,7 @@ pub fn build(b: *std.build.Builder) !void {
}
exe.addOptions("build_options", exe_options);
exe.install();
if (app_runtime != .none) exe.install();
// Add the shared dependencies
_ = try addDeps(b, exe, static);
@ -134,7 +143,7 @@ pub fn build(b: *std.build.Builder) !void {
b.installFile("dist/macos/Ghostty.icns", "Ghostty.app/Contents/Resources/Ghostty.icns");
}
// On Mac we can build the app.
// On Mac we can build the embedding library.
if (builtin.target.isDarwin()) {
const static_lib_aarch64 = lib: {
const lib = b.addStaticLibrary(.{
@ -539,22 +548,30 @@ fn addDeps(
}
if (!lib) {
step.addModule("glfw", glfw.module(b));
// We always statically compile glad
step.addIncludePath("vendor/glad/include/");
step.addCSourceFile("vendor/glad/src/gl.c", &.{});
// Glfw
switch (app_runtime) {
.none => {},
.glfw => {
step.addModule("glfw", glfw.module(b));
const glfw_opts: glfw.Options = .{
.metal = step.target.isDarwin(),
.opengl = false,
};
try glfw.link(b, step, glfw_opts);
// Imgui
// Must also link to imgui
const imgui_step = try imgui.link(b, step, imgui_opts);
try glfw.link(b, imgui_step, glfw_opts);
},
.gtk => {
step.linkSystemLibrary("gtk4");
},
}
}
return static_libs;

View File

@ -22,6 +22,7 @@
, expat
, fontconfig
, freetype
, gtk4
, harfbuzz
, libpng
, libGL
@ -53,6 +54,8 @@ let
libXcursor
libXi
libXrandr
gtk4
];
in mkShell rec {
name = "ghostty";
@ -102,6 +105,9 @@ in mkShell rec {
libXi
libXinerama
libXrandr
# Only needed for GTK builds
gtk4
];
# This should be set onto the rpath of the ghostty binary if you want

View File

@ -8,21 +8,49 @@
//! The goal is to have different implementations share as much of the core
//! logic as possible, and to only reach out to platform-specific implementation
//! code when absolutely necessary.
const std = @import("std");
const builtin = @import("builtin");
const build_config = @import("build_config.zig");
pub usingnamespace @import("apprt/structs.zig");
pub const glfw = @import("apprt/glfw.zig");
pub const gtk = @import("apprt/gtk.zig");
pub const browser = @import("apprt/browser.zig");
pub const embedded = @import("apprt/embedded.zig");
pub const Window = @import("apprt/Window.zig");
/// Runtime is the runtime to use for Ghostty. All runtimes do not provide
/// equivalent feature sets. For example, GTK offers tabbing and more features
/// that glfw does not provide. However, glfw may require many less
/// dependencies.
pub const Runtime = enum {
/// Will not produce an executable at all when `zig build` is called.
/// This is only useful if you're only interested in the lib only (macOS).
none,
/// Glfw-backed. Very simple. Glfw is statically linked. Tabbing and
/// other rich windowing features are not supported.
glfw,
/// GTK-backed. Rich windowed application. GTK is dynamically linked.
gtk,
pub fn default(target: std.zig.CrossTarget) Runtime {
_ = target;
return .glfw;
}
};
/// The implementation to use for the app runtime. This is comptime chosen
/// so that every build has exactly one application runtime implementation.
/// Note: it is very rare to use Runtime directly; most usage will use
/// Window or something.
pub const runtime = switch (build_config.artifact) {
.exe => glfw,
.exe => switch (build_config.app_runtime) {
.none => @compileError("exe with no runtime not allowed"),
.glfw => glfw,
.gtk => gtk,
},
.lib => embedded,
.wasm_module => browser,
};

45
src/apprt/gtk.zig Normal file
View File

@ -0,0 +1,45 @@
//! Application runtime that uses GTK4.
const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
pub const c = @cImport({
@cInclude("gtk/gtk.h");
});
const log = std.log.scoped(.gtk);
pub const App = struct {
pub const Options = struct {
/// GTK app ID
id: [:0]const u8 = "com.mitchellh.ghostty",
};
pub fn init(opts: Options) !App {
const app = c.gtk_application_new(opts.id.ptr, c.G_APPLICATION_DEFAULT_FLAGS);
errdefer c.g_object_unref(app);
return .{};
}
pub fn terminate(self: App) void {
_ = self;
}
pub fn wakeup(self: App) !void {
_ = self;
}
pub fn wait(self: App) !void {
_ = self;
}
};
pub const Window = struct {
pub const Options = struct {};
pub fn deinit(self: *Window) void {
_ = self;
}
};

View File

@ -4,15 +4,19 @@
//! to shim logic and values into them later.
const std = @import("std");
const builtin = @import("builtin");
const options = @import("build_options");
const assert = std.debug.assert;
/// The artifact we're producing. This can be used to determine if we're
/// building a standalone exe, an embedded lib, etc.
pub const artifact = Artifact.detect();
/// The runtime to back exe artifacts with.
pub const app_runtime = options.app_runtime;
/// Whether our devmode UI is enabled or not. This requires imgui to be
/// compiled.
pub const devmode_enabled = artifact == .exe;
pub const devmode_enabled = artifact == .exe and app_runtime == .glfw;
pub const Artifact = enum {
/// Standalone executable

View File

@ -1,5 +1,6 @@
const std = @import("std");
const builtin = @import("builtin");
const build_config = @import("build_config.zig");
const options = @import("build_options");
const glfw = @import("glfw");
const macos = @import("macos");
@ -88,12 +89,19 @@ pub fn main() !void {
try config.finalize();
std.log.debug("config={}", .{config});
switch (build_config.app_runtime) {
.none => {},
.glfw => {
// We want to log all our errors
glfw.setErrorCallback(glfwErrorCallback);
},
.gtk => {},
}
// Run our app with a single initial window to start.
var app = try App.create(alloc, .{}, &config);
defer app.destroy();
if (build_config.app_runtime == .gtk) return;
_ = try app.newWindow(.{});
try app.run();
}

View File

@ -420,6 +420,7 @@ pub fn deinitDevMode(self: *const OpenGL) void {
/// Callback called by renderer.Thread when it begins.
pub fn threadEnter(self: *const OpenGL, win: apprt.runtime.Window) !void {
_ = self;
if (apprt.runtime == apprt.gtk) @panic("TODO");
// We need to make the OpenGL context current. OpenGL requires
// that a single thread own the a single OpenGL context (if any). This
@ -1066,7 +1067,8 @@ pub fn setScreenSize(self: *OpenGL, dim: renderer.ScreenSize) !void {
// Apply our padding
const padding = self.padding.explicit.add(if (self.padding.balance)
renderer.Padding.balanced(dim, grid_size, self.cell_size)
else .{});
else
.{});
const padded_dim = dim.subPadding(padding);
log.debug("screen size padded={} screen={} grid={} cell={} padding={}", .{