Nuke GLFW, make zig build run on macOS build the Xcode project (#7815)

This PR does two things.

1. Build system improvements to make developing on macOS more enjoyable 
2. Delete the GLFW apprt

## Build System Improvements (macOS)

On macOS, there are a few major improvements:

* `zig build` now produces a full macOS app bundle and copies it into
`zig-out`
* `zig build run` now builds the macOS app and runs it, streaming logs
directly into the terminal
* `-Demit-macos-app` can control whether app bundle is created
* `-Dxcframework-target` can be set to one of `native` or `universal` to
control whether the xcframework uses only your target machines arch or
creates a universal one with macOS and iOS. This defaults to `native`
for the `run` command and `universal` for all others.
* The general flow of the `build.zig` file was improved to be more
declarative

## Nuke GLFW

> This deletes the GLFW apprt from the Ghostty codebase.
> 
> The GLFW apprt was the original apprt used by Ghostty (well, before
> Ghostty even had the concept of an "apprt" -- it was all just a single
> application then). It let me iterate on the core terminal features,
> rendering, etc. without bothering about the UI. It was a good way to
get
> started. But it has long since outlived its usefulness.
> 
> We've had a stable GTK apprt for Linux (and Windows via WSL) and a
> native macOS app via libghostty for awhile now. The GLFW apprt only
> remained within the tree for a few reasons:
> 
>   1. Primarily, it provided a faster feedback loop on macOS because
>      building the macOS app historically required us to hop out of the
>      zig build system and into Xcode, which is slow and cumbersome.
> 
>   2. It was a convenient way to narrow whether a bug was in the
> core Ghostty codebase or in the apprt itself. If a bug was in both
>      the glfw and macOS app then it was likely in the core.
> 
>   3. It provided us a way on macOS to test OpenGL.
> 
> All of these reasons are no longer valid. Respectively:
> 
> 1. Our Zig build scripts now execute the `xcodebuild` CLI directly and
>      can open the resulting app, stream logs, etc. This is the same
> experience we have on Linux. (Xcode has always been a dependency of
>      building on macOS in general, so this is not cumbersome.)
> 
>   2. We have a healthy group of maintainers, many of which have access
>      to both macOS and Linux, so we can quickly narrow down bugs
>      regardless of the apprt.
> 
> 3. Our OpenGL renderer hasn't been compatible with macOS for some time
>      now, so this is no longer a useful feature.
> 
> At this point, the GLFW apprt is just a burden. It adds complexity
> across the board, and some people try to run Ghostty with it in the
real
> world and get confused when it doesn't work (it's always been lacking
in
> features and buggy compared to the other apprts).
> 
> So, it's time to say goodbye. Its bittersweet because it is a big part
> of Ghostty's history, but we've grown up now and it's time to move on.
> Thank you, goodbye.
> 
> (NOTE: If you are a user of the GLFW apprt, then please fork the
project
> prior to this commit or start a new project based on it. We've warned
> against using it for a very, very long time now.)
This commit is contained in:
Mitchell Hashimoto
2025-07-05 13:49:24 -07:00
committed by GitHub
75 changed files with 407 additions and 22551 deletions

View File

