mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
metal: devmode
This commit is contained in:
@ -230,9 +230,12 @@ fn addDeps(
|
|||||||
try glfw.link(b, step, glfw_opts);
|
try glfw.link(b, step, glfw_opts);
|
||||||
|
|
||||||
// Imgui, we have to do this later since we need some information
|
// 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 = .{
|
var imgui_opts: imgui.Options = .{
|
||||||
.backends = &imgui_backends,
|
.backends = imgui_backends,
|
||||||
.freetype = .{ .enabled = true },
|
.freetype = .{ .enabled = true },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,11 +101,17 @@ pub fn buildImgui(
|
|||||||
lib.addCSourceFiles(srcs, flags.items);
|
lib.addCSourceFiles(srcs, flags.items);
|
||||||
if (opt.backends) |backends| {
|
if (opt.backends) |backends| {
|
||||||
for (backends) |backend| {
|
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;
|
var buf: [4096]u8 = undefined;
|
||||||
const path = try std.fmt.bufPrint(
|
const path = try std.fmt.bufPrint(
|
||||||
&buf,
|
&buf,
|
||||||
"{s}imgui/backends/imgui_impl_{s}.cpp",
|
"{s}imgui/backends/imgui_impl_{s}.{s}",
|
||||||
.{ root, backend },
|
.{ root, backend, ext },
|
||||||
);
|
);
|
||||||
|
|
||||||
lib.addCSourceFile(path, flags.items);
|
lib.addCSourceFile(path, flags.items);
|
||||||
|
@ -13,6 +13,10 @@ pub const ImplGlfw = struct {
|
|||||||
return ImGui_ImplGlfw_InitForOpenGL(win, install_callbacks);
|
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 {
|
pub fn shutdown() void {
|
||||||
return ImGui_ImplGlfw_Shutdown();
|
return ImGui_ImplGlfw_Shutdown();
|
||||||
}
|
}
|
||||||
@ -23,6 +27,7 @@ pub const ImplGlfw = struct {
|
|||||||
|
|
||||||
extern "c" fn glfwGetError(?*const anyopaque) c_int;
|
extern "c" fn glfwGetError(?*const anyopaque) c_int;
|
||||||
extern "c" fn ImGui_ImplGlfw_InitForOpenGL(*GLFWWindow, bool) bool;
|
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_Shutdown() void;
|
||||||
extern "c" fn ImGui_ImplGlfw_NewFrame() 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("style.zig");
|
||||||
|
|
||||||
pub usingnamespace @import("impl_glfw.zig");
|
pub usingnamespace @import("impl_glfw.zig");
|
||||||
|
pub usingnamespace @import("impl_metal.zig");
|
||||||
pub usingnamespace @import("impl_opengl3.zig");
|
pub usingnamespace @import("impl_opengl3.zig");
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
@ -9,6 +9,7 @@ const assert = std.debug.assert;
|
|||||||
|
|
||||||
const Atlas = @import("Atlas.zig");
|
const Atlas = @import("Atlas.zig");
|
||||||
const Window = @import("Window.zig");
|
const Window = @import("Window.zig");
|
||||||
|
const renderer = @import("renderer.zig");
|
||||||
|
|
||||||
/// If this is false, the rest of the terminal will be compiled without
|
/// If this is false, the rest of the terminal will be compiled without
|
||||||
/// dev mode support at all.
|
/// dev mode support at all.
|
||||||
@ -27,9 +28,10 @@ window: ?*Window = null,
|
|||||||
/// Update the state associated with the dev mode. This should generally
|
/// Update the state associated with the dev mode. This should generally
|
||||||
/// only be called paired with a render since it otherwise wastes CPU
|
/// only be called paired with a render since it otherwise wastes CPU
|
||||||
/// cycles.
|
/// cycles.
|
||||||
|
///
|
||||||
|
/// Note: renderers should call their implementation "newFrame" functions
|
||||||
|
/// prior to this.
|
||||||
pub fn update(self: *const DevMode) !void {
|
pub fn update(self: *const DevMode) !void {
|
||||||
imgui.ImplOpenGL3.newFrame();
|
|
||||||
imgui.ImplGlfw.newFrame();
|
|
||||||
imgui.newFrame();
|
imgui.newFrame();
|
||||||
|
|
||||||
if (imgui.begin("dev mode", null, .{})) {
|
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 " ++
|
helpMarker("The number of glyphs loaded and rendered into a " ++
|
||||||
"font atlas currently.");
|
"font atlas currently.");
|
||||||
|
|
||||||
|
const Renderer = @TypeOf(window.renderer);
|
||||||
if (imgui.treeNode("Atlas: Greyscale", .{ .default_open = true })) {
|
if (imgui.treeNode("Atlas: Greyscale", .{ .default_open = true })) {
|
||||||
defer imgui.treePop();
|
defer imgui.treePop();
|
||||||
const atlas = &window.font_group.atlas_greyscale;
|
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 })) {
|
if (imgui.treeNode("Atlas: Color (Emoji)", .{ .default_open = true })) {
|
||||||
defer imgui.treePop();
|
defer imgui.treePop();
|
||||||
const atlas = &window.font_group.atlas_color;
|
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 glfw = @import("glfw");
|
||||||
const objc = @import("objc");
|
const objc = @import("objc");
|
||||||
const macos = @import("macos");
|
const macos = @import("macos");
|
||||||
|
const imgui = @import("imgui");
|
||||||
const Atlas = @import("../Atlas.zig");
|
const Atlas = @import("../Atlas.zig");
|
||||||
const font = @import("../font/main.zig");
|
const font = @import("../font/main.zig");
|
||||||
const terminal = @import("../terminal/main.zig");
|
const terminal = @import("../terminal/main.zig");
|
||||||
const renderer = @import("../renderer.zig");
|
const renderer = @import("../renderer.zig");
|
||||||
const math = @import("../math.zig");
|
const math = @import("../math.zig");
|
||||||
|
const DevMode = @import("../DevMode.zig");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Terminal = terminal.Terminal;
|
const Terminal = terminal.Terminal;
|
||||||
@ -240,6 +242,11 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Metal) void {
|
pub fn deinit(self: *Metal) void {
|
||||||
|
if (DevMode.enabled) {
|
||||||
|
imgui.ImplMetal.shutdown();
|
||||||
|
imgui.ImplGlfw.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
self.cells.deinit(self.alloc);
|
self.cells.deinit(self.alloc);
|
||||||
|
|
||||||
self.font_shaper.deinit();
|
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").?);
|
const contentView = objc.Object.fromId(nswindow.getProperty(?*anyopaque, "contentView").?);
|
||||||
contentView.setProperty("layer", self.swapchain.value);
|
contentView.setProperty("layer", self.swapchain.value);
|
||||||
contentView.setProperty("wantsLayer", true);
|
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.
|
/// Callback called by renderer.Thread when it begins.
|
||||||
@ -285,6 +301,7 @@ pub fn render(
|
|||||||
const Critical = struct {
|
const Critical = struct {
|
||||||
bg: terminal.color.RGB,
|
bg: terminal.color.RGB,
|
||||||
screen_size: ?renderer.ScreenSize,
|
screen_size: ?renderer.ScreenSize,
|
||||||
|
devmode: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update all our data as tightly as possible within the mutex.
|
// Update all our data as tightly as possible within the mutex.
|
||||||
@ -323,6 +340,7 @@ pub fn render(
|
|||||||
break :critical .{
|
break :critical .{
|
||||||
.bg = self.background,
|
.bg = self.background,
|
||||||
.screen_size = state.resize_screen,
|
.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),
|
@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});
|
buffer.msgSend(void, objc.sel("presentDrawable:"), .{surface.value});
|
||||||
|
@ -483,6 +483,8 @@ pub fn render(
|
|||||||
const devmode_data = devmode_data: {
|
const devmode_data = devmode_data: {
|
||||||
if (state.devmode) |dm| {
|
if (state.devmode) |dm| {
|
||||||
if (dm.visible) {
|
if (dm.visible) {
|
||||||
|
imgui.ImplOpenGL3.newFrame();
|
||||||
|
imgui.ImplGlfw.newFrame();
|
||||||
try dm.update();
|
try dm.update();
|
||||||
break :devmode_data try dm.render();
|
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