mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-30 13:27:51 +03:00
metal: devmode
This commit is contained in:
@ -230,9 +230,12 @@ fn addDeps(
|
||||
try glfw.link(b, step, glfw_opts);
|
||||
|
||||
// Imgui, we have to do this later since we need some information
|
||||
const imgui_backends = [_][]const u8{ "glfw", "opengl3" };
|
||||
const imgui_backends = if (step.target.isDarwin())
|
||||
&[_][]const u8{ "glfw", "opengl3", "metal" }
|
||||
else
|
||||
&[_][]const u8{ "glfw", "opengl3" };
|
||||
var imgui_opts: imgui.Options = .{
|
||||
.backends = &imgui_backends,
|
||||
.backends = imgui_backends,
|
||||
.freetype = .{ .enabled = true },
|
||||
};
|
||||
|
||||
|
@ -101,11 +101,17 @@ pub fn buildImgui(
|
||||
lib.addCSourceFiles(srcs, flags.items);
|
||||
if (opt.backends) |backends| {
|
||||
for (backends) |backend| {
|
||||
const ext = if (std.mem.eql(u8, "metal", backend)) ext: {
|
||||
// Metal requires some extra frameworks
|
||||
step.linkFramework("QuartzCore");
|
||||
break :ext "mm";
|
||||
} else "cpp";
|
||||
|
||||
var buf: [4096]u8 = undefined;
|
||||
const path = try std.fmt.bufPrint(
|
||||
&buf,
|
||||
"{s}imgui/backends/imgui_impl_{s}.cpp",
|
||||
.{ root, backend },
|
||||
"{s}imgui/backends/imgui_impl_{s}.{s}",
|
||||
.{ root, backend, ext },
|
||||
);
|
||||
|
||||
lib.addCSourceFile(path, flags.items);
|
||||
|
@ -13,6 +13,10 @@ pub const ImplGlfw = struct {
|
||||
return ImGui_ImplGlfw_InitForOpenGL(win, install_callbacks);
|
||||
}
|
||||
|
||||
pub fn initForOther(win: *GLFWWindow, install_callbacks: bool) bool {
|
||||
return ImGui_ImplGlfw_InitForOther(win, install_callbacks);
|
||||
}
|
||||
|
||||
pub fn shutdown() void {
|
||||
return ImGui_ImplGlfw_Shutdown();
|
||||
}
|
||||
@ -23,6 +27,7 @@ pub const ImplGlfw = struct {
|
||||
|
||||
extern "c" fn glfwGetError(?*const anyopaque) c_int;
|
||||
extern "c" fn ImGui_ImplGlfw_InitForOpenGL(*GLFWWindow, bool) bool;
|
||||
extern "c" fn ImGui_ImplGlfw_InitForOther(*GLFWWindow, bool) bool;
|
||||
extern "c" fn ImGui_ImplGlfw_Shutdown() void;
|
||||
extern "c" fn ImGui_ImplGlfw_NewFrame() void;
|
||||
};
|
||||
|
31
pkg/imgui/impl_metal.zig
Normal file
31
pkg/imgui/impl_metal.zig
Normal file
@ -0,0 +1,31 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig");
|
||||
const imgui = @import("main.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const ImplMetal = struct {
|
||||
pub fn init(device: ?*anyopaque) bool {
|
||||
return ImGui_ImplMetal_Init(device);
|
||||
}
|
||||
|
||||
pub fn shutdown() void {
|
||||
return ImGui_ImplMetal_Shutdown();
|
||||
}
|
||||
|
||||
pub fn newFrame(render_pass_desc: ?*anyopaque) void {
|
||||
return ImGui_ImplMetal_NewFrame(render_pass_desc);
|
||||
}
|
||||
|
||||
pub fn renderDrawData(
|
||||
data: *imgui.DrawData,
|
||||
command_buffer: ?*anyopaque,
|
||||
command_encoder: ?*anyopaque,
|
||||
) void {
|
||||
ImGui_ImplMetal_RenderDrawData(data, command_buffer, command_encoder);
|
||||
}
|
||||
|
||||
extern "c" fn ImGui_ImplMetal_Init(?*anyopaque) bool;
|
||||
extern "c" fn ImGui_ImplMetal_Shutdown() void;
|
||||
extern "c" fn ImGui_ImplMetal_NewFrame(?*anyopaque) void;
|
||||
extern "c" fn ImGui_ImplMetal_RenderDrawData(*imgui.DrawData, ?*anyopaque, ?*anyopaque) void;
|
||||
};
|
@ -7,6 +7,7 @@ pub usingnamespace @import("io.zig");
|
||||
pub usingnamespace @import("style.zig");
|
||||
|
||||
pub usingnamespace @import("impl_glfw.zig");
|
||||
pub usingnamespace @import("impl_metal.zig");
|
||||
pub usingnamespace @import("impl_opengl3.zig");
|
||||
|
||||
test {
|
||||
|
@ -9,6 +9,7 @@ const assert = std.debug.assert;
|
||||
|
||||
const Atlas = @import("Atlas.zig");
|
||||
const Window = @import("Window.zig");
|
||||
const renderer = @import("renderer.zig");
|
||||
|
||||
/// If this is false, the rest of the terminal will be compiled without
|
||||
/// dev mode support at all.
|
||||
@ -27,9 +28,10 @@ window: ?*Window = null,
|
||||
/// Update the state associated with the dev mode. This should generally
|
||||
/// only be called paired with a render since it otherwise wastes CPU
|
||||
/// cycles.
|
||||
///
|
||||
/// Note: renderers should call their implementation "newFrame" functions
|
||||
/// prior to this.
|
||||
pub fn update(self: *const DevMode) !void {
|
||||
imgui.ImplOpenGL3.newFrame();
|
||||
imgui.ImplGlfw.newFrame();
|
||||
imgui.newFrame();
|
||||
|
||||
if (imgui.begin("dev mode", null, .{})) {
|
||||
@ -42,16 +44,27 @@ pub fn update(self: *const DevMode) !void {
|
||||
helpMarker("The number of glyphs loaded and rendered into a " ++
|
||||
"font atlas currently.");
|
||||
|
||||
const Renderer = @TypeOf(window.renderer);
|
||||
if (imgui.treeNode("Atlas: Greyscale", .{ .default_open = true })) {
|
||||
defer imgui.treePop();
|
||||
const atlas = &window.font_group.atlas_greyscale;
|
||||
try self.atlasInfo(atlas, @intCast(usize, window.renderer.texture.id));
|
||||
const tex = switch (Renderer) {
|
||||
renderer.OpenGL => @intCast(usize, window.renderer.texture.id),
|
||||
renderer.Metal => @ptrToInt(window.renderer.texture_greyscale.value),
|
||||
else => @compileError("renderer unsupported, add it!"),
|
||||
};
|
||||
try self.atlasInfo(atlas, tex);
|
||||
}
|
||||
|
||||
if (imgui.treeNode("Atlas: Color (Emoji)", .{ .default_open = true })) {
|
||||
defer imgui.treePop();
|
||||
const atlas = &window.font_group.atlas_color;
|
||||
try self.atlasInfo(atlas, @intCast(usize, window.renderer.texture_color.id));
|
||||
const tex = switch (Renderer) {
|
||||
renderer.OpenGL => @intCast(usize, window.renderer.texture_color.id),
|
||||
renderer.Metal => @ptrToInt(window.renderer.texture_color.value),
|
||||
else => @compileError("renderer unsupported, add it!"),
|
||||
};
|
||||
try self.atlasInfo(atlas, tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,13 @@ const builtin = @import("builtin");
|
||||
const glfw = @import("glfw");
|
||||
const objc = @import("objc");
|
||||
const macos = @import("macos");
|
||||
const imgui = @import("imgui");
|
||||
const Atlas = @import("../Atlas.zig");
|
||||
const font = @import("../font/main.zig");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
const renderer = @import("../renderer.zig");
|
||||
const math = @import("../math.zig");
|
||||
const DevMode = @import("../DevMode.zig");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Terminal = terminal.Terminal;
|
||||
@ -240,6 +242,11 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Metal) void {
|
||||
if (DevMode.enabled) {
|
||||
imgui.ImplMetal.shutdown();
|
||||
imgui.ImplGlfw.shutdown();
|
||||
}
|
||||
|
||||
self.cells.deinit(self.alloc);
|
||||
|
||||
self.font_shaper.deinit();
|
||||
@ -256,6 +263,15 @@ pub fn finalizeInit(self: *const Metal, window: glfw.Window) !void {
|
||||
const contentView = objc.Object.fromId(nswindow.getProperty(?*anyopaque, "contentView").?);
|
||||
contentView.setProperty("layer", self.swapchain.value);
|
||||
contentView.setProperty("wantsLayer", true);
|
||||
|
||||
if (DevMode.enabled) {
|
||||
// Initialize for our window
|
||||
assert(imgui.ImplGlfw.initForOther(
|
||||
@ptrCast(*imgui.ImplGlfw.GLFWWindow, window.handle),
|
||||
true,
|
||||
));
|
||||
assert(imgui.ImplMetal.init(self.device.value));
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback called by renderer.Thread when it begins.
|
||||
@ -285,6 +301,7 @@ pub fn render(
|
||||
const Critical = struct {
|
||||
bg: terminal.color.RGB,
|
||||
screen_size: ?renderer.ScreenSize,
|
||||
devmode: bool,
|
||||
};
|
||||
|
||||
// Update all our data as tightly as possible within the mutex.
|
||||
@ -323,6 +340,7 @@ pub fn render(
|
||||
break :critical .{
|
||||
.bg = self.background,
|
||||
.screen_size = state.resize_screen,
|
||||
.devmode = if (state.devmode) |dm| dm.visible else false,
|
||||
};
|
||||
};
|
||||
|
||||
@ -470,6 +488,27 @@ pub fn render(
|
||||
@as(c_ulong, self.cells.items.len),
|
||||
},
|
||||
);
|
||||
|
||||
// Build our devmode draw data. This sucks because it requires we
|
||||
// lock our state mutex but the metal imgui implementation requires
|
||||
// access to all this stuff.
|
||||
if (critical.devmode) {
|
||||
state.mutex.lock();
|
||||
defer state.mutex.unlock();
|
||||
|
||||
if (state.devmode) |dm| {
|
||||
if (dm.visible) {
|
||||
imgui.ImplMetal.newFrame(desc.value);
|
||||
imgui.ImplGlfw.newFrame();
|
||||
try dm.update();
|
||||
imgui.ImplMetal.renderDrawData(
|
||||
try dm.render(),
|
||||
buffer.value,
|
||||
encoder.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.msgSend(void, objc.sel("presentDrawable:"), .{surface.value});
|
||||
|
@ -483,6 +483,8 @@ pub fn render(
|
||||
const devmode_data = devmode_data: {
|
||||
if (state.devmode) |dm| {
|
||||
if (dm.visible) {
|
||||
imgui.ImplOpenGL3.newFrame();
|
||||
imgui.ImplGlfw.newFrame();
|
||||
try dm.update();
|
||||
break :devmode_data try dm.render();
|
||||
}
|
||||
|
19
src/renderer/cursor.zig
Normal file
19
src/renderer/cursor.zig
Normal file
@ -0,0 +1,19 @@
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
|
||||
/// Available cursor styles for drawing that renderers must support.
|
||||
pub const CursorStyle = enum {
|
||||
box,
|
||||
box_hollow,
|
||||
bar,
|
||||
|
||||
/// Create a cursor style from the terminal style request.
|
||||
pub fn fromTerminal(style: terminal.CursorStyle) ?CursorStyle {
|
||||
return switch (style) {
|
||||
.blinking_block, .steady_block => .box,
|
||||
.blinking_bar, .steady_bar => .bar,
|
||||
.blinking_underline, .steady_underline => null, // TODO
|
||||
.default => .box,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user