mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: MetalView, render an MTKView
This commit is contained in:
@ -26,6 +26,8 @@
|
||||
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A56D58882ACDE6CA00508D2C /* ServiceProvider.swift */; };
|
||||
A571AB1D2A206FCF00248498 /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */; };
|
||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59444F629A2ED5200725BBA /* SettingsView.swift */; };
|
||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||
A5CDF1912AAF9A5800513312 /* ConfigurationErrors.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5CDF1902AAF9A5800513312 /* ConfigurationErrors.xib */; };
|
||||
@ -59,6 +61,8 @@
|
||||
A56D58882ACDE6CA00508D2C /* ServiceProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceProvider.swift; sourceTree = "<group>"; };
|
||||
A571AB1C2A206FC600248498 /* Ghostty-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Ghostty-Info.plist"; sourceTree = "<group>"; };
|
||||
A59444F629A2ED5200725BBA /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
||||
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
||||
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@ -115,6 +119,7 @@
|
||||
children = (
|
||||
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
||||
8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */,
|
||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */,
|
||||
A5FECBD829D2010400022361 /* WindowAccessor.swift */,
|
||||
A5CEAFDA29B8005900646FDA /* SplitView */,
|
||||
);
|
||||
@ -151,6 +156,7 @@
|
||||
A55B7BB729B6F53A0055DE60 /* Package.swift */,
|
||||
A55B7BB529B6F47F0055DE60 /* AppState.swift */,
|
||||
A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */,
|
||||
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */,
|
||||
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */,
|
||||
A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */,
|
||||
A55B7BBD29B701360055DE60 /* Ghostty.SplitView.swift */,
|
||||
@ -288,6 +294,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
||||
A53426392A7DC55C00EBB7A2 /* PrimaryWindowManager.swift in Sources */,
|
||||
85DE1C922A6A3DCA00493853 /* PrimaryWindow.swift in Sources */,
|
||||
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
||||
@ -304,6 +311,7 @@
|
||||
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */,
|
||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||
A5FECBD929D2010400022361 /* WindowAccessor.swift in Sources */,
|
||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
||||
|
@ -30,7 +30,7 @@ extension Ghostty {
|
||||
// surface. We need to keep the split root around so that we don't
|
||||
// lose all of the surface state so this must be a ZStack.
|
||||
if let surfaceView = zoomedSurface {
|
||||
SurfaceWrapper(surfaceView: surfaceView)
|
||||
InspectableSurface(surfaceView: surfaceView)
|
||||
}
|
||||
}
|
||||
.focusedValue(\.ghosttySurfaceZoomed, zoomedSurface != nil)
|
||||
@ -343,7 +343,7 @@ extension Ghostty {
|
||||
let pubClose = center.publisher(for: Notification.ghosttyCloseSurface, object: leaf.surface)
|
||||
let pubFocus = center.publisher(for: Notification.ghosttyFocusSplit, object: leaf.surface)
|
||||
|
||||
SurfaceWrapper(surfaceView: leaf.surface, isSplit: !neighbors.isEmpty())
|
||||
InspectableSurface(surfaceView: leaf.surface, isSplit: !neighbors.isEmpty())
|
||||
.onReceive(pub) { onNewSplit(notification: $0) }
|
||||
.onReceive(pubClose) { onClose(notification: $0) }
|
||||
.onReceive(pubFocus) { onMoveFocus(notification: $0) }
|
||||
|
70
macos/Sources/Ghostty/InspectorView.swift
Normal file
70
macos/Sources/Ghostty/InspectorView.swift
Normal file
@ -0,0 +1,70 @@
|
||||
import Foundation
|
||||
import MetalKit
|
||||
import SwiftUI
|
||||
|
||||
extension Ghostty {
|
||||
/// InspectableSurface is a type of Surface view that allows an inspector to be attached.
|
||||
struct InspectableSurface: View {
|
||||
/// Same as SurfaceWrapper, see the doc comments there.
|
||||
@ObservedObject var surfaceView: SurfaceView
|
||||
var isSplit: Bool = false
|
||||
|
||||
var body: some View {
|
||||
SplitView(.vertical, left: {
|
||||
SurfaceWrapper(surfaceView: surfaceView, isSplit: isSplit)
|
||||
}, right: {
|
||||
InspectorView()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct InspectorView: View {
|
||||
var body: some View {
|
||||
MetalView<Renderer>()
|
||||
}
|
||||
}
|
||||
|
||||
class Renderer: NSObject, MetalViewRenderer {
|
||||
let device: MTLDevice
|
||||
let commandQueue: MTLCommandQueue
|
||||
|
||||
required init(metalView: MTKView) {
|
||||
// Initialize our Metal primitives
|
||||
guard
|
||||
let device = MTLCreateSystemDefaultDevice(),
|
||||
let commandQueue = device.makeCommandQueue() else {
|
||||
fatalError("GPU not available")
|
||||
}
|
||||
|
||||
self.device = device
|
||||
self.commandQueue = commandQueue
|
||||
super.init()
|
||||
|
||||
// Setup the view to point to this renderer
|
||||
metalView.device = device
|
||||
metalView.delegate = self
|
||||
metalView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Ghostty.Renderer: MTKViewDelegate {
|
||||
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
|
||||
}
|
||||
|
||||
func draw(in view: MTKView) {
|
||||
guard
|
||||
let commandBuffer = self.commandQueue.makeCommandBuffer(),
|
||||
let descriptor = view.currentRenderPassDescriptor,
|
||||
let renderEncoder =
|
||||
commandBuffer.makeRenderCommandEncoder(
|
||||
descriptor: descriptor) else {
|
||||
return
|
||||
}
|
||||
|
||||
renderEncoder.endEncoding()
|
||||
guard let drawable = view.currentDrawable else { return }
|
||||
commandBuffer.present(drawable)
|
||||
commandBuffer.commit()
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ extension Ghostty {
|
||||
content(surfaceView)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SurfaceWrapper: View {
|
||||
// The surface to create a view for. This must be created upstream. As long as this
|
||||
// remains the same, the surface that is being rendered remains the same.
|
||||
|
33
macos/Sources/Helpers/MetalView.swift
Normal file
33
macos/Sources/Helpers/MetalView.swift
Normal file
@ -0,0 +1,33 @@
|
||||
import SwiftUI
|
||||
import MetalKit
|
||||
|
||||
/// Implements the logic for a metal view used by MetalView.
|
||||
protocol MetalViewRenderer: MTKViewDelegate {
|
||||
init(metalView: MTKView)
|
||||
}
|
||||
|
||||
/// Renders an MTKView with the given renderer class.
|
||||
struct MetalView<R: MetalViewRenderer>: View {
|
||||
@State private var metalView = MTKView()
|
||||
@State private var renderer: R?
|
||||
|
||||
var body: some View {
|
||||
MetalViewRepresentable(metalView: $metalView)
|
||||
.onAppear { renderer = R(metalView: metalView) }
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct MetalViewRepresentable: NSViewRepresentable {
|
||||
@Binding var metalView: MTKView
|
||||
|
||||
func makeNSView(context: Context) -> some NSView {
|
||||
metalView
|
||||
}
|
||||
|
||||
func updateNSView(_ view: NSViewType, context: Context) {
|
||||
updateMetalView()
|
||||
}
|
||||
|
||||
func updateMetalView() {
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user