mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
Merge pull request #1298 from mitchellh/ios
build: add iOS-compatible libghostty build to xcframework
This commit is contained in:
2
.github/workflows/release-tip.yml
vendored
2
.github/workflows/release-tip.yml
vendored
@ -88,7 +88,7 @@ jobs:
|
|||||||
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
||||||
# Nix breaks xcodebuild so this has to be run outside.
|
# Nix breaks xcodebuild so this has to be run outside.
|
||||||
- name: Build Ghostty.app
|
- name: Build Ghostty.app
|
||||||
run: cd macos && xcodebuild -configuration Release
|
run: cd macos && xcodebuild -target Ghostty -configuration Release
|
||||||
|
|
||||||
# We inject the "build number" as simply the number of commits since HEAD.
|
# We inject the "build number" as simply the number of commits since HEAD.
|
||||||
# This will be a monotonically always increasing build number that we use.
|
# This will be a monotonically always increasing build number that we use.
|
||||||
|
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
@ -88,7 +88,13 @@ jobs:
|
|||||||
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
||||||
# Nix breaks xcodebuild so this has to be run outside.
|
# Nix breaks xcodebuild so this has to be run outside.
|
||||||
- name: Build Ghostty.app
|
- name: Build Ghostty.app
|
||||||
run: cd macos && xcodebuild
|
run: cd macos && xcodebuild -target Ghostty
|
||||||
|
|
||||||
|
# Build the iOS target without code signing just to verify it works.
|
||||||
|
- name: Build Ghostty iOS
|
||||||
|
run: |
|
||||||
|
cd macos
|
||||||
|
xcodebuild -target Ghostty-iOS "CODE_SIGNING_ALLOWED=NO"
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
|
340
build.zig
340
build.zig
@ -37,25 +37,7 @@ const app_version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0 };
|
|||||||
|
|
||||||
pub fn build(b: *std.Build) !void {
|
pub fn build(b: *std.Build) !void {
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
const target = target: {
|
const target = b.standardTargetOptions(.{});
|
||||||
var result = b.standardTargetOptions(.{});
|
|
||||||
|
|
||||||
// On macOS, we specify a minimum supported version. This is important
|
|
||||||
// to set since header files will use this to determine the availability
|
|
||||||
// of certain APIs and I believe it is also encoded in the Mach-O
|
|
||||||
// binaries.
|
|
||||||
if (result.result.os.tag == .macos and
|
|
||||||
result.query.os_version_min == null)
|
|
||||||
{
|
|
||||||
result.query.os_version_min = .{ .semver = .{
|
|
||||||
.major = 12,
|
|
||||||
.minor = 0,
|
|
||||||
.patch = 0,
|
|
||||||
} };
|
|
||||||
}
|
|
||||||
|
|
||||||
break :target result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const wasm_target: WasmTarget = .browser;
|
const wasm_target: WasmTarget = .browser;
|
||||||
|
|
||||||
@ -438,92 +420,55 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
// 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) {
|
||||||
// Modify our build configuration for macOS builds.
|
// Create the universal macOS lib.
|
||||||
const macos_config: BuildConfig = config: {
|
const macos_lib_step, const macos_lib_path = try createMacOSLib(
|
||||||
var copy = config;
|
b,
|
||||||
|
optimize,
|
||||||
// Always static for the macOS app because we want all of our
|
config,
|
||||||
// dependencies in a fat static library.
|
exe_options,
|
||||||
copy.static = true;
|
);
|
||||||
|
|
||||||
break :config copy;
|
|
||||||
};
|
|
||||||
|
|
||||||
const static_lib_aarch64 = lib: {
|
|
||||||
const lib = b.addStaticLibrary(.{
|
|
||||||
.name = "ghostty",
|
|
||||||
.root_source_file = .{ .path = "src/main_c.zig" },
|
|
||||||
.target = b.resolveTargetQuery(.{
|
|
||||||
.cpu_arch = .aarch64,
|
|
||||||
.os_tag = .macos,
|
|
||||||
.os_version_min = target.query.os_version_min,
|
|
||||||
}),
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
lib.bundle_compiler_rt = true;
|
|
||||||
lib.linkLibC();
|
|
||||||
lib.root_module.addOptions("build_options", exe_options);
|
|
||||||
|
|
||||||
// Create a single static lib with all our dependencies merged
|
|
||||||
var lib_list = try addDeps(b, lib, macos_config);
|
|
||||||
try lib_list.append(lib.getEmittedBin());
|
|
||||||
const libtool = LibtoolStep.create(b, .{
|
|
||||||
.name = "ghostty",
|
|
||||||
.out_name = "libghostty-aarch64-fat.a",
|
|
||||||
.sources = lib_list.items,
|
|
||||||
});
|
|
||||||
libtool.step.dependOn(&lib.step);
|
|
||||||
b.default_step.dependOn(libtool.step);
|
|
||||||
|
|
||||||
break :lib libtool;
|
|
||||||
};
|
|
||||||
|
|
||||||
const static_lib_x86_64 = lib: {
|
|
||||||
const lib = b.addStaticLibrary(.{
|
|
||||||
.name = "ghostty",
|
|
||||||
.root_source_file = .{ .path = "src/main_c.zig" },
|
|
||||||
.target = b.resolveTargetQuery(.{
|
|
||||||
.cpu_arch = .x86_64,
|
|
||||||
.os_tag = .macos,
|
|
||||||
.os_version_min = target.query.os_version_min,
|
|
||||||
}),
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
lib.bundle_compiler_rt = true;
|
|
||||||
lib.linkLibC();
|
|
||||||
lib.root_module.addOptions("build_options", exe_options);
|
|
||||||
|
|
||||||
// Create a single static lib with all our dependencies merged
|
|
||||||
var lib_list = try addDeps(b, lib, macos_config);
|
|
||||||
try lib_list.append(lib.getEmittedBin());
|
|
||||||
const libtool = LibtoolStep.create(b, .{
|
|
||||||
.name = "ghostty",
|
|
||||||
.out_name = "libghostty-x86_64-fat.a",
|
|
||||||
.sources = lib_list.items,
|
|
||||||
});
|
|
||||||
libtool.step.dependOn(&lib.step);
|
|
||||||
b.default_step.dependOn(libtool.step);
|
|
||||||
|
|
||||||
break :lib libtool;
|
|
||||||
};
|
|
||||||
|
|
||||||
const static_lib_universal = LipoStep.create(b, .{
|
|
||||||
.name = "ghostty",
|
|
||||||
.out_name = "libghostty.a",
|
|
||||||
.input_a = static_lib_aarch64.output,
|
|
||||||
.input_b = static_lib_x86_64.output,
|
|
||||||
});
|
|
||||||
static_lib_universal.step.dependOn(static_lib_aarch64.step);
|
|
||||||
static_lib_universal.step.dependOn(static_lib_x86_64.step);
|
|
||||||
|
|
||||||
// Add our library to zig-out
|
// Add our library to zig-out
|
||||||
const lib_install = b.addInstallLibFile(
|
const lib_install = b.addInstallLibFile(
|
||||||
static_lib_universal.output,
|
macos_lib_path,
|
||||||
"libghostty.a",
|
"libghostty-macos.a",
|
||||||
);
|
);
|
||||||
b.getInstallStep().dependOn(&lib_install.step);
|
b.getInstallStep().dependOn(&lib_install.step);
|
||||||
|
|
||||||
// Copy our ghostty.h to include
|
// Create the universal iOS lib.
|
||||||
|
const ios_lib_step, const ios_lib_path = try createIOSLib(
|
||||||
|
b,
|
||||||
|
null,
|
||||||
|
optimize,
|
||||||
|
config,
|
||||||
|
exe_options,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add our library to zig-out
|
||||||
|
const ios_lib_install = b.addInstallLibFile(
|
||||||
|
ios_lib_path,
|
||||||
|
"libghostty-ios.a",
|
||||||
|
);
|
||||||
|
b.getInstallStep().dependOn(&ios_lib_install.step);
|
||||||
|
|
||||||
|
// Create the iOS simulator lib.
|
||||||
|
const ios_sim_lib_step, const ios_sim_lib_path = try createIOSLib(
|
||||||
|
b,
|
||||||
|
.simulator,
|
||||||
|
optimize,
|
||||||
|
config,
|
||||||
|
exe_options,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add our library to zig-out
|
||||||
|
const ios_sim_lib_install = b.addInstallLibFile(
|
||||||
|
ios_sim_lib_path,
|
||||||
|
"libghostty-ios-simulator.a",
|
||||||
|
);
|
||||||
|
b.getInstallStep().dependOn(&ios_sim_lib_install.step);
|
||||||
|
|
||||||
|
// Copy our ghostty.h to include. The header file is shared by
|
||||||
|
// all embedded targets.
|
||||||
const header_install = b.addInstallHeaderFile(
|
const header_install = b.addInstallHeaderFile(
|
||||||
"include/ghostty.h",
|
"include/ghostty.h",
|
||||||
"ghostty.h",
|
"ghostty.h",
|
||||||
@ -535,10 +480,25 @@ pub fn build(b: *std.Build) !void {
|
|||||||
const xcframework = XCFrameworkStep.create(b, .{
|
const xcframework = XCFrameworkStep.create(b, .{
|
||||||
.name = "GhosttyKit",
|
.name = "GhosttyKit",
|
||||||
.out_path = "macos/GhosttyKit.xcframework",
|
.out_path = "macos/GhosttyKit.xcframework",
|
||||||
.library = static_lib_universal.output,
|
.libraries = &.{
|
||||||
|
.{
|
||||||
|
.library = macos_lib_path,
|
||||||
.headers = .{ .path = "include" },
|
.headers = .{ .path = "include" },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.library = ios_lib_path,
|
||||||
|
.headers = .{ .path = "include" },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.library = ios_sim_lib_path,
|
||||||
|
.headers = .{ .path = "include" },
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
xcframework.step.dependOn(static_lib_universal.step);
|
xcframework.step.dependOn(ios_lib_step);
|
||||||
|
xcframework.step.dependOn(ios_sim_lib_step);
|
||||||
|
xcframework.step.dependOn(macos_lib_step);
|
||||||
|
xcframework.step.dependOn(&header_install.step);
|
||||||
b.default_step.dependOn(xcframework.step);
|
b.default_step.dependOn(xcframework.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,6 +632,175 @@ pub fn build(b: *std.Build) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the minimum OS version for the given OS tag. This shouldn't
|
||||||
|
/// be used generally, it should only be used for Darwin-based OS currently.
|
||||||
|
fn osVersionMin(tag: std.Target.Os.Tag) ?std.Target.Query.OsVersion {
|
||||||
|
return switch (tag) {
|
||||||
|
// The lowest supported version of macOS is 12.x because
|
||||||
|
// this is the first version to support Apple Silicon so it is
|
||||||
|
// the earliest version we can virtualize to test (I only have
|
||||||
|
// an Apple Silicon machine for macOS).
|
||||||
|
.macos => .{ .semver = .{
|
||||||
|
.major = 12,
|
||||||
|
.minor = 0,
|
||||||
|
.patch = 0,
|
||||||
|
} },
|
||||||
|
|
||||||
|
// iOS 17 picked arbitrarily
|
||||||
|
.ios => .{ .semver = .{
|
||||||
|
.major = 17,
|
||||||
|
.minor = 0,
|
||||||
|
.patch = 0,
|
||||||
|
} },
|
||||||
|
|
||||||
|
// This should never happen currently. If we add a new target then
|
||||||
|
// we should add a new case here.
|
||||||
|
else => @panic("unhandled os version min os tag"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a universal macOS libghostty library and returns the path
|
||||||
|
/// to the final library.
|
||||||
|
///
|
||||||
|
/// The library is always a fat static library currently because this is
|
||||||
|
/// expected to be used directly with Xcode and Swift. In the future, we
|
||||||
|
/// probably want to change this because it makes it harder to use the
|
||||||
|
/// library in other contexts.
|
||||||
|
fn createMacOSLib(
|
||||||
|
b: *std.Build,
|
||||||
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
config: BuildConfig,
|
||||||
|
exe_options: *std.Build.Step.Options,
|
||||||
|
) !struct { *std.Build.Step, std.Build.LazyPath } {
|
||||||
|
// Modify our build configuration for macOS builds.
|
||||||
|
const macos_config: BuildConfig = config: {
|
||||||
|
var copy = config;
|
||||||
|
|
||||||
|
// Always static for the macOS app because we want all of our
|
||||||
|
// dependencies in a fat static library.
|
||||||
|
copy.static = true;
|
||||||
|
|
||||||
|
break :config copy;
|
||||||
|
};
|
||||||
|
|
||||||
|
const static_lib_aarch64 = lib: {
|
||||||
|
const lib = b.addStaticLibrary(.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.root_source_file = .{ .path = "src/main_c.zig" },
|
||||||
|
.target = b.resolveTargetQuery(.{
|
||||||
|
.cpu_arch = .aarch64,
|
||||||
|
.os_tag = .macos,
|
||||||
|
.os_version_min = osVersionMin(.macos),
|
||||||
|
}),
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
lib.bundle_compiler_rt = true;
|
||||||
|
lib.linkLibC();
|
||||||
|
lib.root_module.addOptions("build_options", exe_options);
|
||||||
|
|
||||||
|
// Create a single static lib with all our dependencies merged
|
||||||
|
var lib_list = try addDeps(b, lib, macos_config);
|
||||||
|
try lib_list.append(lib.getEmittedBin());
|
||||||
|
const libtool = LibtoolStep.create(b, .{
|
||||||
|
.name = "ghostty",
|
||||||
|
.out_name = "libghostty-aarch64-fat.a",
|
||||||
|
.sources = lib_list.items,
|
||||||
|
});
|
||||||
|
libtool.step.dependOn(&lib.step);
|
||||||
|
b.default_step.dependOn(libtool.step);
|
||||||
|
|
||||||
|
break :lib libtool;
|
||||||
|
};
|
||||||
|
|
||||||
|
const static_lib_x86_64 = lib: {
|
||||||
|
const lib = b.addStaticLibrary(.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.root_source_file = .{ .path = "src/main_c.zig" },
|
||||||
|
.target = b.resolveTargetQuery(.{
|
||||||
|
.cpu_arch = .x86_64,
|
||||||
|
.os_tag = .macos,
|
||||||
|
.os_version_min = osVersionMin(.macos),
|
||||||
|
}),
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
lib.bundle_compiler_rt = true;
|
||||||
|
lib.linkLibC();
|
||||||
|
lib.root_module.addOptions("build_options", exe_options);
|
||||||
|
|
||||||
|
// Create a single static lib with all our dependencies merged
|
||||||
|
var lib_list = try addDeps(b, lib, macos_config);
|
||||||
|
try lib_list.append(lib.getEmittedBin());
|
||||||
|
const libtool = LibtoolStep.create(b, .{
|
||||||
|
.name = "ghostty",
|
||||||
|
.out_name = "libghostty-x86_64-fat.a",
|
||||||
|
.sources = lib_list.items,
|
||||||
|
});
|
||||||
|
libtool.step.dependOn(&lib.step);
|
||||||
|
b.default_step.dependOn(libtool.step);
|
||||||
|
|
||||||
|
break :lib libtool;
|
||||||
|
};
|
||||||
|
|
||||||
|
const static_lib_universal = LipoStep.create(b, .{
|
||||||
|
.name = "ghostty",
|
||||||
|
.out_name = "libghostty.a",
|
||||||
|
.input_a = static_lib_aarch64.output,
|
||||||
|
.input_b = static_lib_x86_64.output,
|
||||||
|
});
|
||||||
|
static_lib_universal.step.dependOn(static_lib_aarch64.step);
|
||||||
|
static_lib_universal.step.dependOn(static_lib_x86_64.step);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
static_lib_universal.step,
|
||||||
|
static_lib_universal.output,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an Apple iOS/iPadOS build.
|
||||||
|
fn createIOSLib(
|
||||||
|
b: *std.Build,
|
||||||
|
abi: ?std.Target.Abi,
|
||||||
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
config: BuildConfig,
|
||||||
|
exe_options: *std.Build.Step.Options,
|
||||||
|
) !struct { *std.Build.Step, std.Build.LazyPath } {
|
||||||
|
const ios_config: BuildConfig = config: {
|
||||||
|
var copy = config;
|
||||||
|
copy.static = true;
|
||||||
|
break :config copy;
|
||||||
|
};
|
||||||
|
|
||||||
|
const lib = b.addStaticLibrary(.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.root_source_file = .{ .path = "src/main_c.zig" },
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = b.resolveTargetQuery(.{
|
||||||
|
.cpu_arch = .aarch64,
|
||||||
|
.os_tag = .ios,
|
||||||
|
.os_version_min = osVersionMin(.ios),
|
||||||
|
.abi = abi,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
lib.bundle_compiler_rt = true;
|
||||||
|
lib.linkLibC();
|
||||||
|
lib.root_module.addOptions("build_options", exe_options);
|
||||||
|
|
||||||
|
// Create a single static lib with all our dependencies merged
|
||||||
|
var lib_list = try addDeps(b, lib, ios_config);
|
||||||
|
try lib_list.append(lib.getEmittedBin());
|
||||||
|
const libtool = LibtoolStep.create(b, .{
|
||||||
|
.name = "ghostty",
|
||||||
|
.out_name = "libghostty-ios-fat.a",
|
||||||
|
.sources = lib_list.items,
|
||||||
|
});
|
||||||
|
libtool.step.dependOn(&lib.step);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
libtool.step,
|
||||||
|
libtool.output,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Used to keep track of a list of file sources.
|
/// Used to keep track of a list of file sources.
|
||||||
const LazyPathList = std.ArrayList(std.Build.LazyPath);
|
const LazyPathList = std.ArrayList(std.Build.LazyPath);
|
||||||
|
|
||||||
@ -830,7 +959,14 @@ fn addDeps(
|
|||||||
|
|
||||||
// Mac Stuff
|
// Mac Stuff
|
||||||
if (step.rootModuleTarget().isDarwin()) {
|
if (step.rootModuleTarget().isDarwin()) {
|
||||||
step.root_module.addImport("objc", objc_dep.module("objc"));
|
// This is a bit of a hack that should probably be fixed upstream
|
||||||
|
// in zig-objc, but we need to add the apple SDK paths to the
|
||||||
|
// zig-objc module so that it can find the objc runtime headers.
|
||||||
|
const module = objc_dep.module("objc");
|
||||||
|
module.resolved_target = step.root_module.resolved_target;
|
||||||
|
try @import("apple_sdk").addPaths(b, module);
|
||||||
|
step.root_module.addImport("objc", module);
|
||||||
|
|
||||||
step.root_module.addImport("macos", macos_dep.module("macos"));
|
step.root_module.addImport("macos", macos_dep.module("macos"));
|
||||||
step.linkLibrary(macos_dep.artifact("macos"));
|
step.linkLibrary(macos_dep.artifact("macos"));
|
||||||
try static_libs.append(macos_dep.artifact("macos").getEmittedBin());
|
try static_libs.append(macos_dep.artifact("macos").getEmittedBin());
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
// Zig libs
|
// Zig libs
|
||||||
.libxev = .{
|
.libxev = .{
|
||||||
.url = "https://github.com/mitchellh/libxev/archive/74bc7aea4a8f88210f0ad4215108613ab7e7af1a.tar.gz",
|
.url = "https://github.com/mitchellh/libxev/archive/4e6781895e4e6c477597c8c2713d36cd82b57d07.tar.gz",
|
||||||
.hash = "122029743e5d96aa1b57a1b99ff58bf13ff9ed6d8f624ac3ae8074062feb91c5bd8d",
|
.hash = "12203f87e00caa6c07c02a748f234a5c0ee2ca5c334ec464e88810d93e7b5495a56f",
|
||||||
},
|
},
|
||||||
.mach_glfw = .{
|
.mach_glfw = .{
|
||||||
.url = "https://github.com/der-teufel-programming/mach-glfw/archive/a9aae000cdc104dabe75d829ff9dab6809e47604.tar.gz",
|
.url = "https://github.com/der-teufel-programming/mach-glfw/archive/a9aae000cdc104dabe75d829ff9dab6809e47604.tar.gz",
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "icon_512x512@2x@2x 1.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filename" : "icon_16x16.png",
|
"filename" : "icon_16x16.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 93 KiB |
@ -11,6 +11,9 @@
|
|||||||
552964E62B34A9B400030505 /* vim in Resources */ = {isa = PBXBuildFile; fileRef = 552964E52B34A9B400030505 /* vim */; };
|
552964E62B34A9B400030505 /* vim in Resources */ = {isa = PBXBuildFile; fileRef = 552964E52B34A9B400030505 /* vim */; };
|
||||||
8503D7C72A549C66006CFF3D /* FullScreenHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */; };
|
8503D7C72A549C66006CFF3D /* FullScreenHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */; };
|
||||||
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; };
|
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; };
|
||||||
|
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
|
||||||
|
A514C8D72B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
|
||||||
|
A514C8D82B54DC6800493A16 /* Ghostty.App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */; };
|
||||||
A51B78472AF4B58B00F3EDB9 /* TerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */; };
|
A51B78472AF4B58B00F3EDB9 /* TerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */; };
|
||||||
A51BFC1E2B2FB5CE00E92F16 /* About.xib in Resources */ = {isa = PBXBuildFile; fileRef = A51BFC1D2B2FB5CE00E92F16 /* About.xib */; };
|
A51BFC1E2B2FB5CE00E92F16 /* About.xib in Resources */ = {isa = PBXBuildFile; fileRef = A51BFC1D2B2FB5CE00E92F16 /* About.xib */; };
|
||||||
A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */; };
|
A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */; };
|
||||||
@ -20,8 +23,12 @@
|
|||||||
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */; };
|
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */; };
|
||||||
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */; };
|
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */; };
|
||||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; };
|
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; };
|
||||||
|
A53D0C8E2B53B0EA00305CE6 /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */; };
|
||||||
|
A53D0C942B53B43700305CE6 /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C932B53B43700305CE6 /* iOSApp.swift */; };
|
||||||
|
A53D0C952B53B4D800305CE6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||||
|
A53D0C9B2B543F3B00305CE6 /* Ghostty.App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */; };
|
||||||
|
A53D0C9C2B543F7B00305CE6 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
||||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55685DF29A03A9F004303CE /* AppError.swift */; };
|
A55685E029A03A9F004303CE /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55685DF29A03A9F004303CE /* AppError.swift */; };
|
||||||
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB529B6F47F0055DE60 /* AppState.swift */; };
|
|
||||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
||||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */; };
|
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */; };
|
||||||
A56B880B2A840447007A0E29 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A56B880A2A840447007A0E29 /* Carbon.framework */; };
|
A56B880B2A840447007A0E29 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A56B880A2A840447007A0E29 /* Carbon.framework */; };
|
||||||
@ -60,6 +67,7 @@
|
|||||||
552964E52B34A9B400030505 /* vim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vim; path = "../zig-out/share/vim"; sourceTree = "<group>"; };
|
552964E52B34A9B400030505 /* vim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vim; path = "../zig-out/share/vim"; sourceTree = "<group>"; };
|
||||||
8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenHandler.swift; sourceTree = "<group>"; };
|
8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenHandler.swift; sourceTree = "<group>"; };
|
||||||
857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
||||||
|
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Config.swift; sourceTree = "<group>"; };
|
||||||
A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = "<group>"; };
|
A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = "<group>"; };
|
||||||
A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = "<group>"; };
|
A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = "<group>"; };
|
||||||
A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; };
|
A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; };
|
||||||
@ -69,8 +77,9 @@
|
|||||||
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Input.swift; sourceTree = "<group>"; };
|
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Input.swift; sourceTree = "<group>"; };
|
||||||
A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
|
A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
|
||||||
|
A53D0C932B53B43700305CE6 /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
|
||||||
|
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.App.swift; sourceTree = "<group>"; };
|
||||||
A55685DF29A03A9F004303CE /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
A55685DF29A03A9F004303CE /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
||||||
A55B7BB529B6F47F0055DE60 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
|
||||||
A55B7BB729B6F53A0055DE60 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
A55B7BB729B6F53A0055DE60 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
||||||
A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView.swift; sourceTree = "<group>"; };
|
A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView.swift; sourceTree = "<group>"; };
|
||||||
A56B880A2A840447007A0E29 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
|
A56B880A2A840447007A0E29 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
|
||||||
@ -99,6 +108,7 @@
|
|||||||
A5CEAFFE29C2410700646FDA /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
|
A5CEAFFE29C2410700646FDA /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
|
||||||
A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalRestorable.swift; sourceTree = "<group>"; };
|
A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalRestorable.swift; sourceTree = "<group>"; };
|
||||||
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableBridge.swift; sourceTree = "<group>"; };
|
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableBridge.swift; sourceTree = "<group>"; };
|
||||||
|
A5D4499D2B53AE7B000F5B83 /* Ghostty-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Ghostty-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = GhosttyKit.xcframework; sourceTree = "<group>"; };
|
A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = GhosttyKit.xcframework; sourceTree = "<group>"; };
|
||||||
A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ClipboardConfirmation.xib; sourceTree = "<group>"; };
|
A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ClipboardConfirmation.xib; sourceTree = "<group>"; };
|
||||||
A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationController.swift; sourceTree = "<group>"; };
|
A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationController.swift; sourceTree = "<group>"; };
|
||||||
@ -117,6 +127,14 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
A5D4499A2B53AE7B000F5B83 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
A53D0C8E2B53B0EA00305CE6 /* GhosttyKit.xcframework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
@ -175,12 +193,37 @@
|
|||||||
path = Settings;
|
path = Settings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
A54CD6ED299BEB14008C95BB /* Sources */ = {
|
A53D0C912B53B41900305CE6 /* App */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A53D0C962B53B57D00305CE6 /* macOS */,
|
||||||
|
A53D0C922B53B42000305CE6 /* iOS */,
|
||||||
|
);
|
||||||
|
path = App;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A53D0C922B53B42000305CE6 /* iOS */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A53D0C932B53B43700305CE6 /* iOSApp.swift */,
|
||||||
|
);
|
||||||
|
path = iOS;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A53D0C962B53B57D00305CE6 /* macOS */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A5FEB2FF2ABB69450068369E /* main.swift */,
|
A5FEB2FF2ABB69450068369E /* main.swift */,
|
||||||
A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */,
|
A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */,
|
||||||
857F63802A5E64F200CA4815 /* MainMenu.xib */,
|
857F63802A5E64F200CA4815 /* MainMenu.xib */,
|
||||||
|
);
|
||||||
|
path = macOS;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A54CD6ED299BEB14008C95BB /* Sources */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A53D0C912B53B41900305CE6 /* App */,
|
||||||
A53426362A7DC53000EBB7A2 /* Features */,
|
A53426362A7DC53000EBB7A2 /* Features */,
|
||||||
A534263D2A7DCBB000EBB7A2 /* Helpers */,
|
A534263D2A7DCBB000EBB7A2 /* Helpers */,
|
||||||
A55B7BB429B6F4410055DE60 /* Ghostty */,
|
A55B7BB429B6F4410055DE60 /* Ghostty */,
|
||||||
@ -192,9 +235,10 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A55B7BB729B6F53A0055DE60 /* Package.swift */,
|
A55B7BB729B6F53A0055DE60 /* Package.swift */,
|
||||||
A55B7BB529B6F47F0055DE60 /* AppState.swift */,
|
|
||||||
A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */,
|
A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */,
|
||||||
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */,
|
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */,
|
||||||
|
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */,
|
||||||
|
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */,
|
||||||
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */,
|
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */,
|
||||||
A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */,
|
A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */,
|
||||||
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */,
|
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */,
|
||||||
@ -255,6 +299,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A5B30531299BEAAA0047F10C /* Ghostty.app */,
|
A5B30531299BEAAA0047F10C /* Ghostty.app */,
|
||||||
|
A5D4499D2B53AE7B000F5B83 /* Ghostty-iOS.app */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -310,6 +355,23 @@
|
|||||||
productReference = A5B30531299BEAAA0047F10C /* Ghostty.app */;
|
productReference = A5B30531299BEAAA0047F10C /* Ghostty.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
A5D4499C2B53AE7B000F5B83 /* Ghostty-iOS */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = A5D449AB2B53AE7B000F5B83 /* Build configuration list for PBXNativeTarget "Ghostty-iOS" */;
|
||||||
|
buildPhases = (
|
||||||
|
A5D449992B53AE7B000F5B83 /* Sources */,
|
||||||
|
A5D4499A2B53AE7B000F5B83 /* Frameworks */,
|
||||||
|
A5D4499B2B53AE7B000F5B83 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = "Ghostty-iOS";
|
||||||
|
productName = "Ghostty-iOS";
|
||||||
|
productReference = A5D4499D2B53AE7B000F5B83 /* Ghostty-iOS.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@ -317,12 +379,15 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = 1;
|
BuildIndependentTargetsInParallel = 1;
|
||||||
LastSwiftUpdateCheck = 1420;
|
LastSwiftUpdateCheck = 1520;
|
||||||
LastUpgradeCheck = 1420;
|
LastUpgradeCheck = 1420;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
A5B30530299BEAAA0047F10C = {
|
A5B30530299BEAAA0047F10C = {
|
||||||
CreatedOnToolsVersion = 14.2;
|
CreatedOnToolsVersion = 14.2;
|
||||||
};
|
};
|
||||||
|
A5D4499C2B53AE7B000F5B83 = {
|
||||||
|
CreatedOnToolsVersion = 15.2;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = A5B3052C299BEAAA0047F10C /* Build configuration list for PBXProject "Ghostty" */;
|
buildConfigurationList = A5B3052C299BEAAA0047F10C /* Build configuration list for PBXProject "Ghostty" */;
|
||||||
@ -342,6 +407,7 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
A5B30530299BEAAA0047F10C /* Ghostty */,
|
A5B30530299BEAAA0047F10C /* Ghostty */,
|
||||||
|
A5D4499C2B53AE7B000F5B83 /* Ghostty-iOS */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@ -363,6 +429,14 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
A5D4499B2B53AE7B000F5B83 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
A53D0C952B53B4D800305CE6 /* Assets.xcassets in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@ -371,6 +445,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
||||||
|
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
||||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
||||||
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
|
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
|
||||||
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
||||||
@ -390,7 +465,6 @@
|
|||||||
A5FEB3002ABB69450068369E /* main.swift in Sources */,
|
A5FEB3002ABB69450068369E /* main.swift in Sources */,
|
||||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
||||||
A51B78472AF4B58B00F3EDB9 /* TerminalWindow.swift in Sources */,
|
A51B78472AF4B58B00F3EDB9 /* TerminalWindow.swift in Sources */,
|
||||||
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */,
|
|
||||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
||||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||||
@ -403,6 +477,18 @@
|
|||||||
A596309E2AEE1D6C00D64628 /* TerminalView.swift in Sources */,
|
A596309E2AEE1D6C00D64628 /* TerminalView.swift in Sources */,
|
||||||
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */,
|
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */,
|
||||||
A5E112972AF7401B00C6E0C2 /* ClipboardConfirmationView.swift in Sources */,
|
A5E112972AF7401B00C6E0C2 /* ClipboardConfirmationView.swift in Sources */,
|
||||||
|
A514C8D82B54DC6800493A16 /* Ghostty.App.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
A5D449992B53AE7B000F5B83 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
A53D0C942B53B43700305CE6 /* iOSApp.swift in Sources */,
|
||||||
|
A514C8D72B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
||||||
|
A53D0C9C2B543F7B00305CE6 /* Package.swift in Sources */,
|
||||||
|
A53D0C9B2B543F3B00305CE6 /* Ghostty.App.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -682,6 +768,123 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
A5D449A82B53AE7B000F5B83 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
|
DEVELOPMENT_TEAM = "";
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 0.1;
|
||||||
|
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.mitchellh.ghostty-ios";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
A5D449A92B53AE7B000F5B83 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
|
DEVELOPMENT_TEAM = "";
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 0.1;
|
||||||
|
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.mitchellh.ghostty-ios";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
A5D449AA2B53AE7B000F5B83 /* ReleaseLocal */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
|
DEVELOPMENT_TEAM = "";
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 0.1;
|
||||||
|
"OTHER_LDFLAGS[arch=*]" = "-lstdc++";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.mitchellh.ghostty-ios";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = ReleaseLocal;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@ -705,6 +908,16 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = ReleaseLocal;
|
defaultConfigurationName = ReleaseLocal;
|
||||||
};
|
};
|
||||||
|
A5D449AB2B53AE7B000F5B83 /* Build configuration list for PBXNativeTarget "Ghostty-iOS" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
A5D449A82B53AE7B000F5B83 /* Debug */,
|
||||||
|
A5D449A92B53AE7B000F5B83 /* Release */,
|
||||||
|
A5D449AA2B53AE7B000F5B83 /* ReleaseLocal */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = ReleaseLocal;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
33
macos/Sources/App/iOS/iOSApp.swift
Normal file
33
macos/Sources/App/iOS/iOSApp.swift
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct Ghostty_iOSApp: App {
|
||||||
|
@StateObject private var ghostty_app = Ghostty.App()
|
||||||
|
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
iOS_ContentView()
|
||||||
|
.environmentObject(ghostty_app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iOS_ContentView: View {
|
||||||
|
@EnvironmentObject private var ghostty_app: Ghostty.App
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
Image("AppIconImage")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(maxHeight: 96)
|
||||||
|
Text("Ghostty")
|
||||||
|
Text("State: \(ghostty_app.readiness.rawValue)")
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
iOS_ContentView()
|
||||||
|
}
|
@ -8,7 +8,7 @@ class AppDelegate: NSObject,
|
|||||||
ObservableObject,
|
ObservableObject,
|
||||||
NSApplicationDelegate,
|
NSApplicationDelegate,
|
||||||
UNUserNotificationCenterDelegate,
|
UNUserNotificationCenterDelegate,
|
||||||
GhosttyAppStateDelegate
|
GhosttyAppDelegate
|
||||||
{
|
{
|
||||||
// The application logger. We should probably move this at some point to a dedicated
|
// The application logger. We should probably move this at some point to a dedicated
|
||||||
// class/struct but for now it lives here! 🤷♂️
|
// class/struct but for now it lives here! 🤷♂️
|
||||||
@ -62,7 +62,7 @@ class AppDelegate: NSObject,
|
|||||||
private var applicationHasBecomeActive: Bool = false
|
private var applicationHasBecomeActive: Bool = false
|
||||||
|
|
||||||
/// The ghostty global state. Only one per process.
|
/// The ghostty global state. Only one per process.
|
||||||
let ghostty: Ghostty.AppState = Ghostty.AppState()
|
let ghostty: Ghostty.App = Ghostty.App()
|
||||||
|
|
||||||
/// Manages our terminal windows.
|
/// Manages our terminal windows.
|
||||||
let terminalManager: TerminalManager
|
let terminalManager: TerminalManager
|
||||||
@ -143,7 +143,7 @@ class AppDelegate: NSObject,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
return ghostty.shouldQuitAfterLastWindowClosed
|
return ghostty.config.shouldQuitAfterLastWindowClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
||||||
@ -242,7 +242,7 @@ class AppDelegate: NSObject,
|
|||||||
|
|
||||||
/// Sync all of our menu item keyboard shortcuts with the Ghostty configuration.
|
/// Sync all of our menu item keyboard shortcuts with the Ghostty configuration.
|
||||||
private func syncMenuShortcuts() {
|
private func syncMenuShortcuts() {
|
||||||
guard ghostty.config != nil else { return }
|
guard ghostty.readiness == .ready else { return }
|
||||||
|
|
||||||
syncMenuShortcut(action: "open_config", menuItem: self.menuOpenConfig)
|
syncMenuShortcut(action: "open_config", menuItem: self.menuOpenConfig)
|
||||||
syncMenuShortcut(action: "reload_config", menuItem: self.menuReloadConfig)
|
syncMenuShortcut(action: "reload_config", menuItem: self.menuReloadConfig)
|
||||||
@ -286,19 +286,16 @@ class AppDelegate: NSObject,
|
|||||||
/// Syncs a single menu shortcut for the given action. The action string is the same
|
/// Syncs a single menu shortcut for the given action. The action string is the same
|
||||||
/// action string used for the Ghostty configuration.
|
/// action string used for the Ghostty configuration.
|
||||||
private func syncMenuShortcut(action: String, menuItem: NSMenuItem?) {
|
private func syncMenuShortcut(action: String, menuItem: NSMenuItem?) {
|
||||||
guard let cfg = ghostty.config else { return }
|
|
||||||
guard let menu = menuItem else { return }
|
guard let menu = menuItem else { return }
|
||||||
|
guard let equiv = ghostty.config.keyEquivalent(for: action) else {
|
||||||
let trigger = ghostty_config_trigger(cfg, action, UInt(action.count))
|
|
||||||
guard let equiv = Ghostty.keyEquivalent(key: trigger.key) else {
|
|
||||||
// No shortcut, clear the menu item
|
// No shortcut, clear the menu item
|
||||||
menu.keyEquivalent = ""
|
menu.keyEquivalent = ""
|
||||||
menu.keyEquivalentModifierMask = []
|
menu.keyEquivalentModifierMask = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.keyEquivalent = equiv
|
menu.keyEquivalent = equiv.key
|
||||||
menu.keyEquivalentModifierMask = Ghostty.eventModifierFlags(mods: trigger.mods)
|
menu.keyEquivalentModifierMask = equiv.modifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
private func focusedSurface() -> ghostty_surface_t? {
|
private func focusedSurface() -> ghostty_surface_t? {
|
||||||
@ -341,7 +338,7 @@ class AppDelegate: NSObject,
|
|||||||
withCompletionHandler(options)
|
withCompletionHandler(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - GhosttyAppStateDelegate
|
//MARK: - GhosttyAppDelegate
|
||||||
|
|
||||||
func findSurface(forUUID uuid: UUID) -> Ghostty.SurfaceView? {
|
func findSurface(forUUID uuid: UUID) -> Ghostty.SurfaceView? {
|
||||||
for c in terminalManager.windows {
|
for c in terminalManager.windows {
|
||||||
@ -353,11 +350,11 @@ class AppDelegate: NSObject,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func configDidReload(_ state: Ghostty.AppState) {
|
func configDidReload(_ state: Ghostty.App) {
|
||||||
// Depending on the "window-save-state" setting we have to set the NSQuitAlwaysKeepsWindows
|
// Depending on the "window-save-state" setting we have to set the NSQuitAlwaysKeepsWindows
|
||||||
// configuration. This is the only way to carefully control whether macOS invokes the
|
// configuration. This is the only way to carefully control whether macOS invokes the
|
||||||
// state restoration system.
|
// state restoration system.
|
||||||
switch (ghostty.windowSaveState) {
|
switch (ghostty.config.windowSaveState) {
|
||||||
case "never": UserDefaults.standard.setValue(false, forKey: "NSQuitAlwaysKeepsWindows")
|
case "never": UserDefaults.standard.setValue(false, forKey: "NSQuitAlwaysKeepsWindows")
|
||||||
case "always": UserDefaults.standard.setValue(true, forKey: "NSQuitAlwaysKeepsWindows")
|
case "always": UserDefaults.standard.setValue(true, forKey: "NSQuitAlwaysKeepsWindows")
|
||||||
case "default": fallthrough
|
case "default": fallthrough
|
||||||
@ -373,7 +370,7 @@ class AppDelegate: NSObject,
|
|||||||
|
|
||||||
// If we have configuration errors, we need to show them.
|
// If we have configuration errors, we need to show them.
|
||||||
let c = ConfigurationErrorsController.sharedInstance
|
let c = ConfigurationErrorsController.sharedInstance
|
||||||
c.errors = state.configErrors()
|
c.errors = state.config.errors
|
||||||
if (c.errors.count > 0) {
|
if (c.errors.count > 0) {
|
||||||
if (c.window == nil || !c.window!.isVisible) {
|
if (c.window == nil || !c.window!.isVisible) {
|
||||||
c.showWindow(self)
|
c.showWindow(self)
|
||||||
@ -383,7 +380,7 @@ class AppDelegate: NSObject,
|
|||||||
|
|
||||||
/// Sync the appearance of our app with the theme specified in the config.
|
/// Sync the appearance of our app with the theme specified in the config.
|
||||||
private func syncAppearance() {
|
private func syncAppearance() {
|
||||||
guard let theme = ghostty.windowTheme else { return }
|
guard let theme = ghostty.config.windowTheme else { return }
|
||||||
switch (theme) {
|
switch (theme) {
|
||||||
case "dark":
|
case "dark":
|
||||||
let appearance = NSAppearance(named: .darkAqua)
|
let appearance = NSAppearance(named: .darkAqua)
|
@ -11,7 +11,7 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
override var windowNibName: NSNib.Name? { "Terminal" }
|
override var windowNibName: NSNib.Name? { "Terminal" }
|
||||||
|
|
||||||
/// The app instance that this terminal view will represent.
|
/// The app instance that this terminal view will represent.
|
||||||
let ghostty: Ghostty.AppState
|
let ghostty: Ghostty.App
|
||||||
|
|
||||||
/// The currently focused surface.
|
/// The currently focused surface.
|
||||||
var focusedSurface: Ghostty.SurfaceView? = nil
|
var focusedSurface: Ghostty.SurfaceView? = nil
|
||||||
@ -46,7 +46,7 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
/// changes in the list.
|
/// changes in the list.
|
||||||
private var tabWindowsHash: Int = 0
|
private var tabWindowsHash: Int = 0
|
||||||
|
|
||||||
init(_ ghostty: Ghostty.AppState,
|
init(_ ghostty: Ghostty.App,
|
||||||
withBaseConfig base: Ghostty.SurfaceConfiguration? = nil,
|
withBaseConfig base: Ghostty.SurfaceConfiguration? = nil,
|
||||||
withSurfaceTree tree: Ghostty.SplitNode? = nil
|
withSurfaceTree tree: Ghostty.SplitNode? = nil
|
||||||
) {
|
) {
|
||||||
@ -101,7 +101,6 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
tabListenForFrame = false
|
tabListenForFrame = false
|
||||||
|
|
||||||
guard let windows = self.window?.tabbedWindows else { return }
|
guard let windows = self.window?.tabbedWindows else { return }
|
||||||
guard let cfg = ghostty.config else { return }
|
|
||||||
|
|
||||||
// We only listen for frame changes if we have more than 1 window,
|
// We only listen for frame changes if we have more than 1 window,
|
||||||
// otherwise the accessory view doesn't matter.
|
// otherwise the accessory view doesn't matter.
|
||||||
@ -109,8 +108,7 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
|
|
||||||
for (index, window) in windows.enumerated().prefix(9) {
|
for (index, window) in windows.enumerated().prefix(9) {
|
||||||
let action = "goto_tab:\(index + 1)"
|
let action = "goto_tab:\(index + 1)"
|
||||||
let trigger = ghostty_config_trigger(cfg, action, UInt(action.count))
|
guard let equiv = ghostty.config.keyEquivalent(for: action) else {
|
||||||
guard let equiv = Ghostty.keyEquivalentLabel(key: trigger.key, mods: trigger.mods) else {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,13 +155,13 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
window.identifier = .init(String(describing: TerminalWindowRestoration.self))
|
window.identifier = .init(String(describing: TerminalWindowRestoration.self))
|
||||||
|
|
||||||
// If window decorations are disabled, remove our title
|
// If window decorations are disabled, remove our title
|
||||||
if (!ghostty.windowDecorations) { window.styleMask.remove(.titled) }
|
if (!ghostty.config.windowDecorations) { window.styleMask.remove(.titled) }
|
||||||
|
|
||||||
// Terminals typically operate in sRGB color space and macOS defaults
|
// Terminals typically operate in sRGB color space and macOS defaults
|
||||||
// to "native" which is typically P3. There is a lot more resources
|
// to "native" which is typically P3. There is a lot more resources
|
||||||
// covered in thie GitHub issue: https://github.com/mitchellh/ghostty/pull/376
|
// covered in thie GitHub issue: https://github.com/mitchellh/ghostty/pull/376
|
||||||
// Ghostty defaults to sRGB but this can be overridden.
|
// Ghostty defaults to sRGB but this can be overridden.
|
||||||
switch (ghostty.windowColorspace) {
|
switch (ghostty.config.windowColorspace) {
|
||||||
case "display-p3":
|
case "display-p3":
|
||||||
window.colorSpace = .displayP3
|
window.colorSpace = .displayP3
|
||||||
case "srgb":
|
case "srgb":
|
||||||
@ -462,7 +460,7 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cellSizeDidChange(to: NSSize) {
|
func cellSizeDidChange(to: NSSize) {
|
||||||
guard ghostty.windowStepResize else { return }
|
guard ghostty.config.windowStepResize else { return }
|
||||||
self.window?.contentResizeIncrements = to
|
self.window?.contentResizeIncrements = to
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,7 +502,7 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
str = cc.contents
|
str = cc.contents
|
||||||
}
|
}
|
||||||
|
|
||||||
Ghostty.AppState.completeClipboardRequest(cc.surface, data: str, state: cc.state, confirmed: true)
|
Ghostty.App.completeClipboardRequest(cc.surface, data: str, state: cc.state, confirmed: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,7 +589,7 @@ class TerminalController: NSWindowController, NSWindowDelegate,
|
|||||||
// If we already have a clipboard confirmation view up, we ignore this request.
|
// If we already have a clipboard confirmation view up, we ignore this request.
|
||||||
// This shouldn't be possible...
|
// This shouldn't be possible...
|
||||||
guard self.clipboardConfirmation == nil else {
|
guard self.clipboardConfirmation == nil else {
|
||||||
Ghostty.AppState.completeClipboardRequest(surface, data: "", state: state, confirmed: true)
|
Ghostty.App.completeClipboardRequest(surface, data: "", state: state, confirmed: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ class TerminalManager {
|
|||||||
let closePublisher: AnyCancellable
|
let closePublisher: AnyCancellable
|
||||||
}
|
}
|
||||||
|
|
||||||
let ghostty: Ghostty.AppState
|
let ghostty: Ghostty.App
|
||||||
|
|
||||||
/// The currently focused surface of the main window.
|
/// The currently focused surface of the main window.
|
||||||
var focusedSurface: Ghostty.SurfaceView? { mainWindow?.controller.focusedSurface }
|
var focusedSurface: Ghostty.SurfaceView? { mainWindow?.controller.focusedSurface }
|
||||||
@ -37,7 +37,7 @@ class TerminalManager {
|
|||||||
return windows.last
|
return windows.last
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ ghostty: Ghostty.AppState) {
|
init(_ ghostty: Ghostty.App) {
|
||||||
self.ghostty = ghostty
|
self.ghostty = ghostty
|
||||||
|
|
||||||
let center = NotificationCenter.default
|
let center = NotificationCenter.default
|
||||||
@ -66,7 +66,7 @@ class TerminalManager {
|
|||||||
let window = c.window!
|
let window = c.window!
|
||||||
|
|
||||||
// We want to go fullscreen if we're configured for new windows to go fullscreen
|
// We want to go fullscreen if we're configured for new windows to go fullscreen
|
||||||
var toggleFullScreen = ghostty.windowFullscreen
|
var toggleFullScreen = ghostty.config.windowFullscreen
|
||||||
|
|
||||||
// If the previous focused window prior to creating this window is fullscreen,
|
// If the previous focused window prior to creating this window is fullscreen,
|
||||||
// then this window also becomes fullscreen.
|
// then this window also becomes fullscreen.
|
||||||
@ -130,7 +130,7 @@ class TerminalManager {
|
|||||||
controller.showWindow(self)
|
controller.showWindow(self)
|
||||||
|
|
||||||
// Add the window to the tab group and show it.
|
// Add the window to the tab group and show it.
|
||||||
switch ghostty.windowNewTabPosition {
|
switch ghostty.config.windowNewTabPosition {
|
||||||
case "end":
|
case "end":
|
||||||
// If we already have a tab group and we want the new tab to open at the end,
|
// If we already have a tab group and we want the new tab to open at the end,
|
||||||
// then we use the last window in the tab group as the parent.
|
// then we use the last window in the tab group as the parent.
|
||||||
|
@ -66,7 +66,7 @@ class TerminalWindowRestoration: NSObject, NSWindowRestoration {
|
|||||||
|
|
||||||
// If our configuration is "never" then we never restore the state
|
// If our configuration is "never" then we never restore the state
|
||||||
// no matter what.
|
// no matter what.
|
||||||
if (appDelegate.terminalManager.ghostty.windowSaveState == "never") {
|
if (appDelegate.terminalManager.ghostty.config.windowSaveState == "never") {
|
||||||
completionHandler(nil, nil)
|
completionHandler(nil, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ protocol TerminalViewModel: ObservableObject {
|
|||||||
|
|
||||||
/// The main terminal view. This terminal view supports splits.
|
/// The main terminal view. This terminal view supports splits.
|
||||||
struct TerminalView<ViewModel: TerminalViewModel>: View {
|
struct TerminalView<ViewModel: TerminalViewModel>: View {
|
||||||
@ObservedObject var ghostty: Ghostty.AppState
|
@ObservedObject var ghostty: Ghostty.App
|
||||||
|
|
||||||
// The required view model
|
// The required view model
|
||||||
@ObservedObject var viewModel: ViewModel
|
@ObservedObject var viewModel: ViewModel
|
||||||
@ -83,7 +83,7 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
|||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
// If we're running in debug mode we show a warning so that users
|
// If we're running in debug mode we show a warning so that users
|
||||||
// know that performance will be degraded.
|
// know that performance will be degraded.
|
||||||
if (ghostty.info.mode == GHOSTTY_BUILD_MODE_DEBUG) {
|
if (Ghostty.info.mode == GHOSTTY_BUILD_MODE_DEBUG) {
|
||||||
DebugBuildWarningView()
|
DebugBuildWarningView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,50 +2,35 @@ import SwiftUI
|
|||||||
import UserNotifications
|
import UserNotifications
|
||||||
import GhosttyKit
|
import GhosttyKit
|
||||||
|
|
||||||
protocol GhosttyAppStateDelegate: AnyObject {
|
protocol GhosttyAppDelegate: AnyObject {
|
||||||
/// Called when the configuration did finish reloading.
|
/// Called when the configuration did finish reloading.
|
||||||
func configDidReload(_ state: Ghostty.AppState)
|
func configDidReload(_ app: Ghostty.App)
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
/// Called when a callback needs access to a specific surface. This should return nil
|
/// Called when a callback needs access to a specific surface. This should return nil
|
||||||
/// when the surface is no longer valid.
|
/// when the surface is no longer valid.
|
||||||
func findSurface(forUUID uuid: UUID) -> Ghostty.SurfaceView?
|
func findSurface(forUUID uuid: UUID) -> Ghostty.SurfaceView?
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Ghostty {
|
extension Ghostty {
|
||||||
enum AppReadiness {
|
// IMPORTANT: THIS IS NOT DONE.
|
||||||
|
// This is a refactor/redo of Ghostty.AppState so that it supports both macOS and iOS
|
||||||
|
class App: ObservableObject {
|
||||||
|
enum Readiness: String {
|
||||||
case loading, error, ready
|
case loading, error, ready
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FontSizeModification {
|
|
||||||
case increase(Int)
|
|
||||||
case decrease(Int)
|
|
||||||
case reset
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Info {
|
|
||||||
var mode: ghostty_build_mode_e
|
|
||||||
var version: String
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The AppState is the global state that is associated with the Swift app. This handles initially
|
|
||||||
/// initializing Ghostty, loading the configuration, etc.
|
|
||||||
class AppState: ObservableObject {
|
|
||||||
/// The readiness value of the state.
|
|
||||||
@Published var readiness: AppReadiness = .loading
|
|
||||||
|
|
||||||
/// Optional delegate
|
/// Optional delegate
|
||||||
weak var delegate: GhosttyAppStateDelegate?
|
weak var delegate: GhosttyAppDelegate?
|
||||||
|
|
||||||
/// The ghostty global configuration. This should only be changed when it is definitely
|
/// The readiness value of the state.
|
||||||
/// safe to change. It is definite safe to change only when the embedded app runtime
|
@Published var readiness: Readiness = .loading
|
||||||
/// in Ghostty says so (usually, only in a reload configuration callback).
|
|
||||||
@Published var config: ghostty_config_t? = nil {
|
/// The global app configuration. This defines the app level configuration plus any behavior
|
||||||
didSet {
|
/// for new windows, tabs, etc. Note that when creating a new window, it may inherit some
|
||||||
// Free the old value whenever we change
|
/// configuration (i.e. font size) from the previously focused window. This would override this.
|
||||||
guard let old = oldValue else { return }
|
private(set) var config: Config
|
||||||
ghostty_config_free(old)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The ghostty app instance. We only have one of these for the entire app, although I guess
|
/// The ghostty app instance. We only have one of these for the entire app, although I guess
|
||||||
/// in theory you can have multiple... I don't know why you would...
|
/// in theory you can have multiple... I don't know why you would...
|
||||||
@ -56,173 +41,77 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True if we should quit when the last window is closed.
|
|
||||||
var shouldQuitAfterLastWindowClosed: Bool {
|
|
||||||
guard let config = self.config else { return true }
|
|
||||||
var v = false;
|
|
||||||
let key = "quit-after-last-window-closed"
|
|
||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
/// window-colorspace
|
|
||||||
var windowColorspace: String {
|
|
||||||
guard let config = self.config else { return "" }
|
|
||||||
var v: UnsafePointer<Int8>? = nil
|
|
||||||
let key = "window-colorspace"
|
|
||||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
|
|
||||||
guard let ptr = v else { return "" }
|
|
||||||
return String(cString: ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// window-save-state
|
|
||||||
var windowSaveState: String {
|
|
||||||
guard let config = self.config else { return "" }
|
|
||||||
var v: UnsafePointer<Int8>? = nil
|
|
||||||
let key = "window-save-state"
|
|
||||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
|
|
||||||
guard let ptr = v else { return "" }
|
|
||||||
return String(cString: ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// window-new-tab-position
|
|
||||||
var windowNewTabPosition: String {
|
|
||||||
guard let config = self.config else { return "" }
|
|
||||||
var v: UnsafePointer<Int8>? = nil
|
|
||||||
let key = "window-new-tab-position"
|
|
||||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
|
|
||||||
guard let ptr = v else { return "" }
|
|
||||||
return String(cString: ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// True if we need to confirm before quitting.
|
/// True if we need to confirm before quitting.
|
||||||
var needsConfirmQuit: Bool {
|
var needsConfirmQuit: Bool {
|
||||||
guard let app = app else { return false }
|
guard let app = app else { return false }
|
||||||
return ghostty_app_needs_confirm_quit(app)
|
return ghostty_app_needs_confirm_quit(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build information
|
|
||||||
var info: Info {
|
|
||||||
let raw = ghostty_info()
|
|
||||||
let version = NSString(
|
|
||||||
bytes: raw.version,
|
|
||||||
length: Int(raw.version_len),
|
|
||||||
encoding: NSUTF8StringEncoding
|
|
||||||
) ?? "unknown"
|
|
||||||
|
|
||||||
return Info(mode: raw.build_mode, version: String(version))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// True if we want to render window decorations
|
|
||||||
var windowDecorations: Bool {
|
|
||||||
guard let config = self.config else { return true }
|
|
||||||
var v = false;
|
|
||||||
let key = "window-decoration"
|
|
||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The window theme as a string.
|
|
||||||
var windowTheme: String? {
|
|
||||||
guard let config = self.config else { return nil }
|
|
||||||
var v: UnsafePointer<Int8>? = nil
|
|
||||||
let key = "window-theme"
|
|
||||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return nil }
|
|
||||||
guard let ptr = v else { return nil }
|
|
||||||
return String(cString: ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether to resize windows in discrete steps or use "fluid" resizing
|
|
||||||
var windowStepResize: Bool {
|
|
||||||
guard let config = self.config else { return true }
|
|
||||||
var v = false
|
|
||||||
let key = "window-step-resize"
|
|
||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether to open new windows in fullscreen.
|
|
||||||
var windowFullscreen: Bool {
|
|
||||||
guard let config = self.config else { return true }
|
|
||||||
var v = false
|
|
||||||
let key = "fullscreen"
|
|
||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The background opacity.
|
|
||||||
var backgroundOpacity: Double {
|
|
||||||
guard let config = self.config else { return 1 }
|
|
||||||
var v: Double = 1
|
|
||||||
let key = "background-opacity"
|
|
||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// Initialize ghostty global state. This happens once per process.
|
// Initialize ghostty global state. This happens once per process.
|
||||||
guard ghostty_init() == GHOSTTY_SUCCESS else {
|
if ghostty_init() != GHOSTTY_SUCCESS {
|
||||||
AppDelegate.logger.critical("ghostty_init failed")
|
logger.critical("ghostty_init failed, weird things may happen")
|
||||||
readiness = .error
|
readiness = .error
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the global configuration.
|
// Initialize the global configuration.
|
||||||
guard let cfg = Self.loadConfig() else {
|
self.config = Config()
|
||||||
|
if self.config.config == nil {
|
||||||
readiness = .error
|
readiness = .error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.config = cfg;
|
|
||||||
|
|
||||||
// Create our "runtime" config. The "runtime" is the configuration that ghostty
|
// Create our "runtime" config. The "runtime" is the configuration that ghostty
|
||||||
// uses to interface with the application runtime environment.
|
// uses to interface with the application runtime environment.
|
||||||
var runtime_cfg = ghostty_runtime_config_s(
|
var runtime_cfg = ghostty_runtime_config_s(
|
||||||
userdata: Unmanaged.passUnretained(self).toOpaque(),
|
userdata: Unmanaged.passUnretained(self).toOpaque(),
|
||||||
supports_selection_clipboard: false,
|
supports_selection_clipboard: false,
|
||||||
wakeup_cb: { userdata in AppState.wakeup(userdata) },
|
wakeup_cb: { userdata in App.wakeup(userdata) },
|
||||||
reload_config_cb: { userdata in AppState.reloadConfig(userdata) },
|
reload_config_cb: { userdata in App.reloadConfig(userdata) },
|
||||||
open_config_cb: { userdata in AppState.openConfig(userdata) },
|
open_config_cb: { userdata in App.openConfig(userdata) },
|
||||||
set_title_cb: { userdata, title in AppState.setTitle(userdata, title: title) },
|
set_title_cb: { userdata, title in App.setTitle(userdata, title: title) },
|
||||||
set_mouse_shape_cb: { userdata, shape in AppState.setMouseShape(userdata, shape: shape) },
|
set_mouse_shape_cb: { userdata, shape in App.setMouseShape(userdata, shape: shape) },
|
||||||
set_mouse_visibility_cb: { userdata, visible in AppState.setMouseVisibility(userdata, visible: visible) },
|
set_mouse_visibility_cb: { userdata, visible in App.setMouseVisibility(userdata, visible: visible) },
|
||||||
read_clipboard_cb: { userdata, loc, state in AppState.readClipboard(userdata, location: loc, state: state) },
|
read_clipboard_cb: { userdata, loc, state in App.readClipboard(userdata, location: loc, state: state) },
|
||||||
confirm_read_clipboard_cb: { userdata, str, state, request in AppState.confirmReadClipboard(userdata, string: str, state: state, request: request ) },
|
confirm_read_clipboard_cb: { userdata, str, state, request in App.confirmReadClipboard(userdata, string: str, state: state, request: request ) },
|
||||||
write_clipboard_cb: { userdata, str, loc, confirm in AppState.writeClipboard(userdata, string: str, location: loc, confirm: confirm) },
|
write_clipboard_cb: { userdata, str, loc, confirm in App.writeClipboard(userdata, string: str, location: loc, confirm: confirm) },
|
||||||
new_split_cb: { userdata, direction, surfaceConfig in AppState.newSplit(userdata, direction: direction, config: surfaceConfig) },
|
new_split_cb: { userdata, direction, surfaceConfig in App.newSplit(userdata, direction: direction, config: surfaceConfig) },
|
||||||
new_tab_cb: { userdata, surfaceConfig in AppState.newTab(userdata, config: surfaceConfig) },
|
new_tab_cb: { userdata, surfaceConfig in App.newTab(userdata, config: surfaceConfig) },
|
||||||
new_window_cb: { userdata, surfaceConfig in AppState.newWindow(userdata, config: surfaceConfig) },
|
new_window_cb: { userdata, surfaceConfig in App.newWindow(userdata, config: surfaceConfig) },
|
||||||
control_inspector_cb: { userdata, mode in AppState.controlInspector(userdata, mode: mode) },
|
control_inspector_cb: { userdata, mode in App.controlInspector(userdata, mode: mode) },
|
||||||
close_surface_cb: { userdata, processAlive in AppState.closeSurface(userdata, processAlive: processAlive) },
|
close_surface_cb: { userdata, processAlive in App.closeSurface(userdata, processAlive: processAlive) },
|
||||||
focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) },
|
focus_split_cb: { userdata, direction in App.focusSplit(userdata, direction: direction) },
|
||||||
resize_split_cb: { userdata, direction, amount in
|
resize_split_cb: { userdata, direction, amount in
|
||||||
AppState.resizeSplit(userdata, direction: direction, amount: amount) },
|
App.resizeSplit(userdata, direction: direction, amount: amount) },
|
||||||
equalize_splits_cb: { userdata in
|
equalize_splits_cb: { userdata in
|
||||||
AppState.equalizeSplits(userdata) },
|
App.equalizeSplits(userdata) },
|
||||||
toggle_split_zoom_cb: { userdata in AppState.toggleSplitZoom(userdata) },
|
toggle_split_zoom_cb: { userdata in App.toggleSplitZoom(userdata) },
|
||||||
goto_tab_cb: { userdata, n in AppState.gotoTab(userdata, n: n) },
|
goto_tab_cb: { userdata, n in App.gotoTab(userdata, n: n) },
|
||||||
toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, nonNativeFullscreen: nonNativeFullscreen) },
|
toggle_fullscreen_cb: { userdata, nonNativeFullscreen in App.toggleFullscreen(userdata, nonNativeFullscreen: nonNativeFullscreen) },
|
||||||
set_initial_window_size_cb: { userdata, width, height in AppState.setInitialWindowSize(userdata, width: width, height: height) },
|
set_initial_window_size_cb: { userdata, width, height in App.setInitialWindowSize(userdata, width: width, height: height) },
|
||||||
render_inspector_cb: { userdata in AppState.renderInspector(userdata) },
|
render_inspector_cb: { userdata in App.renderInspector(userdata) },
|
||||||
set_cell_size_cb: { userdata, width, height in AppState.setCellSize(userdata, width: width, height: height) },
|
set_cell_size_cb: { userdata, width, height in App.setCellSize(userdata, width: width, height: height) },
|
||||||
show_desktop_notification_cb: { userdata, title, body in
|
show_desktop_notification_cb: { userdata, title, body in
|
||||||
AppState.showUserNotification(userdata, title: title, body: body)
|
App.showUserNotification(userdata, title: title, body: body)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create the ghostty app.
|
// Create the ghostty app.
|
||||||
guard let app = ghostty_app_new(&runtime_cfg, cfg) else {
|
guard let app = ghostty_app_new(&runtime_cfg, config.config) else {
|
||||||
AppDelegate.logger.critical("ghostty_app_new failed")
|
logger.critical("ghostty_app_new failed")
|
||||||
readiness = .error
|
readiness = .error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
// Subscribe to notifications for keyboard layout change so that we can update Ghostty.
|
// Subscribe to notifications for keyboard layout change so that we can update Ghostty.
|
||||||
NotificationCenter.default.addObserver(
|
NotificationCenter.default.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(self.keyboardSelectionDidChange(notification:)),
|
selector: #selector(self.keyboardSelectionDidChange(notification:)),
|
||||||
name: NSTextInputContext.keyboardSelectionDidChangeNotification,
|
name: NSTextInputContext.keyboardSelectionDidChangeNotification,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
#endif
|
||||||
|
|
||||||
self.readiness = .ready
|
self.readiness = .ready
|
||||||
}
|
}
|
||||||
@ -230,66 +119,17 @@ extension Ghostty {
|
|||||||
deinit {
|
deinit {
|
||||||
// This will force the didSet callbacks to run which free.
|
// This will force the didSet callbacks to run which free.
|
||||||
self.app = nil
|
self.app = nil
|
||||||
self.config = nil
|
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
// Remove our observer
|
// Remove our observer
|
||||||
NotificationCenter.default.removeObserver(
|
NotificationCenter.default.removeObserver(
|
||||||
self,
|
self,
|
||||||
name: NSTextInputContext.keyboardSelectionDidChangeNotification,
|
name: NSTextInputContext.keyboardSelectionDidChangeNotification,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes a new configuration and loads all the values.
|
// MARK: App Operations
|
||||||
static func loadConfig() -> ghostty_config_t? {
|
|
||||||
// Initialize the global configuration.
|
|
||||||
guard let cfg = ghostty_config_new() else {
|
|
||||||
AppDelegate.logger.critical("ghostty_config_new failed")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load our configuration files from the home directory.
|
|
||||||
ghostty_config_load_default_files(cfg);
|
|
||||||
ghostty_config_load_cli_args(cfg);
|
|
||||||
ghostty_config_load_recursive_files(cfg);
|
|
||||||
|
|
||||||
// TODO: we'd probably do some config loading here... for now we'd
|
|
||||||
// have to do this synchronously. When we support config updating we can do
|
|
||||||
// this async and update later.
|
|
||||||
|
|
||||||
// Finalize will make our defaults available.
|
|
||||||
ghostty_config_finalize(cfg)
|
|
||||||
|
|
||||||
// Log any configuration errors. These will be automatically shown in a
|
|
||||||
// pop-up window too.
|
|
||||||
let errCount = ghostty_config_errors_count(cfg)
|
|
||||||
if errCount > 0 {
|
|
||||||
AppDelegate.logger.warning("config error: \(errCount) configuration errors on reload")
|
|
||||||
var errors: [String] = [];
|
|
||||||
for i in 0..<errCount {
|
|
||||||
let err = ghostty_config_get_error(cfg, UInt32(i))
|
|
||||||
let message = String(cString: err.message)
|
|
||||||
errors.append(message)
|
|
||||||
AppDelegate.logger.warning("config error: \(message)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the configuration errors (if any).
|
|
||||||
func configErrors() -> [String] {
|
|
||||||
guard let cfg = self.config else { return [] }
|
|
||||||
|
|
||||||
var errors: [String] = [];
|
|
||||||
let errCount = ghostty_config_errors_count(cfg)
|
|
||||||
for i in 0..<errCount {
|
|
||||||
let err = ghostty_config_get_error(cfg, UInt32(i))
|
|
||||||
let message = String(cString: err.message)
|
|
||||||
errors.append(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
func appTick() {
|
func appTick() {
|
||||||
guard let app = self.app else { return }
|
guard let app = self.app else { return }
|
||||||
@ -298,8 +138,18 @@ extension Ghostty {
|
|||||||
let exit = ghostty_app_tick(app)
|
let exit = ghostty_app_tick(app)
|
||||||
if (!exit) { return }
|
if (!exit) { return }
|
||||||
|
|
||||||
|
// On iOS, applications do not terminate programmatically like they do
|
||||||
|
// on macOS. On iOS, applications are only terminated when a user physically
|
||||||
|
// closes the application (i.e. going to the home screen). If we request
|
||||||
|
// exit on iOS we ignore it.
|
||||||
|
#if os(iOS)
|
||||||
|
logger.info("quit request received, ignoring on iOS")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
// We want to quit, start that process
|
// We want to quit, start that process
|
||||||
NSApplication.shared.terminate(nil)
|
NSApplication.shared.terminate(nil)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func openConfig() {
|
func openConfig() {
|
||||||
@ -321,14 +171,14 @@ extension Ghostty {
|
|||||||
func newTab(surface: ghostty_surface_t) {
|
func newTab(surface: ghostty_surface_t) {
|
||||||
let action = "new_tab"
|
let action = "new_tab"
|
||||||
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
||||||
AppDelegate.logger.warning("action failed action=\(action)")
|
logger.warning("action failed action=\(action)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWindow(surface: ghostty_surface_t) {
|
func newWindow(surface: ghostty_surface_t) {
|
||||||
let action = "new_window"
|
let action = "new_window"
|
||||||
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
||||||
AppDelegate.logger.warning("action failed action=\(action)")
|
logger.warning("action failed action=\(action)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,17 +201,23 @@ extension Ghostty {
|
|||||||
func splitToggleZoom(surface: ghostty_surface_t) {
|
func splitToggleZoom(surface: ghostty_surface_t) {
|
||||||
let action = "toggle_split_zoom"
|
let action = "toggle_split_zoom"
|
||||||
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
||||||
AppDelegate.logger.warning("action failed action=\(action)")
|
logger.warning("action failed action=\(action)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleFullscreen(surface: ghostty_surface_t) {
|
func toggleFullscreen(surface: ghostty_surface_t) {
|
||||||
let action = "toggle_fullscreen"
|
let action = "toggle_fullscreen"
|
||||||
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
||||||
AppDelegate.logger.warning("action failed action=\(action)")
|
logger.warning("action failed action=\(action)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum FontSizeModification {
|
||||||
|
case increase(Int)
|
||||||
|
case decrease(Int)
|
||||||
|
case reset
|
||||||
|
}
|
||||||
|
|
||||||
func changeFontSize(surface: ghostty_surface_t, _ change: FontSizeModification) {
|
func changeFontSize(surface: ghostty_surface_t, _ change: FontSizeModification) {
|
||||||
let action: String
|
let action: String
|
||||||
switch change {
|
switch change {
|
||||||
@ -373,17 +229,72 @@ extension Ghostty {
|
|||||||
action = "reset_font_size"
|
action = "reset_font_size"
|
||||||
}
|
}
|
||||||
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
||||||
AppDelegate.logger.warning("action failed action=\(action)")
|
logger.warning("action failed action=\(action)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleTerminalInspector(surface: ghostty_surface_t) {
|
func toggleTerminalInspector(surface: ghostty_surface_t) {
|
||||||
let action = "inspector:toggle"
|
let action = "inspector:toggle"
|
||||||
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
||||||
AppDelegate.logger.warning("action failed action=\(action)")
|
logger.warning("action failed action=\(action)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
// MARK: Ghostty Callbacks (iOS)
|
||||||
|
|
||||||
|
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {}
|
||||||
|
static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? { return nil }
|
||||||
|
static func openConfig(_ userdata: UnsafeMutableRawPointer?) {}
|
||||||
|
static func setTitle(_ userdata: UnsafeMutableRawPointer?, title: UnsafePointer<CChar>?) {}
|
||||||
|
static func setMouseShape(_ userdata: UnsafeMutableRawPointer?, shape: ghostty_mouse_shape_e) {}
|
||||||
|
static func setMouseVisibility(_ userdata: UnsafeMutableRawPointer?, visible: Bool) {}
|
||||||
|
static func readClipboard(
|
||||||
|
_ userdata: UnsafeMutableRawPointer?,
|
||||||
|
location: ghostty_clipboard_e,
|
||||||
|
state: UnsafeMutableRawPointer?
|
||||||
|
) {}
|
||||||
|
|
||||||
|
static func confirmReadClipboard(
|
||||||
|
_ userdata: UnsafeMutableRawPointer?,
|
||||||
|
string: UnsafePointer<CChar>?,
|
||||||
|
state: UnsafeMutableRawPointer?,
|
||||||
|
request: ghostty_clipboard_request_e
|
||||||
|
) {}
|
||||||
|
|
||||||
|
static func writeClipboard(
|
||||||
|
_ userdata: UnsafeMutableRawPointer?,
|
||||||
|
string: UnsafePointer<CChar>?,
|
||||||
|
location: ghostty_clipboard_e,
|
||||||
|
confirm: Bool
|
||||||
|
) {}
|
||||||
|
|
||||||
|
static func newSplit(
|
||||||
|
_ userdata: UnsafeMutableRawPointer?,
|
||||||
|
direction: ghostty_split_direction_e,
|
||||||
|
config: ghostty_surface_config_s
|
||||||
|
) {}
|
||||||
|
|
||||||
|
static func newTab(_ userdata: UnsafeMutableRawPointer?, config: ghostty_surface_config_s) {}
|
||||||
|
static func newWindow(_ userdata: UnsafeMutableRawPointer?, config: ghostty_surface_config_s) {}
|
||||||
|
static func controlInspector(_ userdata: UnsafeMutableRawPointer?, mode: ghostty_inspector_mode_e) {}
|
||||||
|
static func closeSurface(_ userdata: UnsafeMutableRawPointer?, processAlive: Bool) {}
|
||||||
|
static func focusSplit(_ userdata: UnsafeMutableRawPointer?, direction: ghostty_split_focus_direction_e) {}
|
||||||
|
static func resizeSplit(_ userdata: UnsafeMutableRawPointer?, direction: ghostty_split_resize_direction_e, amount: UInt16) {}
|
||||||
|
static func equalizeSplits(_ userdata: UnsafeMutableRawPointer?) {}
|
||||||
|
static func toggleSplitZoom(_ userdata: UnsafeMutableRawPointer?) {}
|
||||||
|
static func gotoTab(_ userdata: UnsafeMutableRawPointer?, n: Int32) {}
|
||||||
|
static func toggleFullscreen(_ userdata: UnsafeMutableRawPointer?, nonNativeFullscreen: ghostty_non_native_fullscreen_e) {}
|
||||||
|
static func setInitialWindowSize(_ userdata: UnsafeMutableRawPointer?, width: UInt32, height: UInt32) {}
|
||||||
|
static func renderInspector(_ userdata: UnsafeMutableRawPointer?) {}
|
||||||
|
static func setCellSize(_ userdata: UnsafeMutableRawPointer?, width: UInt32, height: UInt32) {}
|
||||||
|
static func showUserNotification(_ userdata: UnsafeMutableRawPointer?, title: UnsafePointer<CChar>?, body: UnsafePointer<CChar>?) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
|
||||||
|
// MARK: Notifications
|
||||||
|
|
||||||
// Called when the selected keyboard changes. We have to notify Ghostty so that
|
// Called when the selected keyboard changes. We have to notify Ghostty so that
|
||||||
// it can reload the keyboard mapping for input.
|
// it can reload the keyboard mapping for input.
|
||||||
@objc private func keyboardSelectionDidChange(notification: NSNotification) {
|
@objc private func keyboardSelectionDidChange(notification: NSNotification) {
|
||||||
@ -391,7 +302,7 @@ extension Ghostty {
|
|||||||
ghostty_app_keyboard_changed(app)
|
ghostty_app_keyboard_changed(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Ghostty Callbacks
|
// MARK: Ghostty Callbacks (macOS)
|
||||||
|
|
||||||
static func newSplit(_ userdata: UnsafeMutableRawPointer?, direction: ghostty_split_direction_e, config: ghostty_surface_config_s) {
|
static func newSplit(_ userdata: UnsafeMutableRawPointer?, direction: ghostty_split_direction_e, config: ghostty_surface_config_s) {
|
||||||
let surface = self.surfaceUserdata(from: userdata)
|
let surface = self.surfaceUserdata(from: userdata)
|
||||||
@ -534,14 +445,15 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? {
|
static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? {
|
||||||
guard let newConfig = Self.loadConfig() else {
|
let newConfig = Config()
|
||||||
|
guard newConfig.loaded else {
|
||||||
AppDelegate.logger.warning("failed to reload configuration")
|
AppDelegate.logger.warning("failed to reload configuration")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign the new config. This will automatically free the old config.
|
// Assign the new config. This will automatically free the old config.
|
||||||
// It is safe to free the old config from within this function call.
|
// It is safe to free the old config from within this function call.
|
||||||
let state = Unmanaged<AppState>.fromOpaque(userdata!).takeUnretainedValue()
|
let state = Unmanaged<Self>.fromOpaque(userdata!).takeUnretainedValue()
|
||||||
state.config = newConfig
|
state.config = newConfig
|
||||||
|
|
||||||
// If we have a delegate, notify.
|
// If we have a delegate, notify.
|
||||||
@ -549,11 +461,11 @@ extension Ghostty {
|
|||||||
delegate.configDidReload(state)
|
delegate.configDidReload(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newConfig
|
return newConfig.config
|
||||||
}
|
}
|
||||||
|
|
||||||
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {
|
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {
|
||||||
let state = Unmanaged<AppState>.fromOpaque(userdata!).takeUnretainedValue()
|
let state = Unmanaged<App>.fromOpaque(userdata!).takeUnretainedValue()
|
||||||
|
|
||||||
// Wakeup can be called from any thread so we schedule the app tick
|
// Wakeup can be called from any thread so we schedule the app tick
|
||||||
// from the main thread. There is probably some improvements we can make
|
// from the main thread. There is probably some improvements we can make
|
||||||
@ -662,7 +574,7 @@ extension Ghostty {
|
|||||||
let surface = self.surfaceUserdata(from: userdata)
|
let surface = self.surfaceUserdata(from: userdata)
|
||||||
|
|
||||||
guard let appState = self.appState(fromView: surface) else { return }
|
guard let appState = self.appState(fromView: surface) else { return }
|
||||||
guard appState.windowDecorations else {
|
guard appState.config.windowDecorations else {
|
||||||
let alert = NSAlert()
|
let alert = NSAlert()
|
||||||
alert.messageText = "Tabs are disabled"
|
alert.messageText = "Tabs are disabled"
|
||||||
alert.informativeText = "Enable window decorations to use tabs"
|
alert.informativeText = "Enable window decorations to use tabs"
|
||||||
@ -701,16 +613,18 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the GhosttyState from the given userdata value.
|
/// Returns the GhosttyState from the given userdata value.
|
||||||
static private func appState(fromView view: SurfaceView) -> AppState? {
|
static private func appState(fromView view: SurfaceView) -> App? {
|
||||||
guard let surface = view.surface else { return nil }
|
guard let surface = view.surface else { return nil }
|
||||||
guard let app = ghostty_surface_app(surface) else { return nil }
|
guard let app = ghostty_surface_app(surface) else { return nil }
|
||||||
guard let app_ud = ghostty_app_userdata(app) else { return nil }
|
guard let app_ud = ghostty_app_userdata(app) else { return nil }
|
||||||
return Unmanaged<AppState>.fromOpaque(app_ud).takeUnretainedValue()
|
return Unmanaged<App>.fromOpaque(app_ud).takeUnretainedValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the surface view from the userdata.
|
/// Returns the surface view from the userdata.
|
||||||
static private func surfaceUserdata(from userdata: UnsafeMutableRawPointer?) -> SurfaceView {
|
static private func surfaceUserdata(from userdata: UnsafeMutableRawPointer?) -> SurfaceView {
|
||||||
return Unmanaged<SurfaceView>.fromOpaque(userdata!).takeUnretainedValue()
|
return Unmanaged<SurfaceView>.fromOpaque(userdata!).takeUnretainedValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
238
macos/Sources/Ghostty/Ghostty.Config.swift
Normal file
238
macos/Sources/Ghostty/Ghostty.Config.swift
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import GhosttyKit
|
||||||
|
|
||||||
|
extension Ghostty {
|
||||||
|
/// Maps to a `ghostty_config_t` and the various operations on that.
|
||||||
|
class Config: ObservableObject {
|
||||||
|
// The underlying C pointer to the Ghostty config structure. This
|
||||||
|
// should never be accessed directly. Any operations on this should
|
||||||
|
// be called from the functions on this or another class.
|
||||||
|
private(set) var config: ghostty_config_t? = nil {
|
||||||
|
didSet {
|
||||||
|
// Free the old value whenever we change
|
||||||
|
guard let old = oldValue else { return }
|
||||||
|
ghostty_config_free(old)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if the configuration is loaded
|
||||||
|
var loaded: Bool { config != nil }
|
||||||
|
|
||||||
|
/// Return the errors found while loading the configuration.
|
||||||
|
var errors: [String] {
|
||||||
|
guard let cfg = self.config else { return [] }
|
||||||
|
|
||||||
|
var errors: [String] = [];
|
||||||
|
let errCount = ghostty_config_errors_count(cfg)
|
||||||
|
for i in 0..<errCount {
|
||||||
|
let err = ghostty_config_get_error(cfg, UInt32(i))
|
||||||
|
let message = String(cString: err.message)
|
||||||
|
errors.append(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if let cfg = Self.loadConfig() {
|
||||||
|
self.config = cfg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.config = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes a new configuration and loads all the values.
|
||||||
|
static private func loadConfig() -> ghostty_config_t? {
|
||||||
|
// Initialize the global configuration.
|
||||||
|
guard let cfg = ghostty_config_new() else {
|
||||||
|
logger.critical("ghostty_config_new failed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load our configuration from files, CLI args, and then any referenced files.
|
||||||
|
// We only do this on macOS because other Apple platforms do not have the
|
||||||
|
// same filesystem concept.
|
||||||
|
#if os(macOS)
|
||||||
|
ghostty_config_load_default_files(cfg);
|
||||||
|
ghostty_config_load_cli_args(cfg);
|
||||||
|
ghostty_config_load_recursive_files(cfg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: we'd probably do some config loading here... for now we'd
|
||||||
|
// have to do this synchronously. When we support config updating we can do
|
||||||
|
// this async and update later.
|
||||||
|
|
||||||
|
// Finalize will make our defaults available.
|
||||||
|
ghostty_config_finalize(cfg)
|
||||||
|
|
||||||
|
// Log any configuration errors. These will be automatically shown in a
|
||||||
|
// pop-up window too.
|
||||||
|
let errCount = ghostty_config_errors_count(cfg)
|
||||||
|
if errCount > 0 {
|
||||||
|
logger.warning("config error: \(errCount) configuration errors on reload")
|
||||||
|
var errors: [String] = [];
|
||||||
|
for i in 0..<errCount {
|
||||||
|
let err = ghostty_config_get_error(cfg, UInt32(i))
|
||||||
|
let message = String(cString: err.message)
|
||||||
|
errors.append(message)
|
||||||
|
logger.warning("config error: \(message)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
// MARK: - Keybindings
|
||||||
|
|
||||||
|
/// A convenience struct that has the key + modifiers for some keybinding.
|
||||||
|
struct KeyEquivalent: CustomStringConvertible {
|
||||||
|
let key: String
|
||||||
|
let modifiers: NSEvent.ModifierFlags
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
var key = self.key
|
||||||
|
|
||||||
|
// Note: the order below matters; it matches the ordering modifiers
|
||||||
|
// shown for macOS menu shortcut labels.
|
||||||
|
if modifiers.contains(.command) { key = "⌘\(key)" }
|
||||||
|
if modifiers.contains(.shift) { key = "⇧\(key)" }
|
||||||
|
if modifiers.contains(.option) { key = "⌥\(key)" }
|
||||||
|
if modifiers.contains(.control) { key = "⌃\(key)" }
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the key equivalent for the given action. The action is the name of the action
|
||||||
|
/// in the Ghostty configuration. For example `keybind = cmd+q=quit` in Ghostty
|
||||||
|
/// configuration would be "quit" action.
|
||||||
|
///
|
||||||
|
/// Returns nil if there is no key equivalent for the given action.
|
||||||
|
func keyEquivalent(for action: String) -> KeyEquivalent? {
|
||||||
|
guard let cfg = self.config else { return nil }
|
||||||
|
|
||||||
|
let trigger = ghostty_config_trigger(cfg, action, UInt(action.count))
|
||||||
|
guard let equiv = Ghostty.keyEquivalent(key: trigger.key) else { return nil }
|
||||||
|
|
||||||
|
return KeyEquivalent(
|
||||||
|
key: equiv,
|
||||||
|
modifiers: Ghostty.eventModifierFlags(mods: trigger.mods)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MARK: - Configuration Values
|
||||||
|
|
||||||
|
/// For all of the configuration values below, see the associated Ghostty documentation for
|
||||||
|
/// details on what each means. We only add documentation if there is a strange conversion
|
||||||
|
/// due to the embedded library and Swift.
|
||||||
|
|
||||||
|
var shouldQuitAfterLastWindowClosed: Bool {
|
||||||
|
guard let config = self.config else { return true }
|
||||||
|
var v = false;
|
||||||
|
let key = "quit-after-last-window-closed"
|
||||||
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowColorspace: String {
|
||||||
|
guard let config = self.config else { return "" }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
|
let key = "window-colorspace"
|
||||||
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
|
||||||
|
guard let ptr = v else { return "" }
|
||||||
|
return String(cString: ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowSaveState: String {
|
||||||
|
guard let config = self.config else { return "" }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
|
let key = "window-save-state"
|
||||||
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
|
||||||
|
guard let ptr = v else { return "" }
|
||||||
|
return String(cString: ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowNewTabPosition: String {
|
||||||
|
guard let config = self.config else { return "" }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
|
let key = "window-new-tab-position"
|
||||||
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
|
||||||
|
guard let ptr = v else { return "" }
|
||||||
|
return String(cString: ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowDecorations: Bool {
|
||||||
|
guard let config = self.config else { return true }
|
||||||
|
var v = false;
|
||||||
|
let key = "window-decoration"
|
||||||
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowTheme: String? {
|
||||||
|
guard let config = self.config else { return nil }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
|
let key = "window-theme"
|
||||||
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return nil }
|
||||||
|
guard let ptr = v else { return nil }
|
||||||
|
return String(cString: ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowStepResize: Bool {
|
||||||
|
guard let config = self.config else { return true }
|
||||||
|
var v = false
|
||||||
|
let key = "window-step-resize"
|
||||||
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowFullscreen: Bool {
|
||||||
|
guard let config = self.config else { return true }
|
||||||
|
var v = false
|
||||||
|
let key = "fullscreen"
|
||||||
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
var backgroundOpacity: Double {
|
||||||
|
guard let config = self.config else { return 1 }
|
||||||
|
var v: Double = 1
|
||||||
|
let key = "background-opacity"
|
||||||
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
var unfocusedSplitOpacity: Double {
|
||||||
|
guard let config = self.config else { return 1 }
|
||||||
|
var opacity: Double = 0.85
|
||||||
|
let key = "unfocused-split-opacity"
|
||||||
|
_ = ghostty_config_get(config, &opacity, key, UInt(key.count))
|
||||||
|
return 1 - opacity
|
||||||
|
}
|
||||||
|
|
||||||
|
var unfocusedSplitFill: Color {
|
||||||
|
guard let config = self.config else { return .white }
|
||||||
|
|
||||||
|
var rgb: UInt32 = 16777215 // white default
|
||||||
|
let key = "unfocused-split-fill"
|
||||||
|
if (!ghostty_config_get(config, &rgb, key, UInt(key.count))) {
|
||||||
|
let bg_key = "background"
|
||||||
|
_ = ghostty_config_get(config, &rgb, bg_key, UInt(bg_key.count));
|
||||||
|
}
|
||||||
|
|
||||||
|
let red = Double(rgb & 0xff)
|
||||||
|
let green = Double((rgb >> 8) & 0xff)
|
||||||
|
let blue = Double((rgb >> 16) & 0xff)
|
||||||
|
|
||||||
|
return Color(
|
||||||
|
red: red / 255,
|
||||||
|
green: green / 255,
|
||||||
|
blue: blue / 255
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,21 +7,6 @@ extension Ghostty {
|
|||||||
return Self.keyToEquivalent[key]
|
return Self.keyToEquivalent[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the keyEquivalent label that includes the mods.
|
|
||||||
static func keyEquivalentLabel(key: ghostty_input_key_e, mods: ghostty_input_mods_e) -> String? {
|
|
||||||
guard var key = Self.keyEquivalent(key: key) else { return nil }
|
|
||||||
let flags = Self.eventModifierFlags(mods: mods)
|
|
||||||
|
|
||||||
// Note: the order below matters; it matches the ordering modifiers show for
|
|
||||||
// macOS menu shortcut labels.
|
|
||||||
if flags.contains(.command) { key = "⌘\(key)" }
|
|
||||||
if flags.contains(.shift) { key = "⇧\(key)" }
|
|
||||||
if flags.contains(.option) { key = "⌥\(key)" }
|
|
||||||
if flags.contains(.control) { key = "⌃\(key)" }
|
|
||||||
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the event modifier flags set for the Ghostty mods enum.
|
/// Returns the event modifier flags set for the Ghostty mods enum.
|
||||||
static func eventModifierFlags(mods: ghostty_input_mods_e) -> NSEvent.ModifierFlags {
|
static func eventModifierFlags(mods: ghostty_input_mods_e) -> NSEvent.ModifierFlags {
|
||||||
var flags = NSEvent.ModifierFlags(rawValue: 0);
|
var flags = NSEvent.ModifierFlags(rawValue: 0);
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
|
import os
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import GhosttyKit
|
import GhosttyKit
|
||||||
|
|
||||||
struct Ghostty {
|
struct Ghostty {
|
||||||
|
// The primary logger used by the GhosttyKit libraries.
|
||||||
|
static let logger = Logger(
|
||||||
|
subsystem: Bundle.main.bundleIdentifier!,
|
||||||
|
category: "ghostty"
|
||||||
|
)
|
||||||
|
|
||||||
// All the notifications that will be emitted will be put here.
|
// All the notifications that will be emitted will be put here.
|
||||||
struct Notification {}
|
struct Notification {}
|
||||||
|
|
||||||
@ -12,6 +19,26 @@ struct Ghostty {
|
|||||||
static let userNotificationActionShow = "com.mitchellh.ghostty.userNotification.Show"
|
static let userNotificationActionShow = "com.mitchellh.ghostty.userNotification.Show"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Build Info
|
||||||
|
|
||||||
|
extension Ghostty {
|
||||||
|
struct Info {
|
||||||
|
var mode: ghostty_build_mode_e
|
||||||
|
var version: String
|
||||||
|
}
|
||||||
|
|
||||||
|
static var info: Info {
|
||||||
|
let raw = ghostty_info()
|
||||||
|
let version = NSString(
|
||||||
|
bytes: raw.version,
|
||||||
|
length: Int(raw.version_len),
|
||||||
|
encoding: NSUTF8StringEncoding
|
||||||
|
) ?? "unknown"
|
||||||
|
|
||||||
|
return Info(mode: raw.build_mode, version: String(version))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Surface Notifications
|
// MARK: Surface Notifications
|
||||||
|
|
||||||
extension Ghostty {
|
extension Ghostty {
|
||||||
|
@ -5,7 +5,7 @@ import GhosttyKit
|
|||||||
extension Ghostty {
|
extension Ghostty {
|
||||||
/// Render a terminal for the active app in the environment.
|
/// Render a terminal for the active app in the environment.
|
||||||
struct Terminal: View {
|
struct Terminal: View {
|
||||||
@EnvironmentObject private var ghostty: Ghostty.AppState
|
@EnvironmentObject private var ghostty: Ghostty.App
|
||||||
@FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle: String?
|
@FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle: String?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -49,40 +49,12 @@ extension Ghostty {
|
|||||||
// Maintain whether our window has focus (is key) or not
|
// Maintain whether our window has focus (is key) or not
|
||||||
@State private var windowFocus: Bool = true
|
@State private var windowFocus: Bool = true
|
||||||
|
|
||||||
@EnvironmentObject private var ghostty: Ghostty.AppState
|
@EnvironmentObject private var ghostty: Ghostty.App
|
||||||
|
|
||||||
// This is true if the terminal is considered "focused". The terminal is focused if
|
// This is true if the terminal is considered "focused". The terminal is focused if
|
||||||
// it is both individually focused and the containing window is key.
|
// it is both individually focused and the containing window is key.
|
||||||
private var hasFocus: Bool { surfaceFocus && windowFocus }
|
private var hasFocus: Bool { surfaceFocus && windowFocus }
|
||||||
|
|
||||||
// The opacity of the rectangle when unfocused.
|
|
||||||
private var unfocusedOpacity: Double {
|
|
||||||
var opacity: Double = 0.85
|
|
||||||
let key = "unfocused-split-opacity"
|
|
||||||
_ = ghostty_config_get(ghostty.config, &opacity, key, UInt(key.count))
|
|
||||||
return 1 - opacity
|
|
||||||
}
|
|
||||||
|
|
||||||
// The color for the rectangle overlay when unfocused.
|
|
||||||
private var unfocusedFill: Color {
|
|
||||||
var rgb: UInt32 = 16777215 // white default
|
|
||||||
let key = "unfocused-split-fill"
|
|
||||||
if (!ghostty_config_get(ghostty.config, &rgb, key, UInt(key.count))) {
|
|
||||||
let bg_key = "background"
|
|
||||||
_ = ghostty_config_get(ghostty.config, &rgb, bg_key, UInt(bg_key.count));
|
|
||||||
}
|
|
||||||
|
|
||||||
let red = Double(rgb & 0xff)
|
|
||||||
let green = Double((rgb >> 8) & 0xff)
|
|
||||||
let blue = Double((rgb >> 16) & 0xff)
|
|
||||||
|
|
||||||
return Color(
|
|
||||||
red: red / 255,
|
|
||||||
green: green / 255,
|
|
||||||
blue: blue / 255
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
// We use a GeometryReader to get the frame bounds so that our metal surface
|
// We use a GeometryReader to get the frame bounds so that our metal surface
|
||||||
@ -175,10 +147,10 @@ extension Ghostty {
|
|||||||
// because we want to keep our focused surface dark even if we don't have window
|
// because we want to keep our focused surface dark even if we don't have window
|
||||||
// focus.
|
// focus.
|
||||||
if (isSplit && !surfaceFocus) {
|
if (isSplit && !surfaceFocus) {
|
||||||
let overlayOpacity = unfocusedOpacity;
|
let overlayOpacity = ghostty.config.unfocusedSplitOpacity;
|
||||||
if (overlayOpacity > 0) {
|
if (overlayOpacity > 0) {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.fill(unfocusedFill)
|
.fill(ghostty.config.unfocusedSplitFill)
|
||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
.opacity(overlayOpacity)
|
.opacity(overlayOpacity)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
||||||
# more details.
|
# more details.
|
||||||
"sha256-hE4MNVZx/kA90MPHEraJDayBtLw29HZfnFChLdXPS0g="
|
"sha256-to0V9rCefIs8KcWsx+nopgQO4i7O3gb06LGNc6NXN2M="
|
||||||
|
@ -45,6 +45,7 @@ const SDK = struct {
|
|||||||
|
|
||||||
pub fn fromTarget(target: std.Target) !SDK {
|
pub fn fromTarget(target: std.Target) !SDK {
|
||||||
return switch (target.os.tag) {
|
return switch (target.os.tag) {
|
||||||
|
.ios => .{ .platform = "iPhoneOS", .version = "" },
|
||||||
.macos => .{ .platform = "MacOSX", .version = "14" },
|
.macos => .{ .platform = "MacOSX", .version = "14" },
|
||||||
else => {
|
else => {
|
||||||
std.log.err("unsupported os={}", .{target.os.tag});
|
std.log.err("unsupported os={}", .{target.os.tag});
|
||||||
|
@ -71,11 +71,13 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.file = imgui.path("backends/imgui_impl_metal.mm"),
|
.file = imgui.path("backends/imgui_impl_metal.mm"),
|
||||||
.flags = flags.items,
|
.flags = flags.items,
|
||||||
});
|
});
|
||||||
|
if (target.result.os.tag == .macos) {
|
||||||
lib.addCSourceFile(.{
|
lib.addCSourceFile(.{
|
||||||
.file = imgui.path("backends/imgui_impl_osx.mm"),
|
.file = imgui.path("backends/imgui_impl_osx.mm"),
|
||||||
.flags = flags.items,
|
.flags = flags.items,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lib.installHeadersDirectoryOptions(.{
|
lib.installHeadersDirectoryOptions(.{
|
||||||
.source_dir = .{ .path = "vendor" },
|
.source_dir = .{ .path = "vendor" },
|
||||||
|
@ -15,6 +15,11 @@ pub fn build(b: *std.Build) !void {
|
|||||||
});
|
});
|
||||||
lib.linkLibC();
|
lib.linkLibC();
|
||||||
lib.addIncludePath(upstream.path("include"));
|
lib.addIncludePath(upstream.path("include"));
|
||||||
|
if (target.result.isDarwin()) {
|
||||||
|
const apple_sdk = @import("apple_sdk");
|
||||||
|
try apple_sdk.addPaths(b, &lib.root_module);
|
||||||
|
}
|
||||||
|
|
||||||
module.addIncludePath(upstream.path("include"));
|
module.addIncludePath(upstream.path("include"));
|
||||||
module.addIncludePath(.{ .path = "" });
|
module.addIncludePath(.{ .path = "" });
|
||||||
|
|
||||||
@ -86,7 +91,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
b.installArtifact(lib);
|
b.installArtifact(lib);
|
||||||
|
|
||||||
{
|
if (target.query.isNative()) {
|
||||||
const test_exe = b.addTest(.{
|
const test_exe = b.addTest(.{
|
||||||
.name = "test",
|
.name = "test",
|
||||||
.root_source_file = .{ .path = "main.zig" },
|
.root_source_file = .{ .path = "main.zig" },
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
.{
|
.{
|
||||||
.name = "freetype",
|
.name = "freetype",
|
||||||
.version = "2.13.2",
|
.version = "2.13.2",
|
||||||
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.freetype = .{
|
.freetype = .{
|
||||||
.url = "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz",
|
.url = "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz",
|
||||||
.hash = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d",
|
.hash = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||||
.libpng = .{ .path = "../libpng" },
|
.libpng = .{ .path = "../libpng" },
|
||||||
.zlib = .{ .path = "../zlib" },
|
.zlib = .{ .path = "../zlib" },
|
||||||
},
|
},
|
||||||
|
@ -12,8 +12,15 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
module.addIncludePath(upstream.path(""));
|
module.addIncludePath(upstream.path(""));
|
||||||
module.addIncludePath(.{ .path = "override" });
|
module.addIncludePath(.{ .path = "override" });
|
||||||
|
if (target.result.isDarwin()) {
|
||||||
|
// See pkg/harfbuzz/build.zig
|
||||||
|
module.resolved_target = target;
|
||||||
|
defer module.resolved_target = null;
|
||||||
|
const apple_sdk = @import("apple_sdk");
|
||||||
|
try apple_sdk.addPaths(b, module);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
if (target.query.isNative()) {
|
||||||
const test_exe = b.addTest(.{
|
const test_exe = b.addTest(.{
|
||||||
.name = "test",
|
.name = "test",
|
||||||
.root_source_file = .{ .path = "main.zig" },
|
.root_source_file = .{ .path = "main.zig" },
|
||||||
@ -45,6 +52,10 @@ fn buildGlslang(
|
|||||||
lib.linkLibCpp();
|
lib.linkLibCpp();
|
||||||
lib.addIncludePath(upstream.path(""));
|
lib.addIncludePath(upstream.path(""));
|
||||||
lib.addIncludePath(.{ .path = "override" });
|
lib.addIncludePath(.{ .path = "override" });
|
||||||
|
if (target.result.isDarwin()) {
|
||||||
|
const apple_sdk = @import("apple_sdk");
|
||||||
|
try apple_sdk.addPaths(b, &lib.root_module);
|
||||||
|
}
|
||||||
|
|
||||||
var flags = std.ArrayList([]const u8).init(b.allocator);
|
var flags = std.ArrayList([]const u8).init(b.allocator);
|
||||||
defer flags.deinit();
|
defer flags.deinit();
|
||||||
|
@ -7,5 +7,7 @@
|
|||||||
.url = "https://github.com/KhronosGroup/glslang/archive/refs/tags/13.1.1.tar.gz",
|
.url = "https://github.com/KhronosGroup/glslang/archive/refs/tags/13.1.1.tar.gz",
|
||||||
.hash = "1220481fe19def1172cd0728743019c0f440181a6342b62d03e24d05c70141516799",
|
.hash = "1220481fe19def1172cd0728743019c0f440181a6342b62d03e24d05c70141516799",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,18 @@ pub fn build(b: *std.Build) !void {
|
|||||||
lib.addIncludePath(upstream.path("src"));
|
lib.addIncludePath(upstream.path("src"));
|
||||||
module.addIncludePath(upstream.path("src"));
|
module.addIncludePath(upstream.path("src"));
|
||||||
|
|
||||||
|
if (target.result.isDarwin()) {
|
||||||
|
// This is definitely super sketchy and not right but without this
|
||||||
|
// zig build test breaks on macOS. We have to look into what exactly
|
||||||
|
// is going on here but this getting comitted in the interest of
|
||||||
|
// unblocking zig build test.
|
||||||
|
module.resolved_target = target;
|
||||||
|
defer module.resolved_target = null;
|
||||||
|
|
||||||
|
try apple_sdk.addPaths(b, &lib.root_module);
|
||||||
|
try apple_sdk.addPaths(b, module);
|
||||||
|
}
|
||||||
|
|
||||||
const freetype_dep = b.dependency("freetype", .{ .target = target, .optimize = optimize });
|
const freetype_dep = b.dependency("freetype", .{ .target = target, .optimize = optimize });
|
||||||
lib.linkLibrary(freetype_dep.artifact("freetype"));
|
lib.linkLibrary(freetype_dep.artifact("freetype"));
|
||||||
module.addIncludePath(freetype_dep.builder.dependency("freetype", .{}).path("include"));
|
module.addIncludePath(freetype_dep.builder.dependency("freetype", .{}).path("include"));
|
||||||
@ -59,19 +71,10 @@ pub fn build(b: *std.Build) !void {
|
|||||||
"-DHAVE_FT_DONE_MM_VAR=1",
|
"-DHAVE_FT_DONE_MM_VAR=1",
|
||||||
"-DHAVE_FT_GET_TRANSFORM=1",
|
"-DHAVE_FT_GET_TRANSFORM=1",
|
||||||
});
|
});
|
||||||
if (coretext_enabled and target.result.isDarwin()) {
|
if (coretext_enabled) {
|
||||||
// This is definitely super sketchy and not right but without this
|
|
||||||
// zig build test breaks on macOS. We have to look into what exactly
|
|
||||||
// is going on here but this getting comitted in the interest of
|
|
||||||
// unblocking zig build test.
|
|
||||||
module.resolved_target = target;
|
|
||||||
defer module.resolved_target = null;
|
|
||||||
|
|
||||||
try flags.appendSlice(&.{"-DHAVE_CORETEXT=1"});
|
try flags.appendSlice(&.{"-DHAVE_CORETEXT=1"});
|
||||||
try apple_sdk.addPaths(b, &lib.root_module);
|
lib.linkFramework("CoreText");
|
||||||
try apple_sdk.addPaths(b, module);
|
module.linkFramework("CoreText", .{});
|
||||||
lib.linkFramework("ApplicationServices");
|
|
||||||
module.linkFramework("ApplicationServices", .{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lib.addCSourceFile(.{
|
lib.addCSourceFile(.{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
.{
|
.{
|
||||||
.name = "harfbuzz",
|
.name = "harfbuzz",
|
||||||
.version = "8.2.2",
|
.version = "8.2.2",
|
||||||
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.harfbuzz = .{
|
.harfbuzz = .{
|
||||||
.url = "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.2.2.tar.gz",
|
.url = "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.2.2.tar.gz",
|
||||||
|
@ -15,6 +15,10 @@ pub fn build(b: *std.Build) !void {
|
|||||||
if (target.result.os.tag == .linux) {
|
if (target.result.os.tag == .linux) {
|
||||||
lib.linkSystemLibrary("m");
|
lib.linkSystemLibrary("m");
|
||||||
}
|
}
|
||||||
|
if (target.result.isDarwin()) {
|
||||||
|
const apple_sdk = @import("apple_sdk");
|
||||||
|
try apple_sdk.addPaths(b, &lib.root_module);
|
||||||
|
}
|
||||||
|
|
||||||
const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize });
|
const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize });
|
||||||
lib.linkLibrary(zlib_dep.artifact("z"));
|
lib.linkLibrary(zlib_dep.artifact("z"));
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
.{
|
.{
|
||||||
.name = "libpng",
|
.name = "libpng",
|
||||||
.version = "1.6.40",
|
.version = "1.6.40",
|
||||||
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.libpng = .{
|
.libpng = .{
|
||||||
.url = "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.40.tar.gz",
|
.url = "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.40.tar.gz",
|
||||||
.hash = "12203d2722e3af6f9556503b114c25fe3eead528a93f5f26eefcb187a460d1548e07",
|
.hash = "12203d2722e3af6f9556503b114c25fe3eead528a93f5f26eefcb187a460d1548e07",
|
||||||
},
|
},
|
||||||
|
|
||||||
.zlib = .{
|
.zlib = .{ .path = "../zlib" },
|
||||||
.path = "../zlib",
|
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,16 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.file = .{ .path = "text/ext.c" },
|
.file = .{ .path = "text/ext.c" },
|
||||||
.flags = flags.items,
|
.flags = flags.items,
|
||||||
});
|
});
|
||||||
lib.linkFramework("Carbon");
|
|
||||||
lib.linkFramework("CoreFoundation");
|
lib.linkFramework("CoreFoundation");
|
||||||
lib.linkFramework("CoreGraphics");
|
lib.linkFramework("CoreGraphics");
|
||||||
lib.linkFramework("CoreText");
|
lib.linkFramework("CoreText");
|
||||||
lib.linkFramework("CoreVideo");
|
lib.linkFramework("CoreVideo");
|
||||||
|
if (target.result.os.tag == .macos) {
|
||||||
|
lib.linkFramework("Carbon");
|
||||||
|
module.linkFramework("Carbon", .{});
|
||||||
|
}
|
||||||
|
|
||||||
if (target.result.isDarwin()) {
|
if (target.result.isDarwin()) {
|
||||||
module.linkFramework("Carbon", .{});
|
|
||||||
module.linkFramework("CoreFoundation", .{});
|
module.linkFramework("CoreFoundation", .{});
|
||||||
module.linkFramework("CoreGraphics", .{});
|
module.linkFramework("CoreGraphics", .{});
|
||||||
module.linkFramework("CoreText", .{});
|
module.linkFramework("CoreText", .{});
|
||||||
|
@ -12,7 +12,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
module.addIncludePath(upstream.path("src"));
|
module.addIncludePath(upstream.path("src"));
|
||||||
b.installArtifact(lib);
|
b.installArtifact(lib);
|
||||||
|
|
||||||
{
|
if (target.query.isNative()) {
|
||||||
const test_exe = b.addTest(.{
|
const test_exe = b.addTest(.{
|
||||||
.name = "test",
|
.name = "test",
|
||||||
.root_source_file = .{ .path = "main.zig" },
|
.root_source_file = .{ .path = "main.zig" },
|
||||||
@ -44,6 +44,11 @@ fn buildOniguruma(
|
|||||||
lib.linkLibC();
|
lib.linkLibC();
|
||||||
lib.addIncludePath(upstream.path("src"));
|
lib.addIncludePath(upstream.path("src"));
|
||||||
|
|
||||||
|
if (target.result.isDarwin()) {
|
||||||
|
const apple_sdk = @import("apple_sdk");
|
||||||
|
try apple_sdk.addPaths(b, &lib.root_module);
|
||||||
|
}
|
||||||
|
|
||||||
lib.addConfigHeader(b.addConfigHeader(.{
|
lib.addConfigHeader(b.addConfigHeader(.{
|
||||||
.style = .{ .cmake = upstream.path("src/config.h.cmake.in") },
|
.style = .{ .cmake = upstream.path("src/config.h.cmake.in") },
|
||||||
}, .{
|
}, .{
|
||||||
|
@ -7,5 +7,7 @@
|
|||||||
.url = "https://github.com/kkos/oniguruma/archive/refs/tags/v6.9.9.tar.gz",
|
.url = "https://github.com/kkos/oniguruma/archive/refs/tags/v6.9.9.tar.gz",
|
||||||
.hash = "1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb",
|
.hash = "1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,10 @@ pub fn build(b: *std.Build) !void {
|
|||||||
if (target.result.os.tag != .windows) {
|
if (target.result.os.tag != .windows) {
|
||||||
lib.linkSystemLibrary("pthread");
|
lib.linkSystemLibrary("pthread");
|
||||||
}
|
}
|
||||||
|
if (target.result.isDarwin()) {
|
||||||
|
const apple_sdk = @import("apple_sdk");
|
||||||
|
try apple_sdk.addPaths(b, &lib.root_module);
|
||||||
|
}
|
||||||
|
|
||||||
lib.addIncludePath(upstream.path(""));
|
lib.addIncludePath(upstream.path(""));
|
||||||
lib.addIncludePath(.{ .path = "" });
|
lib.addIncludePath(.{ .path = "" });
|
||||||
@ -68,7 +72,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
b.installArtifact(lib);
|
b.installArtifact(lib);
|
||||||
|
|
||||||
{
|
if (target.query.isNative()) {
|
||||||
const test_exe = b.addTest(.{
|
const test_exe = b.addTest(.{
|
||||||
.name = "test",
|
.name = "test",
|
||||||
.root_source_file = .{ .path = "main.zig" },
|
.root_source_file = .{ .path = "main.zig" },
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
.{
|
.{
|
||||||
.name = "pixman",
|
.name = "pixman",
|
||||||
.version = "0.42.2",
|
.version = "0.42.2",
|
||||||
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.pixman = .{
|
.pixman = .{
|
||||||
.url = "https://deps.files.ghostty.dev/pixman-pixman-0.42.2.tar.gz",
|
.url = "https://deps.files.ghostty.dev/pixman-pixman-0.42.2.tar.gz",
|
||||||
.hash = "12209b9206f9a5d31ccd9a2312cc72cb9dfc3e034aee1883c549dc1d753fae457230",
|
.hash = "12209b9206f9a5d31ccd9a2312cc72cb9dfc3e034aee1883c549dc1d753fae457230",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
const lib = try buildSpirvCross(b, upstream, target, optimize);
|
const lib = try buildSpirvCross(b, upstream, target, optimize);
|
||||||
b.installArtifact(lib);
|
b.installArtifact(lib);
|
||||||
|
|
||||||
{
|
if (target.query.isNative()) {
|
||||||
const test_exe = b.addTest(.{
|
const test_exe = b.addTest(.{
|
||||||
.name = "test",
|
.name = "test",
|
||||||
.root_source_file = .{ .path = "main.zig" },
|
.root_source_file = .{ .path = "main.zig" },
|
||||||
@ -42,8 +42,10 @@ fn buildSpirvCross(
|
|||||||
});
|
});
|
||||||
lib.linkLibC();
|
lib.linkLibC();
|
||||||
lib.linkLibCpp();
|
lib.linkLibCpp();
|
||||||
//lib.addIncludePath(upstream.path(""));
|
if (target.result.isDarwin()) {
|
||||||
//lib.addIncludePath(.{ .path = "override" });
|
const apple_sdk = @import("apple_sdk");
|
||||||
|
try apple_sdk.addPaths(b, &lib.root_module);
|
||||||
|
}
|
||||||
|
|
||||||
var flags = std.ArrayList([]const u8).init(b.allocator);
|
var flags = std.ArrayList([]const u8).init(b.allocator);
|
||||||
defer flags.deinit();
|
defer flags.deinit();
|
||||||
|
@ -7,5 +7,7 @@
|
|||||||
.url = "https://github.com/KhronosGroup/SPIRV-Cross/archive/4818f7e7ef7b7078a3a7a5a52c4a338e0dda22f4.tar.gz",
|
.url = "https://github.com/KhronosGroup/SPIRV-Cross/archive/4818f7e7ef7b7078a3a7a5a52c4a338e0dda22f4.tar.gz",
|
||||||
.hash = "1220b2d8a6cff1926ef28a29e312a0a503b555ebc2f082230b882410f49e672ac9c6",
|
.hash = "1220b2d8a6cff1926ef28a29e312a0a503b555ebc2f082230b882410f49e672ac9c6",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,11 @@ pub fn build(b: *std.Build) !void {
|
|||||||
});
|
});
|
||||||
lib.linkLibC();
|
lib.linkLibC();
|
||||||
lib.addIncludePath(upstream.path(""));
|
lib.addIncludePath(upstream.path(""));
|
||||||
|
if (target.result.isDarwin()) {
|
||||||
|
const apple_sdk = @import("apple_sdk");
|
||||||
|
try apple_sdk.addPaths(b, &lib.root_module);
|
||||||
|
}
|
||||||
|
|
||||||
lib.installHeadersDirectoryOptions(.{
|
lib.installHeadersDirectoryOptions(.{
|
||||||
.source_dir = upstream.path(""),
|
.source_dir = upstream.path(""),
|
||||||
.install_dir = .header,
|
.install_dir = .header,
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
.{
|
.{
|
||||||
.name = "zlib",
|
.name = "zlib",
|
||||||
.version = "1.3.0",
|
.version = "1.3.0",
|
||||||
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.zlib = .{
|
.zlib = .{
|
||||||
.url = "https://github.com/madler/zlib/archive/refs/tags/v1.3.tar.gz",
|
.url = "https://github.com/madler/zlib/archive/refs/tags/v1.3.tar.gz",
|
||||||
.hash = "12207d353609d95cee9da7891919e6d9582e97b7aa2831bd50f33bf523a582a08547",
|
.hash = "12207d353609d95cee9da7891919e6d9582e97b7aa2831bd50f33bf523a582a08547",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ fn setupFd(src: File.Handle, target: i32) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.macos => {
|
.ios, .macos => {
|
||||||
// Mac doesn't support dup3 so we use dup2. We purposely clear
|
// Mac doesn't support dup3 so we use dup2. We purposely clear
|
||||||
// CLO_ON_EXEC for this fd.
|
// CLO_ON_EXEC for this fd.
|
||||||
const flags = try os.fcntl(src, os.F.GETFD, 0);
|
const flags = try os.fcntl(src, os.F.GETFD, 0);
|
||||||
|
@ -1664,6 +1664,9 @@ pub const CAPI = struct {
|
|||||||
ptr: *Surface,
|
ptr: *Surface,
|
||||||
window: *anyopaque,
|
window: *anyopaque,
|
||||||
) void {
|
) void {
|
||||||
|
// This is only supported on macOS
|
||||||
|
if (comptime builtin.target.os.tag != .macos) return;
|
||||||
|
|
||||||
const config = ptr.app.config;
|
const config = ptr.app.config;
|
||||||
|
|
||||||
// Do nothing if we don't have background transparency enabled
|
// Do nothing if we don't have background transparency enabled
|
||||||
|
@ -15,6 +15,12 @@ pub const Options = struct {
|
|||||||
/// The path to write the framework
|
/// The path to write the framework
|
||||||
out_path: []const u8,
|
out_path: []const u8,
|
||||||
|
|
||||||
|
/// The libraries to bundle
|
||||||
|
libraries: []const Library,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A single library to bundle into the xcframework.
|
||||||
|
pub const Library = struct {
|
||||||
/// Library file (dylib, a) to package.
|
/// Library file (dylib, a) to package.
|
||||||
library: LazyPath,
|
library: LazyPath,
|
||||||
|
|
||||||
@ -41,10 +47,12 @@ pub fn create(b: *std.Build, opts: Options) *XCFrameworkStep {
|
|||||||
const run = RunStep.create(b, b.fmt("xcframework {s}", .{opts.name}));
|
const run = RunStep.create(b, b.fmt("xcframework {s}", .{opts.name}));
|
||||||
run.has_side_effects = true;
|
run.has_side_effects = true;
|
||||||
run.addArgs(&.{ "xcodebuild", "-create-xcframework" });
|
run.addArgs(&.{ "xcodebuild", "-create-xcframework" });
|
||||||
|
for (opts.libraries) |lib| {
|
||||||
run.addArg("-library");
|
run.addArg("-library");
|
||||||
run.addFileArg(opts.library);
|
run.addFileArg(lib.library);
|
||||||
run.addArg("-headers");
|
run.addArg("-headers");
|
||||||
run.addFileArg(opts.headers);
|
run.addFileArg(lib.headers);
|
||||||
|
}
|
||||||
run.addArg("-output");
|
run.addArg("-output");
|
||||||
run.addArg(opts.out_path);
|
run.addArg(opts.out_path);
|
||||||
break :run run;
|
break :run run;
|
||||||
|
@ -18,7 +18,7 @@ pub const SplitResizeDirection = Binding.Action.SplitResizeDirection;
|
|||||||
// in theory for XKB too on Linux but we don't need it right now.
|
// in theory for XKB too on Linux but we don't need it right now.
|
||||||
pub const Keymap = switch (builtin.os.tag) {
|
pub const Keymap = switch (builtin.os.tag) {
|
||||||
.macos => @import("input/KeymapDarwin.zig"),
|
.macos => @import("input/KeymapDarwin.zig"),
|
||||||
else => struct {},
|
else => @import("input/KeymapNoop.zig"),
|
||||||
};
|
};
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
38
src/input/KeymapNoop.zig
Normal file
38
src/input/KeymapNoop.zig
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//! A noop implementation of the keymap interface so that the embedded
|
||||||
|
//! library can compile on non-macOS platforms.
|
||||||
|
const KeymapNoop = @This();
|
||||||
|
|
||||||
|
const Mods = @import("key.zig").Mods;
|
||||||
|
|
||||||
|
pub const State = struct {};
|
||||||
|
pub const Translation = struct {
|
||||||
|
text: []const u8,
|
||||||
|
composing: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init() !KeymapNoop {
|
||||||
|
return .{};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *const KeymapNoop) void {
|
||||||
|
_ = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reload(self: *KeymapNoop) !void {
|
||||||
|
_ = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(
|
||||||
|
self: *const KeymapNoop,
|
||||||
|
out: []u8,
|
||||||
|
state: *State,
|
||||||
|
code: u16,
|
||||||
|
mods: Mods,
|
||||||
|
) !Translation {
|
||||||
|
_ = self;
|
||||||
|
_ = out;
|
||||||
|
_ = state;
|
||||||
|
_ = code;
|
||||||
|
_ = mods;
|
||||||
|
return .{ .text = "", .composing = false };
|
||||||
|
}
|
@ -9,7 +9,7 @@ const Key = @import("key.zig").Key;
|
|||||||
/// The full list of entries for the current platform.
|
/// The full list of entries for the current platform.
|
||||||
pub const entries: []const Entry = entries: {
|
pub const entries: []const Entry = entries: {
|
||||||
const native_idx = switch (builtin.os.tag) {
|
const native_idx = switch (builtin.os.tag) {
|
||||||
.macos => 4, // mac
|
.ios, .macos => 4, // mac
|
||||||
.windows => 3, // win
|
.windows => 3, // win
|
||||||
.linux => 2, // xkb
|
.linux => 2, // xkb
|
||||||
else => @compileError("unsupported platform"),
|
else => @compileError("unsupported platform"),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
const std = @import("std");
|
||||||
pub const cursor = @import("cursor.zig");
|
pub const cursor = @import("cursor.zig");
|
||||||
pub const key = @import("key.zig");
|
pub const key = @import("key.zig");
|
||||||
pub const termio = @import("termio.zig");
|
pub const termio = @import("termio.zig");
|
||||||
|
@ -130,7 +130,7 @@ pub const std_options = struct {
|
|||||||
//
|
//
|
||||||
// sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'
|
// sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'
|
||||||
//
|
//
|
||||||
if (builtin.os.tag == .macos) {
|
if (builtin.target.isDarwin()) {
|
||||||
// Convert our levels to Mac levels
|
// Convert our levels to Mac levels
|
||||||
const mac_level: macos.os.LogType = switch (level) {
|
const mac_level: macos.os.LogType = switch (level) {
|
||||||
.debug => .debug,
|
.debug => .debug,
|
||||||
|
@ -52,6 +52,9 @@ pub fn launchedFromDesktop() bool {
|
|||||||
// TODO: This should have some logic to detect this. Perhaps std.builtin.subsystem
|
// TODO: This should have some logic to detect this. Perhaps std.builtin.subsystem
|
||||||
.windows => false,
|
.windows => false,
|
||||||
|
|
||||||
|
// iPhone/iPad is always launched from the "desktop"
|
||||||
|
.ios => true,
|
||||||
|
|
||||||
else => @compileError("unsupported platform"),
|
else => @compileError("unsupported platform"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,10 @@ pub inline fn home(buf: []u8) !?[]u8 {
|
|||||||
return switch (builtin.os.tag) {
|
return switch (builtin.os.tag) {
|
||||||
inline .linux, .macos => try homeUnix(buf),
|
inline .linux, .macos => try homeUnix(buf),
|
||||||
.windows => try homeWindows(buf),
|
.windows => try homeWindows(buf),
|
||||||
|
|
||||||
|
// iOS doesn't have a user-writable home directory
|
||||||
|
.ios => null,
|
||||||
|
|
||||||
else => @compileError("unimplemented"),
|
else => @compileError("unimplemented"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,9 @@ const log = std.log.scoped(.os);
|
|||||||
|
|
||||||
/// The system-configured double-click interval if its available.
|
/// The system-configured double-click interval if its available.
|
||||||
pub fn clickInterval() ?u32 {
|
pub fn clickInterval() ?u32 {
|
||||||
|
return switch (builtin.os.tag) {
|
||||||
// On macOS, we can ask the system.
|
// On macOS, we can ask the system.
|
||||||
if (comptime builtin.target.isDarwin()) {
|
.macos => macos: {
|
||||||
const NSEvent = objc.getClass("NSEvent") orelse {
|
const NSEvent = objc.getClass("NSEvent") orelse {
|
||||||
log.err("NSEvent class not found. Can't get click interval.", .{});
|
log.err("NSEvent class not found. Can't get click interval.", .{});
|
||||||
return null;
|
return null;
|
||||||
@ -17,8 +18,9 @@ pub fn clickInterval() ?u32 {
|
|||||||
// Get the interval and convert to ms
|
// Get the interval and convert to ms
|
||||||
const interval = NSEvent.msgSend(f64, objc.sel("doubleClickInterval"), .{});
|
const interval = NSEvent.msgSend(f64, objc.sel("doubleClickInterval"), .{});
|
||||||
const ms = @as(u32, @intFromFloat(@ceil(interval * 1000)));
|
const ms = @as(u32, @intFromFloat(@ceil(interval * 1000)));
|
||||||
return ms;
|
break :macos ms;
|
||||||
}
|
},
|
||||||
|
|
||||||
return null;
|
else => null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ pub fn open(alloc: Allocator, url: []const u8) !void {
|
|||||||
.linux => &.{ "xdg-open", url },
|
.linux => &.{ "xdg-open", url },
|
||||||
.macos => &.{ "open", url },
|
.macos => &.{ "open", url },
|
||||||
.windows => &.{ "rundll32", "url.dll,FileProtocolHandler", url },
|
.windows => &.{ "rundll32", "url.dll,FileProtocolHandler", url },
|
||||||
|
.ios => return error.Unimplemented,
|
||||||
else => @compileError("unsupported OS"),
|
else => @compileError("unsupported OS"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
39
src/pty.zig
39
src/pty.zig
@ -14,10 +14,41 @@ pub const winsize = extern struct {
|
|||||||
ws_ypixel: u16 = 600,
|
ws_ypixel: u16 = 600,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Pty = if (builtin.os.tag == .windows)
|
pub const Pty = switch (builtin.os.tag) {
|
||||||
WindowsPty
|
.windows => WindowsPty,
|
||||||
else
|
.ios => NullPty,
|
||||||
PosixPty;
|
else => PosixPty,
|
||||||
|
};
|
||||||
|
|
||||||
|
// A pty implementation that does nothing.
|
||||||
|
//
|
||||||
|
// TODO: This should be removed. This is only temporary until we have
|
||||||
|
// a termio that doesn't use a pty. This isn't used in any user-facing
|
||||||
|
// artifacts, this is just a stopgap to get compilation to work on iOS.
|
||||||
|
const NullPty = struct {
|
||||||
|
pub const Fd = std.os.fd_t;
|
||||||
|
|
||||||
|
master: Fd,
|
||||||
|
slave: Fd,
|
||||||
|
|
||||||
|
pub fn open(size: winsize) !Pty {
|
||||||
|
_ = size;
|
||||||
|
return .{ .master = 0, .slave = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Pty) void {
|
||||||
|
_ = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setSize(self: *Pty, size: winsize) !void {
|
||||||
|
_ = self;
|
||||||
|
_ = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn childPreExec(self: Pty) !void {
|
||||||
|
_ = self;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Linux PTY creation and management. This is just a thin layer on top
|
/// Linux PTY creation and management. This is just a thin layer on top
|
||||||
/// of Linux syscalls. The caller is responsible for detail-oriented handling
|
/// of Linux syscalls. The caller is responsible for detail-oriented handling
|
||||||
|
Reference in New Issue
Block a user