mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
lots of progress running a surface but still crashes
This commit is contained in:
14
build.zig
14
build.zig
@ -147,6 +147,9 @@ pub fn build(b: *std.build.Builder) !void {
|
||||
lib.linkLibC();
|
||||
lib.addOptions("build_options", exe_options);
|
||||
|
||||
// See the comment in this file
|
||||
lib.addCSourceFile("src/renderer/metal_workaround.c", &.{});
|
||||
|
||||
// Create a single static lib with all our dependencies merged
|
||||
var lib_list = try addDeps(b, lib, true);
|
||||
try lib_list.append(.{ .generated = &lib.output_path_source });
|
||||
@ -172,6 +175,9 @@ pub fn build(b: *std.build.Builder) !void {
|
||||
lib.linkLibC();
|
||||
lib.addOptions("build_options", exe_options);
|
||||
|
||||
// See the comment in this file
|
||||
lib.addCSourceFile("src/renderer/metal_workaround.c", &.{});
|
||||
|
||||
// Create a single static lib with all our dependencies merged
|
||||
var lib_list = try addDeps(b, lib, true);
|
||||
try lib_list.append(.{ .generated = &lib.output_path_source });
|
||||
@ -425,10 +431,12 @@ fn addDeps(
|
||||
}
|
||||
|
||||
// stb_image_resize
|
||||
_ = try stb_image_resize.link(b, step, .{});
|
||||
const stb_image_resize_step = try stb_image_resize.link(b, step, .{});
|
||||
try static_libs.append(.{ .generated = &stb_image_resize_step.output_path_source });
|
||||
|
||||
// utf8proc
|
||||
_ = try utf8proc.link(b, step);
|
||||
const utf8proc_step = try utf8proc.link(b, step);
|
||||
try static_libs.append(.{ .generated = &utf8proc_step.output_path_source });
|
||||
|
||||
// Imgui, we have to do this later since we need some information
|
||||
const imgui_backends = if (step.target.isDarwin())
|
||||
@ -499,7 +507,7 @@ fn addDeps(
|
||||
|
||||
// Pixman
|
||||
const pixman_step = try pixman.link(b, step, .{});
|
||||
_ = pixman_step;
|
||||
try static_libs.append(.{ .generated = &pixman_step.output_path_source });
|
||||
|
||||
// Only Linux gets fontconfig
|
||||
if (enable_fontconfig) {
|
||||
|
@ -27,7 +27,10 @@ typedef struct {
|
||||
void *userdata;
|
||||
ghostty_runtime_wakeup_cb wakeup_cb;
|
||||
} ghostty_runtime_config_s;
|
||||
typedef struct {} ghostty_surface_config_s;
|
||||
typedef struct {
|
||||
void *nsview;
|
||||
double scale_factor;
|
||||
} ghostty_surface_config_s;
|
||||
|
||||
// Opaque types
|
||||
typedef void *ghostty_app_t;
|
||||
@ -44,12 +47,12 @@ void ghostty_config_free(ghostty_config_t);
|
||||
void ghostty_config_load_string(ghostty_config_t, const char *, uintptr_t);
|
||||
void ghostty_config_finalize(ghostty_config_t);
|
||||
|
||||
ghostty_app_t ghostty_app_new(ghostty_runtime_config_s *, ghostty_config_t);
|
||||
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s *, ghostty_config_t);
|
||||
void ghostty_app_free(ghostty_app_t);
|
||||
int ghostty_app_tick(ghostty_app_t);
|
||||
|
||||
ghostty_surface_t ghostty_surface_new(ghostty_app_t, ghostty_surface_config_s*);
|
||||
void ghostty_surface_free(ghostty_app_t, ghostty_surface_t);
|
||||
void ghostty_surface_free(ghostty_surface_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -9,8 +9,8 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
A507573E299FF33C009D7DC7 /* TerminalSurfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A507573D299FF33C009D7DC7 /* TerminalSurfaceView.swift */; };
|
||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; };
|
||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55685DF29A03A9F004303CE /* AppError.swift */; };
|
||||
A5B30535299BEAAA0047F10C /* GhosttyApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B30534299BEAAA0047F10C /* GhosttyApp.swift */; };
|
||||
A5B30537299BEAAA0047F10C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B30536299BEAAA0047F10C /* ContentView.swift */; };
|
||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||
A5D495A2299BEC7E00DD1313 /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */; };
|
||||
/* End PBXBuildFile section */
|
||||
@ -18,9 +18,9 @@
|
||||
/* Begin PBXFileReference section */
|
||||
A507573D299FF33C009D7DC7 /* TerminalSurfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSurfaceView.swift; sourceTree = "<group>"; };
|
||||
A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
|
||||
A55685DF29A03A9F004303CE /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
||||
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5B30534299BEAAA0047F10C /* GhosttyApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyApp.swift; sourceTree = "<group>"; };
|
||||
A5B30536299BEAAA0047F10C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
||||
A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = GhosttyKit.xcframework; sourceTree = "<group>"; };
|
||||
@ -43,9 +43,9 @@
|
||||
children = (
|
||||
A5D495A0299BEC2200DD1313 /* Preview Content */,
|
||||
A5B30534299BEAAA0047F10C /* GhosttyApp.swift */,
|
||||
A5B30536299BEAAA0047F10C /* ContentView.swift */,
|
||||
A535B9D9299C569B0017E2E4 /* ErrorView.swift */,
|
||||
A507573D299FF33C009D7DC7 /* TerminalSurfaceView.swift */,
|
||||
A55685DF29A03A9F004303CE /* AppError.swift */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
@ -153,8 +153,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||
A507573E299FF33C009D7DC7 /* TerminalSurfaceView.swift in Sources */,
|
||||
A5B30537299BEAAA0047F10C /* ContentView.swift in Sources */,
|
||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
||||
A5B30535299BEAAA0047F10C /* GhosttyApp.swift in Sources */,
|
||||
);
|
||||
|
3
macos/Sources/AppError.swift
Normal file
3
macos/Sources/AppError.swift
Normal file
@ -0,0 +1,3 @@
|
||||
enum AppError: Error {
|
||||
case surfaceCreateError
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
Image(systemName: "globe")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(.accentColor)
|
||||
Text("Hello, world!")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView()
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ struct GhosttyApp: App {
|
||||
case .error:
|
||||
ErrorView()
|
||||
case .ready:
|
||||
TerminalSurfaceView()
|
||||
TerminalSurfaceView(app: ghostty.app!)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,10 @@ class GhosttyState: ObservableObject {
|
||||
/// The ghostty global configuration.
|
||||
var config: ghostty_config_t? = nil
|
||||
|
||||
/// 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...
|
||||
var app: ghostty_app_t? = nil
|
||||
|
||||
init() {
|
||||
// Initialize ghostty global state. This happens once per process.
|
||||
guard ghostty_init() == GHOSTTY_SUCCESS else {
|
||||
@ -51,6 +55,7 @@ class GhosttyState: ObservableObject {
|
||||
readiness = .error
|
||||
return
|
||||
}
|
||||
self.config = 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
|
||||
@ -59,11 +64,29 @@ class GhosttyState: ObservableObject {
|
||||
// Finalize will make our defaults available.
|
||||
ghostty_config_finalize(cfg)
|
||||
|
||||
config = cfg
|
||||
readiness = .ready
|
||||
// Create our "runtime" config. The "runtime" is the configuration that ghostty
|
||||
// uses to interface with the application runtime environment.
|
||||
var runtime_cfg = ghostty_runtime_config_s(
|
||||
userdata: nil,
|
||||
wakeup_cb: { userdata in GhosttyState.wakeup() })
|
||||
|
||||
// Create the ghostty app.
|
||||
guard let app = ghostty_app_new(&runtime_cfg, cfg) else {
|
||||
GhosttyApp.logger.critical("ghostty_app_new failed")
|
||||
readiness = .error
|
||||
return
|
||||
}
|
||||
self.app = app
|
||||
|
||||
self.readiness = .ready
|
||||
}
|
||||
|
||||
static func wakeup() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
deinit {
|
||||
ghostty_app_free(app)
|
||||
ghostty_config_free(config)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import OSLog
|
||||
import SwiftUI
|
||||
import GhosttyKit
|
||||
|
||||
/// A surface is terminology in Ghostty for a terminal surface, or a place where a terminal is actually drawn
|
||||
/// and interacted with. The word "surface" is used because a surface may represent a window, a tab,
|
||||
@ -8,7 +10,11 @@ import SwiftUI
|
||||
/// since that is what the Metal renderer in Ghostty expects. In the future, it may make more sense to
|
||||
/// wrap an MTKView and use that, but for legacy reasons we didn't do that to begin with.
|
||||
struct TerminalSurfaceView: NSViewRepresentable {
|
||||
@StateObject private var state = TerminalSurfaceState()
|
||||
@StateObject private var state: TerminalSurfaceState
|
||||
|
||||
init(app: ghostty_app_t) {
|
||||
self._state = StateObject(wrappedValue: TerminalSurfaceState(app))
|
||||
}
|
||||
|
||||
func makeNSView(context: Context) -> TerminalSurfaceView_Real {
|
||||
// We need the view as part of the state to be created previously because
|
||||
@ -24,10 +30,33 @@ struct TerminalSurfaceView: NSViewRepresentable {
|
||||
|
||||
/// The state for the terminal surface view.
|
||||
class TerminalSurfaceState: ObservableObject {
|
||||
var view: TerminalSurfaceView_Real;
|
||||
static let logger = Logger(
|
||||
subsystem: Bundle.main.bundleIdentifier!,
|
||||
category: String(describing: TerminalSurfaceState.self)
|
||||
)
|
||||
|
||||
init() {
|
||||
var view: TerminalSurfaceView_Real
|
||||
private var surface: ghostty_surface_t? = nil
|
||||
private var error: Error? = nil
|
||||
|
||||
init(_ app: ghostty_app_t) {
|
||||
view = TerminalSurfaceView_Real()
|
||||
|
||||
var surface_cfg = ghostty_surface_config_s(
|
||||
nsview: Unmanaged.passUnretained(view).toOpaque(),
|
||||
scale_factor: 2.0)
|
||||
guard let surface = ghostty_surface_new(app, &surface_cfg) else {
|
||||
self.error = AppError.surfaceCreateError
|
||||
return
|
||||
}
|
||||
|
||||
self.surface = surface;
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let surface = self.surface {
|
||||
ghostty_surface_free(surface)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,8 +81,10 @@ class TerminalSurfaceView_Real: NSView {
|
||||
}
|
||||
}
|
||||
|
||||
struct TerminalSurfaceView_Previews: PreviewProvider {
|
||||
/*
|
||||
struct TerminalSurfaceView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
TerminalSurfaceView()
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -138,7 +138,7 @@ const srcs = &.{
|
||||
root ++ "pixman/pixman-region16.c",
|
||||
root ++ "pixman/pixman-region32.c",
|
||||
root ++ "pixman/pixman-solid-fill.c",
|
||||
root ++ "pixman/pixman-timer.c",
|
||||
//root ++ "pixman/pixman-timer.c",
|
||||
root ++ "pixman/pixman-trap.c",
|
||||
root ++ "pixman/pixman-utils.c",
|
||||
};
|
||||
|
@ -387,8 +387,9 @@ pub const CAPI = struct {
|
||||
app: *App,
|
||||
opts: *const apprt.runtime.Window.Options,
|
||||
) !*Window {
|
||||
_ = opts;
|
||||
const w = try app.newWindow(.{});
|
||||
const w = try app.newWindow(.{
|
||||
.runtime = opts.*,
|
||||
});
|
||||
return w;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const objc = @import("objc");
|
||||
const apprt = @import("../apprt.zig");
|
||||
const CoreApp = @import("../App.zig");
|
||||
const CoreWindow = @import("../Window.zig");
|
||||
@ -45,15 +46,25 @@ pub const App = struct {
|
||||
};
|
||||
|
||||
pub const Window = struct {
|
||||
nsview: objc.Object,
|
||||
scale_factor: f64,
|
||||
|
||||
pub const Options = extern struct {
|
||||
id: usize = 0,
|
||||
/// The pointer to the backing NSView for the surface.
|
||||
nsview: *anyopaque = undefined,
|
||||
|
||||
/// The scale factor of the screen.
|
||||
scale_factor: f64 = 1,
|
||||
};
|
||||
|
||||
pub fn init(app: *const CoreApp, core_win: *CoreWindow, opts: Options) !Window {
|
||||
_ = app;
|
||||
_ = core_win;
|
||||
_ = opts;
|
||||
return .{};
|
||||
|
||||
return .{
|
||||
.nsview = objc.Object.fromId(opts.nsview),
|
||||
.scale_factor = opts.scale_factor,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Window) void {
|
||||
@ -67,7 +78,10 @@ pub const Window = struct {
|
||||
|
||||
pub fn getSize(self: *const Window) !apprt.WindowSize {
|
||||
_ = self;
|
||||
return apprt.WindowSize{ .width = 1, .height = 1 };
|
||||
|
||||
// Initially our window will have a zero size. Until we can determine
|
||||
// the size of the window, we just send down this value.
|
||||
return apprt.WindowSize{ .width = 800, .height = 600 };
|
||||
}
|
||||
|
||||
pub fn setSizeLimits(self: *Window, min: apprt.WindowSize, max_: ?apprt.WindowSize) !void {
|
||||
|
@ -305,22 +305,42 @@ pub fn deinit(self: *Metal) void {
|
||||
/// This is called just prior to spinning up the renderer thread for
|
||||
/// final main thread setup requirements.
|
||||
pub fn finalizeWindowInit(self: *const Metal, win: apprt.runtime.Window) !void {
|
||||
// Set our window backing layer to be our swapchain
|
||||
const nswindow = switch (apprt.runtime) {
|
||||
apprt.glfw => objc.Object.fromId(glfwNative.getCocoaWindow(win.window).?),
|
||||
apprt.embedded => @panic("TODO"),
|
||||
const Info = struct {
|
||||
view: objc.Object,
|
||||
scaleFactor: f64,
|
||||
};
|
||||
|
||||
// Get the view and scale factor for our surface.
|
||||
const info: Info = 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(win.window).?);
|
||||
const contentView = objc.Object.fromId(nswindow.getProperty(?*anyopaque, "contentView").?);
|
||||
const scaleFactor = nswindow.getProperty(macos.graphics.c.CGFloat, "backingScaleFactor");
|
||||
break :info .{
|
||||
.view = contentView,
|
||||
.scaleFactor = scaleFactor,
|
||||
};
|
||||
},
|
||||
|
||||
apprt.embedded => .{
|
||||
.view = win.nsview,
|
||||
.scaleFactor = win.scale_factor,
|
||||
},
|
||||
|
||||
else => @compileError("unsupported apprt for metal"),
|
||||
};
|
||||
const contentView = objc.Object.fromId(nswindow.getProperty(?*anyopaque, "contentView").?);
|
||||
contentView.setProperty("layer", self.swapchain.value);
|
||||
contentView.setProperty("wantsLayer", true);
|
||||
|
||||
// Make our view layer-backed with our Metal layer
|
||||
info.view.setProperty("layer", self.swapchain.value);
|
||||
info.view.setProperty("wantsLayer", true);
|
||||
|
||||
// Ensure that our metal layer has a content scale set to match the
|
||||
// scale factor of the window. This avoids magnification issues leading
|
||||
// to blurry rendering.
|
||||
const layer = contentView.getProperty(objc.Object, "layer");
|
||||
const scaleFactor = nswindow.getProperty(macos.graphics.c.CGFloat, "backingScaleFactor");
|
||||
layer.setProperty("contentsScale", scaleFactor);
|
||||
const layer = info.view.getProperty(objc.Object, "layer");
|
||||
layer.setProperty("contentsScale", info.scaleFactor);
|
||||
}
|
||||
|
||||
/// This is called if this renderer runs DevMode.
|
||||
|
@ -106,10 +106,10 @@ pub const Padding = struct {
|
||||
const padding_bot = space_bot - padding_top;
|
||||
|
||||
return .{
|
||||
.top = padding_top,
|
||||
.bottom = padding_bot,
|
||||
.right = padding_right,
|
||||
.left = padding_left,
|
||||
.top = @max(0, padding_top),
|
||||
.bottom = @max(0, padding_bot),
|
||||
.right = @max(0, padding_right),
|
||||
.left = @max(0, padding_left),
|
||||
};
|
||||
}
|
||||
|
||||
@ -124,6 +124,17 @@ pub const Padding = struct {
|
||||
}
|
||||
};
|
||||
|
||||
test "Padding balanced on zero" {
|
||||
// On some systems, our screen can be zero-sized for a bit, and we
|
||||
// don't want to end up with negative padding.
|
||||
const testing = std.testing;
|
||||
const grid: GridSize = .{ .columns = 100, .rows = 37 };
|
||||
const cell: CellSize = .{ .width = 10, .height = 20 };
|
||||
const screen: ScreenSize = .{ .width = 0, .height = 0 };
|
||||
const padding = Padding.balanced(screen, grid, cell);
|
||||
try testing.expectEqual(padding, .{});
|
||||
}
|
||||
|
||||
test "GridSize update exact" {
|
||||
const testing = std.testing;
|
||||
|
||||
|
Reference in New Issue
Block a user