@@ -3,7 +3,7 @@
//! getting user input (mouse/keyboard), etc.
//!
//! This enables compile-time interfaces to be built to swap out the underlying
//! application runtime. For example: glfw, pure macOS Cocoa, GTK+, browser, etc.
//! application runtime. For example: pure macOS Cocoa, GTK+, browser, etc.
//!
//! The goal is to have different implementations share as much of the core
//! logic as possible, and to only reach out to platform-specific implementation
@@ -15,7 +15,6 @@ const build_config = @import("build_config.zig");
const structs = @import("apprt/structs.zig");
pub const action = @import("apprt/action.zig");
pub const glfw = @import("apprt/glfw.zig");
pub const gtk = @import("apprt/gtk.zig");
pub const none = @import("apprt/none.zig");
pub const browser = @import("apprt/browser.zig");
@@ -42,7 +41,6 @@ pub const SurfaceSize = structs.SurfaceSize;
pub const runtime = switch (build_config.artifact) {
.exe => switch (build_config.app_runtime) {
.none => none,
.glfw => glfw,
.gtk => gtk,
},
.lib => embedded,
@@ -53,18 +51,12 @@ pub const App = runtime.App;
pub const Surface = runtime.Surface;
/// Runtime is the runtime to use for Ghostty. All runtimes do not provide
/// equivalent feature sets. For example, GTK offers tabbing and more features
/// that glfw does not provide. However, glfw may require many less
/// dependencies.
/// equivalent feature sets.
pub const Runtime = enum {
/// Will not produce an executable at all when `zig build` is called.
/// This is only useful if you're only interested in the lib only (macOS).
none,
/// Glfw-backed. Very simple. Glfw is statically linked. Tabbing and
/// other rich windowing features are not supported.
glfw,
/// GTK-backed. Rich windowed application. GTK is dynamically linked.
gtk,
@@ -72,12 +64,8 @@ pub const Runtime = enum {
// The Linux default is GTK because it is full featured.
if (target.os.tag == .linux) return .gtk;
// Windows we currently only support glfw
if (target.os.tag == .windows) return .glfw;
// Otherwise, we do NONE so we don't create an exe. The GLFW
// build is opt-in because it is missing so many features compared
// to the other builds that are impossible due to the GLFW interface.
// Otherwise, we do NONE so we don't create an exe and we
// create libghostty.
return .none;
}
};

View File

@@ -236,7 +236,7 @@ pub const App = struct {
var surface = try self.core_app.alloc.create(Surface);
errdefer self.core_app.alloc.destroy(surface);
// Create the surface -- because windows are surfaces for glfw.
// Create the surface
try surface.init(self, opts);
errdefer surface.deinit();

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ const apprt = @import("../apprt.zig");
const font = @import("../font/main.zig");
const rendererpkg = @import("../renderer.zig");
const Command = @import("../Command.zig");
const XCFramework = @import("GhosttyXCFramework.zig");
const WasmTarget = @import("../os/wasm/target.zig").Target;
const gtk = @import("gtk.zig");
@@ -24,6 +25,7 @@ const app_version: std.SemanticVersion = .{ .major = 1, .minor = 1, .patch = 4 }
/// Standard build configuration options.
optimize: std.builtin.OptimizeMode,
target: std.Build.ResolvedTarget,
xcframework_target: XCFramework.Target = .universal,
wasm_target: WasmTarget,
/// Comptime interfaces
@@ -48,14 +50,15 @@ patch_rpath: ?[]const u8 = null,
/// Artifacts
flatpak: bool = false,
emit_test_exe: bool = false,
emit_bench: bool = false,
emit_helpgen: bool = false,
emit_docs: bool = false,
emit_webdata: bool = false,
emit_xcframework: bool = false,
emit_helpgen: bool = false,
emit_macos_app: bool = false,
emit_terminfo: bool = false,
emit_termcap: bool = false,
emit_test_exe: bool = false,
emit_xcframework: bool = false,
emit_webdata: bool = false,
/// Environmental properties
env: std.process.EnvMap,
@@ -109,6 +112,14 @@ pub fn init(b: *std.Build) !Config {
.env = env,
};
//---------------------------------------------------------------
// Target-specific properties
config.xcframework_target = b.option(
XCFramework.Target,
"xcframework-target",
"The target for the xcframework.",
) orelse .universal;
//---------------------------------------------------------------
// Comptime Interfaces
config.font_backend = b.option(
@@ -340,6 +351,12 @@ pub fn init(b: *std.Build) !Config {
!config.emit_test_exe and
!config.emit_helpgen);
config.emit_macos_app = b.option(
bool,
"emit-macos-app",
"Build and install the macOS app bundle.",
) orelse config.emit_xcframework;
//---------------------------------------------------------------
// System Packages
@@ -378,11 +395,6 @@ pub fn init(b: *std.Build) !Config {
"glslang",
"spirv-cross",
"simdutf",
// This is default false because it is used for testing
// primarily and not official packaging. The packaging
// guide advises against building the GLFW backend.
"glfw3",
}) |dep| {
_ = b.systemIntegrationOption(dep, .{ .default = false });
}

View File

@@ -93,5 +93,32 @@ pub fn init(
pub fn install(self: *const GhosttyDocs) void {
const b = self.steps[0].owner;
for (self.steps) |step| b.getInstallStep().dependOn(step);
self.addStepDependencies(b.getInstallStep());
}
pub fn addStepDependencies(
self: *const GhosttyDocs,
other_step: *std.Build.Step,
) void {
for (self.steps) |step| other_step.dependOn(step);
}
/// Installs some dummy files to satisfy the folder structure of docs
/// without actually generating any documentation. This is useful
/// when the `emit-docs` option is not set to true, but we still
/// need the rough directory structure to exist, such as for the macOS
/// app.
pub fn installDummy(self: *const GhosttyDocs, step: *std.Build.Step) void {
_ = self;
const b = step.owner;
var wf = b.addWriteFiles();
const path = "share/man/.placeholder";
step.dependOn(&b.addInstallFile(
wf.add(
path,
"emit-docs not true so no man pages",
),
path,
).step);
}

View File

@@ -50,7 +50,14 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n {
}
pub fn install(self: *const GhosttyI18n) void {
for (self.steps) |step| self.owner.getInstallStep().dependOn(step);
self.addStepDependencies(self.owner.getInstallStep());
}
pub fn addStepDependencies(
self: *const GhosttyI18n,
other_step: *std.Build.Step,
) void {
for (self.steps) |step| other_step.dependOn(step);
}
fn createUpdateStep(b: *std.Build) !*std.Build.Step {

View File

@@ -397,5 +397,12 @@ fn addLinuxAppResources(
pub fn install(self: *const GhosttyResources) void {
const b = self.steps[0].owner;
for (self.steps) |step| b.getInstallStep().dependOn(step);
self.addStepDependencies(b.getInstallStep());
}
pub fn addStepDependencies(
self: *const GhosttyResources,
other_step: *std.Build.Step,
) void {
for (self.steps) |step| other_step.dependOn(step);
}

View File

@@ -7,11 +7,23 @@ const GhosttyLib = @import("GhosttyLib.zig");
const XCFrameworkStep = @import("XCFrameworkStep.zig");
xcframework: *XCFrameworkStep,
macos: GhosttyLib,
target: Target,
pub fn init(b: *std.Build, deps: *const SharedDeps) !GhosttyXCFramework {
// Create our universal macOS static library.
const macos = try GhosttyLib.initMacOSUniversal(b, deps);
pub const Target = enum { native, universal };
pub fn init(
b: *std.Build,
deps: *const SharedDeps,
target: Target,
) !GhosttyXCFramework {
// Universal macOS build
const macos_universal = try GhosttyLib.initMacOSUniversal(b, deps);
// Native macOS build
const macos_native = try GhosttyLib.initStatic(b, &try deps.retarget(
b,
Config.genericMacOSTarget(b, null),
));
// iOS
const ios = try GhosttyLib.initStatic(b, &try deps.retarget(
@@ -47,29 +59,43 @@ pub fn init(b: *std.Build, deps: *const SharedDeps) !GhosttyXCFramework {
const xcframework = XCFrameworkStep.create(b, .{
.name = "GhosttyKit",
.out_path = "macos/GhosttyKit.xcframework",
.libraries = &.{
.{
.library = macos.output,
.headers = b.path("include"),
.libraries = switch (target) {
.universal => &.{
.{
.library = macos_universal.output,
.headers = b.path("include"),
},
.{
.library = ios.output,
.headers = b.path("include"),
},
.{
.library = ios_sim.output,
.headers = b.path("include"),
},
},
.{
.library = ios.output,
.native => &.{.{
.library = macos_native.output,
.headers = b.path("include"),
},
.{
.library = ios_sim.output,
.headers = b.path("include"),
},
}},
},
});
return .{
.xcframework = xcframework,
.macos = macos,
.target = target,
};
}
pub fn install(self: *const GhosttyXCFramework) void {
const b = self.xcframework.step.owner;
b.getInstallStep().dependOn(self.xcframework.step);
self.addStepDependencies(b.getInstallStep());
}
pub fn addStepDependencies(
self: *const GhosttyXCFramework,
other_step: *std.Build.Step,
) void {
other_step.dependOn(self.xcframework.step);
}

View File

@@ -0,0 +1,149 @@
const Ghostty = @This();
const std = @import("std");
const builtin = @import("builtin");
const RunStep = std.Build.Step.Run;
const Config = @import("Config.zig");
const Docs = @import("GhosttyDocs.zig");
const I18n = @import("GhosttyI18n.zig");
const Resources = @import("GhosttyResources.zig");
const XCFramework = @import("GhosttyXCFramework.zig");
build: *std.Build.Step.Run,
open: *std.Build.Step.Run,
copy: *std.Build.Step.Run,
pub const Deps = struct {
xcframework: *const XCFramework,
docs: *const Docs,
i18n: *const I18n,
resources: *const Resources,
};
pub fn init(
b: *std.Build,
config: *const Config,
deps: Deps,
) !Ghostty {
const xc_config = switch (config.optimize) {
.Debug => "Debug",
.ReleaseSafe,
.ReleaseSmall,
.ReleaseFast,
=> "Release",
};
const app_path = b.fmt("macos/build/{s}/Ghostty.app", .{xc_config});
// Our step to build the Ghostty macOS app.
const build = build: {
// External environment variables can mess up xcodebuild, so
// we create a new empty environment.
const env_map = try b.allocator.create(std.process.EnvMap);
env_map.* = .init(b.allocator);
const build = RunStep.create(b, "xcodebuild");
build.has_side_effects = true;
build.cwd = b.path("macos");
build.env_map = env_map;
build.addArgs(&.{
"xcodebuild",
"-target",
"Ghostty",
"-configuration",
xc_config,
});
switch (deps.xcframework.target) {
// Universal is our default target, so we don't have to
// add anything.
.universal => {},
// Native we need to override the architecture in the Xcode
// project with the -arch flag.
.native => build.addArgs(&.{
"-arch",
switch (builtin.cpu.arch) {
.aarch64 => "arm64",
.x86_64 => "x86_64",
else => @panic("unsupported macOS arch"),
},
}),
}
// We need the xcframework
deps.xcframework.addStepDependencies(&build.step);
// We also need all these resources because the xcode project
// references them via symlinks.
deps.resources.addStepDependencies(&build.step);
deps.i18n.addStepDependencies(&build.step);
deps.docs.installDummy(&build.step);
// Expect success
build.expectExitCode(0);
break :build build;
};
// Our step to open the resulting Ghostty app.
const open = open: {
const open = RunStep.create(b, "run Ghostty app");
open.has_side_effects = true;
open.cwd = b.path("");
open.addArgs(&.{b.fmt(
"{s}/Contents/MacOS/ghostty",
.{app_path},
)});
// Open depends on the app
open.step.dependOn(&build.step);
// This overrides our default behavior and forces logs to show
// up on stderr (in addition to the centralized macOS log).
open.setEnvironmentVariable("GHOSTTY_LOG", "1");
// This is hack so that we can activate the app and bring it to
// the front forcibly even though we're executing directly
// via the binary and not launch services.
open.setEnvironmentVariable("GHOSTTY_MAC_ACTIVATE", "1");
if (b.args) |args| {
open.addArgs(args);
} else {
// This tricks the app into thinking it's running from the
// app bundle so we don't execute our CLI mode.
open.setEnvironmentVariable("GHOSTTY_MAC_APP", "1");
}
break :open open;
};
// Our step to copy the app bundle to the install path.
// We have to use `cp -R` because there are symlinks in the
// bundle.
const copy = copy: {
const step = RunStep.create(b, "copy app bundle");
step.addArgs(&.{ "cp", "-R" });
step.addFileArg(b.path(app_path));
step.addArg(b.fmt("{s}", .{b.install_path}));
step.step.dependOn(&build.step);
break :copy step;
};
return .{
.build = build,
.open = open,
.copy = copy,
};
}
pub fn install(self: *const Ghostty) void {
const b = self.copy.step.owner;
b.getInstallStep().dependOn(&self.copy.step);
}
pub fn installXcframework(self: *const Ghostty) void {
const b = self.build.step.owner;
b.getInstallStep().dependOn(&self.build.step);
}

View File

@@ -515,17 +515,6 @@ pub fn add(
switch (self.config.app_runtime) {
.none => {},
.glfw => if (b.lazyDependency("glfw", .{
.target = target,
.optimize = optimize,
})) |glfw_dep| {
step.root_module.addImport(
"glfw",
glfw_dep.module("glfw"),
);
},
.gtk => try self.addGTK(step),
}
}

View File

@@ -55,6 +55,9 @@ pub fn create(b: *std.Build, opts: Options) *XCFrameworkStep {
}
run.addArg("-output");
run.addArg(opts.out_path);
run.expectExitCode(0);
_ = run.captureStdOut();
_ = run.captureStdErr();
break :run run;
};
run_create.step.dependOn(&run_delete.step);

View File

@@ -15,6 +15,7 @@ pub const GhosttyFrameData = @import("GhosttyFrameData.zig");
pub const GhosttyLib = @import("GhosttyLib.zig");
pub const GhosttyResources = @import("GhosttyResources.zig");
pub const GhosttyI18n = @import("GhosttyI18n.zig");
pub const GhosttyXcodebuild = @import("GhosttyXcodebuild.zig");
pub const GhosttyXCFramework = @import("GhosttyXCFramework.zig");
pub const GhosttyWebdata = @import("GhosttyWebdata.zig");
pub const HelpStrings = @import("HelpStrings.zig");

View File

@@ -1582,9 +1582,9 @@ keybind: Keybinds = .{},
/// the visible screen area. This means that if the menu bar is visible, the
/// window will be placed below the menu bar.
///
/// Note: this is only supported on macOS and Linux GLFW builds. The GTK
/// runtime does not support setting the window position, as windows are
/// only allowed position themselves in X11 and not Wayland.
/// Note: this is only supported on macOS. The GTK runtime does not support
/// setting the window position, as windows are only allowed position
/// themselves in X11 and not Wayland.
@"window-position-x": ?i16 = null,
@"window-position-y": ?i16 = null,
@@ -2504,8 +2504,6 @@ keybind: Keybinds = .{},
///
/// The values `left` or `right` enable this for the left or right *Option*
/// key, respectively.
///
/// This does not work with GLFW builds.
@"macos-option-as-alt": ?OptionAsAlt = null,
/// Whether to enable the macOS window shadow. The default value is true.

View File

@@ -7,7 +7,6 @@ const Allocator = std.mem.Allocator;
const posix = std.posix;
const build_config = @import("build_config.zig");
const options = @import("build_options");
const glfw = @import("glfw");
const glslang = @import("glslang");
const macos = @import("macos");
const oni = @import("oniguruma");

View File

@@ -5,7 +5,6 @@ const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const builtin = @import("builtin");
const glfw = @import("glfw");
const objc = @import("objc");
const macos = @import("macos");
const graphics = macos.graphics;
@@ -38,11 +37,6 @@ pub const swap_chain_count = 3;
const log = std.log.scoped(.metal);
// Get native API access on certain platforms so we can do more customization.
const glfwNative = glfw.Native(.{
.cocoa = builtin.os.tag == .macos,
});
layer: IOSurfaceLayer,
/// MTLDevice
@@ -87,27 +81,6 @@ pub fn init(alloc: Allocator, opts: rendererpkg.Options) !Metal {
// Get the metadata about our underlying view that we'll be rendering to.
const info: ViewInfo = switch (apprt.runtime) {
apprt.glfw => info: {
// Everything in glfw is window-oriented so we grab the backing
// window, then derive everything from that.
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(
opts.rt_surface.window,
).?);
const contentView = objc.Object.fromId(
nswindow.getProperty(?*anyopaque, "contentView").?,
);
const scaleFactor = nswindow.getProperty(
graphics.c.CGFloat,
"backingScaleFactor",
);
break :info .{
.view = contentView,
.scaleFactor = scaleFactor,
};
},
apprt.embedded => .{
.scaleFactor = @floatCast(opts.rt_surface.content_scale.x),
.view = switch (opts.rt_surface.platform) {

View File

@@ -5,7 +5,6 @@ const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const builtin = @import("builtin");
const glfw = @import("glfw");
const gl = @import("opengl");
const shadertoy = @import("shadertoy.zig");
const apprt = @import("../apprt.zig");
@@ -60,18 +59,6 @@ pub fn deinit(self: *OpenGL) void {
self.* = undefined;
}
/// Returns the hints that we want for this
pub fn glfwWindowHints(config: *const configpkg.Config) glfw.Window.Hints {
_ = config;
return .{
.context_version_major = MIN_VERSION_MAJOR,
.context_version_minor = MIN_VERSION_MINOR,
.opengl_profile = .opengl_core_profile,
.opengl_forward_compat = true,
.transparent_framebuffer = true,
};
}
/// 32-bit windows cross-compilation breaks with `.c` for some reason, so...
const gl_debug_proc_callconv =
@typeInfo(
@@ -172,8 +159,7 @@ fn prepareContext(getProcAddress: anytype) !void {
/// This is called early right after surface creation.
pub fn surfaceInit(surface: *apprt.Surface) !void {
// Treat this like a thread entry
const self: OpenGL = undefined;
_ = surface;
switch (apprt.runtime) {
else => @compileError("unsupported app runtime for OpenGL"),
@@ -181,8 +167,6 @@ pub fn surfaceInit(surface: *apprt.Surface) !void {
// GTK uses global OpenGL context so we load from null.
apprt.gtk => try prepareContext(null),
apprt.glfw => try self.threadEnter(surface),
apprt.embedded => {
// TODO(mitchellh): this does nothing today to allow libghostty
// to compile for OpenGL targets but libghostty is strictly
@@ -205,17 +189,12 @@ pub fn surfaceInit(surface: *apprt.Surface) !void {
pub fn finalizeSurfaceInit(self: *const OpenGL, surface: *apprt.Surface) !void {
_ = self;
_ = surface;
// For GLFW, we grabbed the OpenGL context in surfaceInit and
// we need to release it before we start the renderer thread.
if (apprt.runtime == apprt.glfw) {
glfw.makeContextCurrent(null);
}
}
/// Callback called by renderer.Thread when it begins.
pub fn threadEnter(self: *const OpenGL, surface: *apprt.Surface) !void {
_ = self;
_ = surface;
switch (apprt.runtime) {
else => @compileError("unsupported app runtime for OpenGL"),
@@ -227,21 +206,6 @@ pub fn threadEnter(self: *const OpenGL, surface: *apprt.Surface) !void {
// on the main thread. As such, we don't do anything here.
},
apprt.glfw => {
// We need to make the OpenGL context current. OpenGL requires
// that a single thread own the a single OpenGL context (if any).
// This ensures that the context switches over to our thread.
// Important: the prior thread MUST have detached the context
// prior to calling this entrypoint.
glfw.makeContextCurrent(surface.window);
errdefer glfw.makeContextCurrent(null);
glfw.swapInterval(1);
// Load OpenGL bindings. This API is context-aware so this sets
// a threadlocal context for these pointers.
try prepareContext(&glfw.getProcAddress);
},
apprt.embedded => {
// TODO(mitchellh): this does nothing today to allow libghostty
// to compile for OpenGL targets but libghostty is strictly
@@ -262,11 +226,6 @@ pub fn threadExit(self: *const OpenGL) void {
// be sharing the global bindings with other windows.
},
apprt.glfw => {
gl.glad.unload();
glfw.makeContextCurrent(null);
},
apprt.embedded => {
// TODO: see threadEnter
},

View File

@@ -1,6 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
const glfw = @import("glfw");
const xev = @import("xev");
const wuffs = @import("wuffs");
const apprt = @import("../apprt.zig");
@@ -606,20 +605,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
}
};
/// Returns the hints that we want for this window.
pub fn glfwWindowHints(config: *const configpkg.Config) glfw.Window.Hints {
// If our graphics API provides hints, use them,
// otherwise fall back to generic hints.
if (@hasDecl(GraphicsAPI, "glfwWindowHints")) {
return GraphicsAPI.glfwWindowHints(config);
}
return .{
.client_api = .no_api,
.transparent_framebuffer = config.@"background-opacity" < 1,
};
}
pub fn init(alloc: Allocator, options: renderer.Options) !Self {
// Initialize our graphics API wrapper, this will prepare the
// surface provided by the apprt and set up any API-specific

View File

@@ -15,11 +15,6 @@ const posix = std.posix;
const log = std.log.scoped(.io_handler);
/// True if we should disable the kitty keyboard protocol. We have to
/// disable this on GLFW because GLFW input events don't support the
/// correct granularity of events.
const disable_kitty_keyboard_protocol = apprt.runtime == apprt.glfw;
/// This is used as the handler for the terminal.Stream type. This is
/// stateful and is expected to live for the entire lifetime of the terminal.
/// It is NOT VALID to stop a stream handler, create a new one, and use that
@@ -913,8 +908,6 @@ pub const StreamHandler = struct {
}
pub fn queryKittyKeyboard(self: *StreamHandler) !void {
if (comptime disable_kitty_keyboard_protocol) return;
log.debug("querying kitty keyboard mode", .{});
var data: termio.Message.WriteReq.Small.Array = undefined;
const resp = try std.fmt.bufPrint(&data, "\x1b[?{}u", .{
@@ -933,15 +926,11 @@ pub const StreamHandler = struct {
self: *StreamHandler,
flags: terminal.kitty.KeyFlags,
) !void {
if (comptime disable_kitty_keyboard_protocol) return;
log.debug("pushing kitty keyboard mode: {}", .{flags});
self.terminal.screen.kitty_keyboard.push(flags);
}
pub fn popKittyKeyboard(self: *StreamHandler, n: u16) !void {
if (comptime disable_kitty_keyboard_protocol) return;
log.debug("popping kitty keyboard mode n={}", .{n});
self.terminal.screen.kitty_keyboard.pop(@intCast(n));
}
@@ -951,8 +940,6 @@ pub const StreamHandler = struct {
mode: terminal.kitty.KeySetMode,
flags: terminal.kitty.KeyFlags,
) !void {
if (comptime disable_kitty_keyboard_protocol) return;
log.debug("setting kitty keyboard mode: {} {}", .{ mode, flags });
self.terminal.screen.kitty_keyboard.set(mode, flags);
}