diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/128.png b/Assets.xcassets/AppIcon-Debug.appiconset/128.png new file mode 100644 index 00000000..f470aeff Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/128.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/128@2x.png b/Assets.xcassets/AppIcon-Debug.appiconset/128@2x.png new file mode 100644 index 00000000..abcbc24f Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/128@2x.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/16.png b/Assets.xcassets/AppIcon-Debug.appiconset/16.png new file mode 100644 index 00000000..d4941f6d Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/16.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/16@2x.png b/Assets.xcassets/AppIcon-Debug.appiconset/16@2x.png new file mode 100644 index 00000000..e298fe39 Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/16@2x.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/256.png b/Assets.xcassets/AppIcon-Debug.appiconset/256.png new file mode 100644 index 00000000..67457fa1 Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/256.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/256@2x.png b/Assets.xcassets/AppIcon-Debug.appiconset/256@2x.png new file mode 100644 index 00000000..be740aae Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/256@2x.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/32.png b/Assets.xcassets/AppIcon-Debug.appiconset/32.png new file mode 100644 index 00000000..e298fe39 Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/32.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/32@2x.png b/Assets.xcassets/AppIcon-Debug.appiconset/32@2x.png new file mode 100644 index 00000000..9be0ae7a Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/32@2x.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/512.png b/Assets.xcassets/AppIcon-Debug.appiconset/512.png new file mode 100644 index 00000000..be740aae Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/512.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/512@2x.png b/Assets.xcassets/AppIcon-Debug.appiconset/512@2x.png new file mode 100644 index 00000000..8804e1d4 Binary files /dev/null and b/Assets.xcassets/AppIcon-Debug.appiconset/512@2x.png differ diff --git a/Assets.xcassets/AppIcon-Debug.appiconset/Contents.json b/Assets.xcassets/AppIcon-Debug.appiconset/Contents.json new file mode 100644 index 00000000..93a6772e --- /dev/null +++ b/Assets.xcassets/AppIcon-Debug.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "filename" : "16.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "16@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "32.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "32@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "128.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "128@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "256.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "256@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "512.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "512@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Assets.xcassets/AppIcon.appiconset/128.png b/Assets.xcassets/AppIcon.appiconset/128.png new file mode 100644 index 00000000..b458571a Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/128.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/128@2x.png b/Assets.xcassets/AppIcon.appiconset/128@2x.png new file mode 100644 index 00000000..158d4b64 Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/128@2x.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/16.png b/Assets.xcassets/AppIcon.appiconset/16.png new file mode 100644 index 00000000..43570df5 Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/16.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/16@2x.png b/Assets.xcassets/AppIcon.appiconset/16@2x.png new file mode 100644 index 00000000..1e3fd85b Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/16@2x.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/256.png b/Assets.xcassets/AppIcon.appiconset/256.png new file mode 100644 index 00000000..37255441 Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/256.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/256@2x.png b/Assets.xcassets/AppIcon.appiconset/256@2x.png new file mode 100644 index 00000000..52e0e222 Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/256@2x.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/32.png b/Assets.xcassets/AppIcon.appiconset/32.png new file mode 100644 index 00000000..1e3fd85b Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/32.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/32@2x.png b/Assets.xcassets/AppIcon.appiconset/32@2x.png new file mode 100644 index 00000000..c97a8c72 Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/32@2x.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/512.png b/Assets.xcassets/AppIcon.appiconset/512.png new file mode 100644 index 00000000..52e0e222 Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/512.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/512@2x.png b/Assets.xcassets/AppIcon.appiconset/512@2x.png new file mode 100644 index 00000000..5a099e36 Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/512@2x.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/Contents.json b/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..93a6772e --- /dev/null +++ b/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "filename" : "16.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "16@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "32.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "32@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "128.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "128@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "256.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "256@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "512.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "512@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Assets.xcassets/Contents.json b/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/GhosttyTabs.xcodeproj/project.pbxproj b/GhosttyTabs.xcodeproj/project.pbxproj index 08bb0b63..b2a6698d 100644 --- a/GhosttyTabs.xcodeproj/project.pbxproj +++ b/GhosttyTabs.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ A50010A4 /* SplitTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50010A0 /* SplitTree.swift */; }; A50010A5 /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50010A1 /* SplitView.swift */; }; A50010A6 /* TerminalSplitTreeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50010A2 /* TerminalSplitTreeView.swift */; }; + A5001100 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5001101 /* Assets.xcassets */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -52,6 +53,7 @@ A50010A0 /* SplitTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Splits/SplitTree.swift; sourceTree = ""; }; A50010A1 /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Splits/SplitView.swift; sourceTree = ""; }; A50010A2 /* TerminalSplitTreeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Splits/TerminalSplitTreeView.swift; sourceTree = ""; }; + A5001101 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,11 +67,23 @@ }; /* End PBXFrameworksBuildPhase section */ +/* Begin PBXResourcesBuildPhase section */ + A5001102 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A5001100 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + /* Begin PBXGroup section */ A5001040 = { isa = PBXGroup; children = ( A5001041 /* Sources */, + A5001101 /* Assets.xcassets */, A5001016 /* GhosttyKit.xcframework */, A5001017 /* ghostty.h */, A5001018 /* GhosttyTabs-Bridging-Header.h */, @@ -113,6 +127,7 @@ buildPhases = ( A5001051 /* Sources */, A5001030 /* Frameworks */, + A5001102 /* Resources */, A5001020 /* Embed Frameworks */, ); buildRules = ( @@ -239,7 +254,7 @@ A5001082 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Debug"; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index ce45f109..181d90b9 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -107,7 +107,7 @@ class GhosttyApp { runtimeConfig.supports_selection_clipboard = true runtimeConfig.wakeup_cb = { userdata in DispatchQueue.main.async { - // Wakeup - trigger redraw if needed + GhosttyApp.shared.tick() } } runtimeConfig.action_cb = { app, target, action in @@ -211,6 +211,7 @@ class GhosttyApp { func tick() { guard let app = app else { return } ghostty_app_tick(app) + AppDelegate.shared?.tabManager?.tickRender() } private func updateDefaultBackground(from config: ghostty_config_t?) { @@ -447,6 +448,7 @@ class GhosttyApp { blue: CGFloat(change.b) / 255, alpha: 1.0 ) + surfaceView.applySurfaceBackground() if backgroundLogEnabled { logBackground("OSC background change tab=\(surfaceView.tabId?.uuidString ?? "unknown") color=\(surfaceView.backgroundColor?.description ?? "nil")") } @@ -559,11 +561,13 @@ class TerminalSurface: Identifiable { return } + ghostty_surface_set_content_scale(surface, scale, scale) ghostty_surface_set_size( surface, UInt32(view.bounds.width * scale), UInt32(view.bounds.height * scale) ) + ghostty_surface_refresh(surface) setupDisplayLink() } @@ -603,7 +607,9 @@ class TerminalSurface: Identifiable { func updateSize(width: CGFloat, height: CGFloat, scale: CGFloat) { guard let surface = surface else { return } + ghostty_surface_set_content_scale(surface, scale, scale) ghostty_surface_set_size(surface, UInt32(width * scale), UInt32(height * scale)) + ghostty_surface_refresh(surface) if let view = attachedView, let metalLayer = view.layer as? CAMetalLayer { metalLayer.contentsScale = scale @@ -611,6 +617,12 @@ class TerminalSurface: Identifiable { } } + func renderIfVisible() { + guard let view = attachedView else { return } + guard view.window != nil, view.bounds.width > 0, view.bounds.height > 0 else { return } + ghostty_surface_draw(surface) + } + func applyWindowBackgroundIfActive() { surfaceView.applyWindowBackgroundIfActive() } @@ -678,11 +690,20 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { return base.withAlphaComponent(opacity) } + func applySurfaceBackground() { + let color = effectiveBackgroundColor() + if let metalLayer = layer as? CAMetalLayer { + metalLayer.backgroundColor = color.cgColor + metalLayer.isOpaque = color.alphaComponent >= 1.0 + } + } + func applyWindowBackgroundIfActive() { guard let window else { return } if let tabId, let selectedId = AppDelegate.shared?.tabManager?.selectedTabId, tabId != selectedId { return } + applySurfaceBackground() let color = effectiveBackgroundColor() window.backgroundColor = color window.isOpaque = color.alphaComponent >= 1.0 @@ -745,10 +766,16 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { if window != nil { attachSurfaceIfNeeded() updateSurfaceSize() + applySurfaceBackground() applyWindowBackgroundIfActive() } } + override func viewDidChangeBackingProperties() { + super.viewDidChangeBackingProperties() + updateSurfaceSize() + } + override func setFrameSize(_ newSize: NSSize) { super.setFrameSize(newSize) attachSurfaceIfNeeded() diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index cff25dbe..e1fd8ce7 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -204,6 +204,14 @@ class TabManager: ObservableObject { }) } + func tickRender() { + for tab in tabs { + for surface in tab.splitTree.map({ $0 }) { + surface.renderIfVisible() + } + } + } + func addTab() { let newTab = Tab(title: "Terminal \(tabs.count + 1)") tabs.append(newTab)